diff options
Diffstat (limited to 'trunk/src/emu/cpu/h83002/h8periph.c')
-rw-r--r-- | trunk/src/emu/cpu/h83002/h8periph.c | 794 |
1 files changed, 794 insertions, 0 deletions
diff --git a/trunk/src/emu/cpu/h83002/h8periph.c b/trunk/src/emu/cpu/h83002/h8periph.c new file mode 100644 index 00000000000..ec12f2c7a26 --- /dev/null +++ b/trunk/src/emu/cpu/h83002/h8periph.c @@ -0,0 +1,794 @@ +/*************************************************************************** + + h8periph.c: Implementation of Hitachi H8/3002 on-board MCU functions. + + Original by The_Author & DynaChicken for the ZiNc emulator. + + Rewritten for MAME to use MAME timers and other facilities by R. Belmont + +****************************************************************************/ + +#include "emu.h" +#include "debugger.h" +#include "h8.h" +#include "h8priv.h" + +#define H8_REG_START (0x00ffff10) + +// timer registers +#define TSTR (0x60) +#define TIER0 (0x66) +#define TIER1 (0x70) +#define TIER2 (0x7a) +#define TIER3 (0x84) +#define TIER4 (0x94) +#define TSR0 (0x67) +#define TSR1 (0x71) +#define TSR2 (0x7b) +#define TSR3 (0x85) +#define TSR4 (0x95) +#define TCR0 (0x64) +#define TCR1 (0x6e) +#define TCR2 (0x78) +#define TCR3 (0x82) +#define TCR4 (0x92) + +static const UINT8 tsr[5] = { TSR0, TSR1, TSR2, TSR3, TSR4 }; +static const UINT8 tier[5] = { TIER0, TIER1, TIER2, TIER3, TIER4 }; +static const UINT8 tcr[5] = { TCR0, TCR1, TCR2, TCR3, TCR4 }; +static const int tscales[4] = { 1, 2, 4, 8 }; + +extern void h8_3002_InterruptRequest(h83xx_state *h8, UINT8 source, UINT8 state); + +static void h8itu_timer_expire(h83xx_state *h8, int which) +{ + h8->timer[which]->adjust(attotime::never); + h8->h8TCNT[which] = 0; + h8->per_regs[tsr[which]] |= 4; + // interrupt on overflow ? + if(h8->per_regs[tier[which]] & 4) + { + h8_3002_InterruptRequest(h8, 26 + 4*which, 1); + } +} + +static TIMER_CALLBACK( h8itu_timer_0_cb ) +{ + h83xx_state *h8 = (h83xx_state *)ptr; + + h8itu_timer_expire(h8, 0); +} + +static TIMER_CALLBACK( h8itu_timer_1_cb ) +{ + h83xx_state *h8 = (h83xx_state *)ptr; + + h8itu_timer_expire(h8, 1); +} + +static TIMER_CALLBACK( h8itu_timer_2_cb ) +{ + h83xx_state *h8 = (h83xx_state *)ptr; + + h8itu_timer_expire(h8, 2); +} + +static TIMER_CALLBACK( h8itu_timer_3_cb ) +{ + h83xx_state *h8 = (h83xx_state *)ptr; + + h8itu_timer_expire(h8, 3); +} + +static TIMER_CALLBACK( h8itu_timer_4_cb ) +{ + h83xx_state *h8 = (h83xx_state *)ptr; + + h8itu_timer_expire(h8, 4); +} + +static void h8_itu_refresh_timer(h83xx_state *h8, int tnum) +{ + int ourTCR = 0; + int ourTVAL = 0; + attotime period; + + ourTCR = h8->per_regs[tcr[tnum]]; + ourTVAL = h8->h8TCNT[tnum]; + + period = attotime::from_hz(h8->device->unscaled_clock()) * tscales[ourTCR & 3] * (65536 - ourTVAL); + + if (ourTCR & 4) + { + logerror("H8/3002: Timer %d is using an external clock. Unsupported!\n", tnum); + } + + h8->timer[tnum]->adjust(period); +} + +static void h8_itu_sync_timers(h83xx_state *h8, int tnum) +{ + int ourTCR = 0; + attotime cycle_time, cur; + UINT16 ratio; + + ourTCR = h8->per_regs[tcr[tnum]]; + + // get the time per unit + cycle_time = attotime::from_hz(h8->device->unscaled_clock()) * tscales[ourTCR & 3]; + cur = h8->timer[tnum]->elapsed(); + + ratio = cur.as_double() / cycle_time.as_double(); + + h8->h8TCNT[tnum] = ratio; +} + +UINT8 h8_itu_read8(h83xx_state *h8, UINT8 reg) +{ + UINT8 val; + + switch(reg) + { + case 0x60: + val = h8->h8TSTR; + break; + case 0x68: + h8_itu_sync_timers(h8, 0); + val = h8->h8TCNT[0]>>8; + break; + case 0x69: + h8_itu_sync_timers(h8, 0); + val = h8->h8TCNT[0]&0xff; + break; + case 0x72: + h8_itu_sync_timers(h8, 1); + val = h8->h8TCNT[1]>>8; + break; + case 0x73: + h8_itu_sync_timers(h8, 1); + val = h8->h8TCNT[1]&0xff; + break; + case 0x7c: + h8_itu_sync_timers(h8, 2); + val = h8->h8TCNT[2]>>8; + break; + case 0x7d: + h8_itu_sync_timers(h8, 2); + val = h8->h8TCNT[2]&0xff; + break; + case 0x86: + h8_itu_sync_timers(h8, 3); + val = h8->h8TCNT[3]>>8; + break; + case 0x87: + h8_itu_sync_timers(h8, 3); + val = h8->h8TCNT[3]&0xff; + break; + default: + val = h8->per_regs[reg]; + break; + } + + + return val; +} + +void h8_itu_write8(h83xx_state *h8, UINT8 reg, UINT8 val) +{ + h8->per_regs[reg] = val; + switch(reg) + { + case 0x60: + if ((val & 1) && !(h8->h8TSTR & 1)) + { + h8_itu_refresh_timer(h8, 0); + } + if ((val & 2) && !(h8->h8TSTR & 2)) + { + h8_itu_refresh_timer(h8, 1); + } + if ((val & 4) && !(h8->h8TSTR & 4)) + { + h8_itu_refresh_timer(h8, 2); + } + if ((val & 8) && !(h8->h8TSTR & 8)) + { + h8_itu_refresh_timer(h8, 3); + } + if ((val & 0x10) && !(h8->h8TSTR & 0x10)) + { + h8_itu_refresh_timer(h8, 4); + } + h8->h8TSTR = val; + break; + case 0x68: + h8->h8TCNT[0] = (val<<8) | (h8->h8TCNT[0] & 0xff); + if (h8->h8TSTR & 1) + { + h8_itu_refresh_timer(h8, 0); + } + break; + case 0x69: + h8->h8TCNT[0] = (val) | (h8->h8TCNT[0] & 0xff00); + if (h8->h8TSTR & 1) + { + h8_itu_refresh_timer(h8, 0); + } + break; + case 0x72: + h8->h8TCNT[1] = (val<<8) | (h8->h8TCNT[1] & 0xff); + if (h8->h8TSTR & 2) + { + h8_itu_refresh_timer(h8, 1); + } + break; + case 0x73: + h8->h8TCNT[1] = (val) | (h8->h8TCNT[1] & 0xff00); + if (h8->h8TSTR & 2) + { + h8_itu_refresh_timer(h8, 1); + } + break; + case 0x7c: + h8->h8TCNT[2] = (val<<8) | (h8->h8TCNT[2] & 0xff); + if (h8->h8TSTR & 4) + { + h8_itu_refresh_timer(h8, 2); + } + break; + case 0x7d: + h8->h8TCNT[2] = (val) | (h8->h8TCNT[2] & 0xff00); + if (h8->h8TSTR & 4) + { + h8_itu_refresh_timer(h8, 2); + } + break; + case 0x86: + h8->h8TCNT[3] = (val<<8) | (h8->h8TCNT[3] & 0xff); + if (h8->h8TSTR & 8) + { + h8_itu_refresh_timer(h8, 3); + } + break; + case 0x87: + h8->h8TCNT[3] = (val) | (h8->h8TCNT[3] & 0xff00); + if (h8->h8TSTR & 8) + { + h8_itu_refresh_timer(h8, 3); + } + break; + case 0x96: + h8->h8TCNT[4] = (val<<8) | (h8->h8TCNT[4] & 0xff); + if (h8->h8TSTR & 0x10) + { + h8_itu_refresh_timer(h8, 4); + } + break; + case 0x97: + h8->h8TCNT[4] = (val) | (h8->h8TCNT[4] & 0xff00); + if (h8->h8TSTR & 0x10) + { + h8_itu_refresh_timer(h8, 4); + } + break; + default: + val = 0; + break; + } +} + +#ifdef UNUSED_FUNCTION +UINT8 h8_debugger_itu_read8(UINT8 reg) +{ + UINT8 val; + val = 0; + return val; +} +#endif + + +static UINT8 h8_ISR_r(h83xx_state *h8) +{ + UINT8 res = 0; + + int i; + for (i = 0; i < 6; i++) + if (h8->irq_req[0] & (1 << (12+i))) res |= (1 << i); + + return res; +} + +static void h8_ISR_w(h83xx_state *h8, UINT8 val) +{ + int i; + for (i = 0; i < 6; i++) + if ((~val) & (1 << i)) h8->irq_req[0] &= ~(1 << (12+i)); +} + + +UINT8 h8_register_read8(h83xx_state *h8, UINT32 address) +{ + UINT8 val; + UINT8 reg; + + address &= 0xffffff; + + reg = address & 0xff; + + if(reg >= 0x60 && reg <= 0x9f) + { + return h8_itu_read8(h8, reg); + } + else + { + switch(reg) + { + case 0xb4: // serial port A status + val = h8->per_regs[reg]; + val |= 0xc4; // transmit finished, receive ready, no errors + break; + case 0xb5: // serial port A receive + val = h8->io->read_byte(H8_SERIAL_0); + break; + case 0xbc: // serial port B status + val = h8->per_regs[reg]; + val |= 0xc4; // transmit finished, receive ready, no errors + break; + case 0xbd: // serial port B receive + val = h8->io->read_byte(H8_SERIAL_1); + break; + case 0xe0: + val = h8->io->read_byte(H8_ADC_0_H); + break; + case 0xe1: + val = h8->io->read_byte(H8_ADC_0_L); + break; + case 0xe2: + val = h8->io->read_byte(H8_ADC_1_H); + break; + case 0xe3: + val = h8->io->read_byte(H8_ADC_1_L); + break; + case 0xe4: + val = h8->io->read_byte(H8_ADC_2_H); + break; + case 0xe5: + val = h8->io->read_byte(H8_ADC_2_L); + break; + case 0xe6: + val = h8->io->read_byte(H8_ADC_3_H); + break; + case 0xe7: + val = h8->io->read_byte(H8_ADC_3_L); + break; + case 0xe8: // adc status + val = 0x80; + break; + case 0xc7: // port 4 data + val = h8->io->read_byte(H8_PORT_4); + break; + case 0xcb: // port 6 data + val = h8->io->read_byte(H8_PORT_6); + break; + case 0xce: // port 7 data + val = h8->io->read_byte(H8_PORT_7); + break; + case 0xcf: // port 8 data + val = h8->io->read_byte(H8_PORT_8); + break; + case 0xd2: // port 9 data + val = h8->io->read_byte(H8_PORT_9); + break; + case 0xd3: // port a data + val = h8->io->read_byte(H8_PORT_A); + break; + case 0xd6: // port b data + val = h8->io->read_byte(H8_PORT_B); + break; + case 0xf6: + val = h8_ISR_r(h8); + break; + + default: + val = h8->per_regs[reg]; + break; + } + } + + return val; +} + +void h8_register_write8(h83xx_state *h8, UINT32 address, UINT8 val) +{ + UINT8 reg; + + address &= 0xffffff; + + reg = address & 0xff; + + if(reg >= 0x60 && reg <= 0x9f) + { + h8_itu_write8(h8, reg, val); + } + + switch (reg) + { + case 0xb3: // serial 0 send + h8->io->write_byte(H8_SERIAL_0, val); + h8_3002_InterruptRequest(h8, 54, 1); + h8_3002_InterruptRequest(h8, 55, 1); + break; + case 0xbb: // serial 1 send + h8->io->write_byte(H8_SERIAL_1, val); + h8_3002_InterruptRequest(h8, 58, 1); + h8_3002_InterruptRequest(h8, 59, 1); + break; + case 0xc7: + h8->io->write_byte(H8_PORT_4, val); + break; + case 0xcb: // port 6 data + h8->io->write_byte(H8_PORT_6, val); + break; + case 0xce: // port 7 data + h8->io->write_byte(H8_PORT_7, val); + break; + case 0xcf: // port 8 data + h8->io->write_byte(H8_PORT_8, val); + break; + case 0xd2: // port 9 data + h8->io->write_byte(H8_PORT_9, val); + break; + case 0xd3: // port a data + h8->io->write_byte(H8_PORT_A, val); + break; + case 0xd6: // port b data + h8->io->write_byte(H8_PORT_B, val); + break; + case 0xf6: + h8_ISR_w(h8, val); + break; + } + + h8->per_regs[reg] = val; +} + +static void h8_3007_itu_refresh_timer(h83xx_state *h8, int tnum) +{ + attotime period; + int ourTCR = h8->per_regs[0x68+(tnum*8)]; + + period = attotime::from_hz(h8->device->unscaled_clock()) * tscales[ourTCR & 3]; + + if (ourTCR & 4) + { + logerror("H8/3007: Timer %d is using an external clock. Unsupported!\n", tnum); + } + + h8->timer[tnum]->adjust(period); +} + +static void h8itu_3007_timer_expire(h83xx_state *h8, int tnum) +{ + int base = 0x68 + (tnum*8); + UINT16 count; + + count = (h8->per_regs[base + 0x2]<<8) | h8->per_regs[base + 0x3]; + count++; + + //logerror("h8/3007 timer %d count = %04x\n",tnum,count); + + // GRA match + if ((h8->per_regs[base + 0x1] & 0x03) && (count == ((h8->per_regs[base + 0x4]<<8) | h8->per_regs[base + 0x5]))) + { + if ((h8->per_regs[base + 0x0] & 0x60) == 0x20) + { + //logerror("h8/3007 timer %d GRA match, restarting\n",tnum); + count = 0; + h8_3007_itu_refresh_timer(h8, tnum); + } + else + { + //logerror("h8/3007 timer %d GRA match, stopping\n",tnum); + h8->timer[tnum]->adjust(attotime::never); + } + + h8->per_regs[0x64] |= 1<<tnum; + if(h8->per_regs[0x64] & (4<<tnum)) // interrupt enable + { + //logerror("h8/3007 timer %d GRA INTERRUPT\n",tnum); + h8_3002_InterruptRequest(h8, 24+tnum*4, 1); + } + } + // GRB match + if ((h8->per_regs[base + 0x1] & 0x30) && (count == ((h8->per_regs[base + 0x6]<<8) | h8->per_regs[base + 0x7]))) + { + if ((h8->per_regs[base + 0x0] & 0x60) == 0x40) + { + //logerror("h8/3007 timer %d GRB match, restarting\n",tnum); + count = 0; + h8_3007_itu_refresh_timer(h8, tnum); + } + else + { + //logerror("h8/3007 timer %d GRB match, stopping\n",tnum); + h8->timer[tnum]->adjust(attotime::never); + } + + h8->per_regs[0x65] |= 1<<tnum; + if(h8->per_regs[0x65] & (4<<tnum)) // interrupt enable + { + //logerror("h8/3007 timer %d GRB INTERRUPT\n",tnum); + h8_3002_InterruptRequest(h8, 25+tnum*4, 1); + } + } + // Overflow + if (((h8->per_regs[base + 0x1] & 0x33) == 0) && (count == 0)) + { + //logerror("h8/3007 timer %d OVF match, restarting\n",tnum); + h8->per_regs[0x66] |= 1<<tnum; + if(h8->per_regs[0x66] & (4<<tnum)) // interrupt enable + { + //logerror("h8/3007 timer %d OVF INTERRUPT\n",tnum); + h8_3002_InterruptRequest(h8, 26+tnum*4, 1); + } + } + + h8->per_regs[base + 0x2] = count >> 8; + h8->per_regs[base + 0x3] = count & 0xff; +} + +static TIMER_CALLBACK( h8itu_3007_timer_0_cb ) +{ + h83xx_state *h8 = (h83xx_state *)ptr; + + h8itu_3007_timer_expire(h8, 0); +} + +static TIMER_CALLBACK( h8itu_3007_timer_1_cb ) +{ + h83xx_state *h8 = (h83xx_state *)ptr; + + h8itu_3007_timer_expire(h8, 1); +} + +static TIMER_CALLBACK( h8itu_3007_timer_2_cb ) +{ + h83xx_state *h8 = (h83xx_state *)ptr; + + h8itu_3007_timer_expire(h8, 2); +} + +UINT8 h8_3007_itu_read8(h83xx_state *h8, UINT8 reg) +{ + UINT8 val; + + switch(reg) + { + case 0x60: + val = h8->h8TSTR | 0xf8; + break; + default: + val = h8->per_regs[reg]; + break; + } + + return val; +} + +void h8_3007_itu_write8(h83xx_state *h8, UINT8 reg, UINT8 val) +{ + logerror("%06x: h8/3007 reg %02x = %02x\n",h8->pc,reg,val); + h8->per_regs[reg] = val; + switch(reg) + { + case 0x60: + if ((val & 1) && !(h8->h8TSTR & 1)) + { + h8_3007_itu_refresh_timer(h8, 0); + } + if ((val & 2) && !(h8->h8TSTR & 2)) + { + h8_3007_itu_refresh_timer(h8, 1); + } + if ((val & 4) && !(h8->h8TSTR & 4)) + { + h8_3007_itu_refresh_timer(h8, 2); + } + h8->h8TSTR = val; + break; + default: + val = 0; + break; + } +} + +UINT8 h8_3007_register_read8(h83xx_state *h8, UINT32 address) +{ + UINT8 val; + UINT8 reg; + + address &= 0xffffff; + + reg = address & 0xff; + + if(reg >= 0x60 && reg <= 0x7f) + { + return h8_3007_itu_read8(h8, reg); + } + else + { + switch(reg) + { + case 0xb4: // serial port A status + val = h8->per_regs[reg]; + val |= 0xc4; // transmit finished, receive ready, no errors + break; + case 0xb5: // serial port A receive + val = h8->io->read_byte(H8_SERIAL_0); + break; + case 0xbc: // serial port B status + val = h8->per_regs[reg]; + val |= 0xc4; // transmit finished, receive ready, no errors + break; + case 0xbd: // serial port B receive + val = h8->io->read_byte(H8_SERIAL_1); + break; + case 0xe0: + val = h8->io->read_byte(H8_ADC_0_H); + break; + case 0xe1: + val = h8->io->read_byte(H8_ADC_0_L); + break; + case 0xe2: + val = h8->io->read_byte(H8_ADC_1_H); + break; + case 0xe3: + val = h8->io->read_byte(H8_ADC_1_L); + break; + case 0xe4: + val = h8->io->read_byte(H8_ADC_2_H); + break; + case 0xe5: + val = h8->io->read_byte(H8_ADC_2_L); + break; + case 0xe6: + val = h8->io->read_byte(H8_ADC_3_H); + break; + case 0xe7: + val = h8->io->read_byte(H8_ADC_3_L); + break; + case 0xe8: // adc status + val = 0x80; + break; + + case 0xd3: // port 4 data + val = h8->io->read_byte(H8_PORT_4); + break; + case 0xd5: // port 6 data + val = h8->io->read_byte(H8_PORT_6); + break; + case 0xd6: // port 7 data + val = h8->io->read_byte(H8_PORT_7); + break; + case 0xd7: // port 8 data + val = h8->io->read_byte(H8_PORT_8); + break; + case 0xd8: // port 9 data + val = h8->io->read_byte(H8_PORT_9); + break; + case 0xd9: // port a data + val = h8->io->read_byte(H8_PORT_A); + break; + case 0xda: // port b data + val = h8->io->read_byte(H8_PORT_B); + break; + default: + val = h8->per_regs[reg]; + break; + } + } + + return val; +} + +void h8_3007_register_write8(h83xx_state *h8, UINT32 address, UINT8 val) +{ + UINT8 reg; + + address &= 0xffffff; + + reg = address & 0xff; + + h8->per_regs[reg] = val; + + if(reg >= 0x60 && reg <= 0x7f) + { + h8_3007_itu_write8(h8, reg, val); + } + else + { + switch (reg) + { + case 0xb3: + h8->io->write_byte(H8_SERIAL_0, val); + break; + case 0xbb: + h8->io->write_byte(H8_SERIAL_1, val); + break; + case 0xd3: + h8->io->write_byte(H8_PORT_4, val); + break; + case 0xd5: // port 6 data + h8->io->write_byte(H8_PORT_6, val); + break; + case 0xd6: // port 7 data + h8->io->write_byte(H8_PORT_7, val); + break; + case 0xd7: // port 8 data + h8->io->write_byte(H8_PORT_8, val); + break; + case 0xd8: // port 9 data + h8->io->write_byte(H8_PORT_9, val); + break; + case 0xd9: // port a data + h8->io->write_byte(H8_PORT_A, val); + break; + case 0xda: // port b data + h8->io->write_byte(H8_PORT_B, val); + break; + } + } +} + +UINT8 h8_3007_register1_read8(h83xx_state *h8, UINT32 address) +{ + switch (address) + { + case 0xfee012: return h8->per_regs[0xF2]; // SYSCR + case 0xfee016: return h8_ISR_r(h8); // ISR + case 0xfee018: return h8->per_regs[0xF8]; // IPRA + } + + logerror("cpu '%s' (PC=%08X): unmapped I/O(1) byte read from %08X\n",h8->device->tag(),h8->pc,address); + return 0; +} + +void h8_3007_register1_write8(h83xx_state *h8, UINT32 address, UINT8 val) +{ + switch (address) + { + case 0xfee012: h8->per_regs[0xF2] = val; return; // SYSCR + case 0xfee016: h8_ISR_w(h8, val); return; // ISR + case 0xfee018: h8->per_regs[0xF8] = val; return; // IPRA + } + logerror("cpu '%s' (PC=%08X): unmapped I/O(1) byte write to %08X = %02X\n",h8->device->tag(),h8->pc,address,val); +} + +void h8_3007_itu_init(h83xx_state *h8) +{ + h8->timer[0] = h8->device->machine().scheduler().timer_alloc(FUNC(h8itu_3007_timer_0_cb), h8); + h8->timer[1] = h8->device->machine().scheduler().timer_alloc(FUNC(h8itu_3007_timer_1_cb), h8); + h8->timer[2] = h8->device->machine().scheduler().timer_alloc(FUNC(h8itu_3007_timer_2_cb), h8); + + h8_itu_reset(h8); +} + +void h8_itu_init(h83xx_state *h8) +{ + h8->timer[0] = h8->device->machine().scheduler().timer_alloc(FUNC(h8itu_timer_0_cb), h8); + h8->timer[1] = h8->device->machine().scheduler().timer_alloc(FUNC(h8itu_timer_1_cb), h8); + h8->timer[2] = h8->device->machine().scheduler().timer_alloc(FUNC(h8itu_timer_2_cb), h8); + h8->timer[3] = h8->device->machine().scheduler().timer_alloc(FUNC(h8itu_timer_3_cb), h8); + h8->timer[4] = h8->device->machine().scheduler().timer_alloc(FUNC(h8itu_timer_4_cb), h8); + + h8_itu_reset(h8); +} + +void h8_itu_reset(h83xx_state *h8) +{ + int i; + + // stop all the timers + for (i=0; i<5; i++) + h8->timer[i]->adjust(attotime::never); +} |