diff --git a/src/emu/cpu/tms9900/tms9900.c b/src/emu/cpu/tms9900/tms9900.c index 6b7f5793562..02801a01e2b 100644 --- a/src/emu/cpu/tms9900/tms9900.c +++ b/src/emu/cpu/tms9900/tms9900.c @@ -177,6 +177,7 @@ void tms99xx_device::device_start() m_clock_out_line.resolve(conf->clock_out, *this); m_wait_line.resolve(conf->wait_line, *this); m_holda_line.resolve(conf->holda_line, *this); + m_dbin_line.resolve(conf->dbin_line, *this); // we need this for the set_address operation // set our instruction counter m_icountptr = &m_icount; @@ -202,7 +203,7 @@ void tms99xx_device::device_start() void tms99xx_device::device_stop() { int k = 0; - if (VERBOSE>8) LOG("tms99xx: Deleting lookup tables\n"); + if (VERBOSE>3) LOG("tms99xx: Deleting lookup tables\n"); while (m_lotables[k]!=NULL) delete[] m_lotables[k++]; } @@ -1122,18 +1123,18 @@ void tms99xx_device::execute_run() m_program[MPC] != MEMORY_READ && m_program[MPC] != MEMORY_WRITE && m_program[MPC] != REG_READ && m_program[MPC] != REG_WRITE))) { - if (VERBOSE>5) LOG("tms99xx: hold state\n"); + if (VERBOSE>2) LOG("tms99xx: hold state\n"); if (!m_hold_acknowledged) acknowledge_hold(); pulse_clock(1); } else { // Normal operation - if (m_check_ready && m_ready_state == false) + if (m_check_ready && m_ready == false) { // We are in a wait state set_wait_state(true); - if (VERBOSE>5) LOG("tms99xx: wait state\n"); + if (VERBOSE>2) LOG("tms99xx: wait state\n"); // The clock output should be used to change the state of an outer // device which operates the READY line pulse_clock(1); @@ -1157,6 +1158,7 @@ void tms99xx_device::execute_run() { m_pass = 1; MPC++; + m_mem_phase = 1; m_iaq_line(CLEAR_LINE); } } @@ -1214,10 +1216,11 @@ void tms99xx_device::service_interrupt() m_command = INTR; m_idle_state = false; m_external_operation(IDLE_OP, 0); - m_lowbyte = false; m_state = 0; + m_dbin_line(ASSERT_LINE); + // If reset, we just start with execution, otherwise we put the MPC // on the first microinstruction, which also means that the main loop shall // leave it where it is. So we pretend we have another pass to do. @@ -1227,13 +1230,15 @@ void tms99xx_device::service_interrupt() { m_irq_level = RESET_INT; - m_ready_state = true; + m_ready_bufd = true; + m_ready = true; m_load_state = false; m_hold_state = false; m_hold_acknowledged = false; m_wait_state = false; IR = 0; ST = 0; + m_mem_phase = 1; m_reset = false; } @@ -1251,9 +1256,14 @@ void tms99xx_device::pulse_clock(int count) for (int i=0; i < count; i++) { m_clock_out_line(ASSERT_LINE); + m_ready = m_ready_bufd; // get the latched READY state m_clock_out_line(CLEAR_LINE); m_icount--; // This is the only location where we count down the cycles. - if (VERBOSE>7) LOG("tms99xx: pulse_clock\n"); + if (VERBOSE>7) + { + if (m_check_ready) LOG("tms99xx: pulse_clock, READY=%d\n", m_ready? 1:0); + else LOG("tms99xx: pulse_clock\n"); + } } } @@ -1280,11 +1290,12 @@ inline void tms99xx_device::acknowledge_hold() } /* - Signal READY to the CPU. When cleared, the CPU enters wait states. + Signal READY to the CPU. When cleared, the CPU enters wait states. This + becomes effective on a clock pulse. */ void tms99xx_device::set_ready(int state) { - m_ready_state = (state==ASSERT_LINE); + m_ready_bufd = (state==ASSERT_LINE); } void tms99xx_device::abort_operation() @@ -1344,7 +1355,7 @@ void tms99xx_device::decode(UINT16 inst) m_program = decoded->prog; MPC = -1; m_command = decoded->id; - if (VERBOSE>7) LOG("tms99xx: Command decoded as id %d, %s, base opcode %04x\n", m_command, opname[m_command], decoded->opcode); + if (VERBOSE>8) LOG("tms99xx: Command decoded as id %d, %s, base opcode %04x\n", m_command, opname[m_command], decoded->opcode); // Byte operations are either format 1 with the byte flag set // or format 4 (CRU multi bit operations) with 1-8 bits to transfer. m_byteop = ((decoded->format==1 && ((IR & 0x1000)!=0)) @@ -1360,77 +1371,107 @@ inline bool tms99xx_device::byte_operation() void tms99xx_device::acquire_instruction() { - m_iaq_line(ASSERT_LINE); - m_address = PC; - m_first_cycle = m_icount; + if (m_mem_phase == 1) + { + m_iaq_line(ASSERT_LINE); + m_address = PC; + m_first_cycle = m_icount; + } + mem_read(); - decode(m_current_value); - if (VERBOSE>3) LOG("tms99xx: ===== Next operation %04x (%s) at %04x =====\n", IR, opname[m_command], PC); - debugger_instruction_hook(this, PC); - PC = (PC + 2) & 0xfffe & m_prgaddr_mask; - // IAQ will be cleared in the main loop + + if (m_mem_phase == 1) + { + decode(m_current_value); + if (VERBOSE>3) LOG("tms99xx: ===== Next operation %04x (%s) at %04x =====\n", IR, opname[m_command], PC); + debugger_instruction_hook(this, PC); + PC = (PC + 2) & 0xfffe & m_prgaddr_mask; + // IAQ will be cleared in the main loop + } } /* - Memory read: - 1) Pulse clock (done above) - 2) Set address (we also get the value right here) - 3) Pulse clock - 4) If READY=L (WAIT=H, GOTO 3) else (WAIT=L, STOP) - + Memory read Clock cycles: 2 + W, W = number of wait states */ void tms99xx_device::mem_read() { - // The following line will be taken out of this method and - // be executed at an earlier microprogram clock tick // After set_address, any device attached to the address bus may pull down // READY in order to put the CPU into wait state before the read_word // operation will be performed // set_address and read_word should pass the same address as argument - m_prgspace->set_address(m_address & m_prgaddr_mask & 0xfffe); + if (m_mem_phase==1) + { + m_dbin_line(ASSERT_LINE); + m_prgspace->set_address(m_address & m_prgaddr_mask & 0xfffe); + m_check_ready = true; + m_mem_phase = 2; + m_pass = 2; + if (VERBOSE>7) LOG("tms99xx: set address bus %04x\n", m_address); - m_current_value = m_prgspace->read_word(m_address & m_prgaddr_mask & 0xfffe); - pulse_clock(2); - m_check_ready = true; - if (VERBOSE>7) LOG("tms99xx: memory read %04x -> %04x\n", m_address, m_current_value); + pulse_clock(1); // Concludes the first cycle + // If READY has been found to be low, the CPU will now stay in the wait state loop + } + else + { + // Second phase (after READY was raised again) + m_current_value = m_prgspace->read_word(m_address & m_prgaddr_mask & 0xfffe); + pulse_clock(1); + m_dbin_line(CLEAR_LINE); + m_mem_phase = 1; // reset to phase 1 + if (VERBOSE>7) LOG("tms99xx: memory read %04x -> %04x\n", m_address, m_current_value); + } } void tms99xx_device::mem_write() { - // see mem_read - m_prgspace->set_address(m_address & m_prgaddr_mask & 0xfffe); - - m_prgspace->write_word(m_address & m_prgaddr_mask & 0xfffe, m_current_value); - pulse_clock(2); - m_check_ready = true; - if (VERBOSE>7) LOG("tms99xx: memory write %04x <- %04x\n", m_address, m_current_value); + if (m_mem_phase==1) + { + m_dbin_line(CLEAR_LINE); + // When writing, the data bus is asserted immediately after the address bus + if (VERBOSE>7) LOG("tms99xx: set address bus %04x\n", m_address); + m_prgspace->set_address(m_address & m_prgaddr_mask & 0xfffe); + if (VERBOSE>7) LOG("tms99xx: memory write %04x <- %04x\n", m_address, m_current_value); + m_prgspace->write_word(m_address & m_prgaddr_mask & 0xfffe, m_current_value); + m_check_ready = true; + m_mem_phase = 2; + m_pass = 2; + pulse_clock(1); + } + else + { + // Second phase (we arrive here when the wait states are over) + pulse_clock(1); + } } void tms99xx_device::register_read() { // Need to set m_address for F1/F3 (we don't know what the data_derive did) - m_address = WP + (m_regnumber<<1); + if (m_mem_phase==1) + { + m_address = WP + (m_regnumber<<1); + } + mem_read(); - m_check_ready = true; - m_register_contents = m_current_value; + + if (m_mem_phase==1) + { + m_register_contents = m_current_value; + } } /* Memory write: - 1) Pulse clock - 2) Set address and write (as in the real system) - 3) Pulse clock - 4) If READY=L (WAIT=H, GOTO 3) else (WAIT=L, STOP) Clock cycles: 2 + W, W = number of wait states */ void tms99xx_device::register_write() { + // This will be called twice; m_pass is set by the embedded mem_write UINT16 addr_save = m_address; m_address = (WP + (m_regnumber<<1)) & m_prgaddr_mask & 0xfffe; mem_write(); - m_check_ready = true; m_address = addr_save; } @@ -1530,13 +1571,13 @@ void tms99xx_device::command_completed() // Pseudo state at the end of the current instruction cycle sequence if (VERBOSE>4) { - LOG("tms99xx: +++++ Instruction %04x (%s) completed +++++\n", IR, opname[m_command]); + LOG("tms99xx: +++++ Instruction %04x (%s) completed", IR, opname[m_command]); int cycles = m_first_cycle - m_icount; // Avoid nonsense values due to expired and resumed main loop - if (cycles > 0 && cycles < 10000) LOG("tms99xx: Consumed %d cycles\n", cycles); + if (cycles > 0 && cycles < 10000) LOG(", consumed %d cycles", cycles); + LOG(" +++++\n"); } m_program = NULL; - m_lowbyte = false; } /* diff --git a/src/emu/cpu/tms9900/tms9900.h b/src/emu/cpu/tms9900/tms9900.h index 96fa15197c7..ee9bfaa330e 100644 --- a/src/emu/cpu/tms9900/tms9900.h +++ b/src/emu/cpu/tms9900/tms9900.h @@ -117,6 +117,7 @@ struct tms99xx_config devcb_write_line clock_out; devcb_write_line wait_line; devcb_write_line holda_line; + devcb_write_line dbin_line; }; #define TMS99xx_CONFIG(name) \ @@ -208,12 +209,12 @@ protected: // Data bus width. Needed for TMS9980. int m_databus_width; - // Needed for TMS9980 - bool m_lowbyte; - // Check the READY line? bool m_check_ready; + // Phase of the memory access + int m_mem_phase; + // Max address const UINT16 m_prgaddr_mask; const UINT16 m_cruaddr_mask; @@ -238,13 +239,22 @@ protected: // Get the value of the interrupt level lines devcb_resolved_read8 m_get_intlevel; + // DBIN line. When asserted (high), the CPU has disabled the data bus output buffers. + devcb_resolved_write_line m_dbin_line; + private: // Indicates if this is a byte-oriented command inline bool byte_operation(); // Processor states bool m_idle_state; - bool m_ready_state; + + // READY handling. The READY line is operated before the phi1 clock + // pulse rises. As the ready line is only set once in this emulation we + // keep the level in a buffer (like a latch) + bool m_ready_bufd; // buffered state + bool m_ready; // sampled value + bool m_wait_state; bool m_hold_state; diff --git a/src/emu/cpu/tms9900/tms9980a.c b/src/emu/cpu/tms9900/tms9980a.c index db13b8dda2f..b98789f1152 100644 --- a/src/emu/cpu/tms9900/tms9980a.c +++ b/src/emu/cpu/tms9900/tms9980a.c @@ -147,56 +147,79 @@ int tms9980a_device::get_intlevel(int state) void tms9980a_device::mem_read() { UINT8 value; - if (m_lowbyte) + switch (m_mem_phase) { + case 1: + m_pass = 4; // make the CPU visit this method more than once + m_dbin_line(ASSERT_LINE); + m_prgspace->set_address(m_address & m_prgaddr_mask & ~1); + if (VERBOSE>7) LOG("tms9980a: set address bus %04x\n", m_address & m_prgaddr_mask & ~1); + m_check_ready = true; + break; + case 2: + // Sample the value on the data bus (high byte) + value = m_prgspace->read_byte(m_address & m_prgaddr_mask & ~1); + if (VERBOSE>7) LOG("tms9980a: memory read high byte %04x -> %02x\n", m_address & m_prgaddr_mask & ~1, value); + m_current_value = (value << 8) & 0xff00; + break; + case 3: + m_prgspace->set_address((m_address & m_prgaddr_mask) | 1); + if (VERBOSE>7) LOG("tms9980a: set address bus %04x\n", (m_address & m_prgaddr_mask) | 1); + break; + case 4: + // Sample the value on the data bus (low byte) value = m_prgspace->read_byte((m_address & m_prgaddr_mask) | 1); m_current_value = m_current_value | (value & 0x00ff); - if (VERBOSE>7) LOG("tms9980a: memory read low byte %04x -> complete word %04x\n", (m_address & m_prgaddr_mask) | 1, m_current_value); - m_lowbyte = false; + if (VERBOSE>7) LOG("tms9980a: memory read low byte %04x -> %02x -> complete word %04x\n", (m_address & m_prgaddr_mask) | 1, value, m_current_value); + break; } - else - { - value = m_prgspace->read_byte(m_address & 0x3ffe); - if (VERBOSE>7) LOG("tms9980a: memory read high byte %04x -> %02x\n", m_address & m_prgaddr_mask, value); - m_current_value = (value << 8) & 0xff00; - m_lowbyte = true; - m_pass = 2; // make the CPU visit this method once more - } - pulse_clock(2); - m_check_ready = true; + pulse_clock(1); + m_mem_phase = (m_mem_phase % 4) +1; } + void tms9980a_device::mem_write() { - if (m_lowbyte) + switch (m_mem_phase) { - m_prgspace->write_byte((m_address & 0x3ffe) | 1, m_current_value & 0xff); - if (VERBOSE>7) LOG("tms9980a: memory write low byte %04x <- %02x\n", (m_address & m_prgaddr_mask) | 1, m_current_value & 0xff); - m_lowbyte = false; + case 1: + m_pass = 4; // make the CPU visit this method once more + m_dbin_line(CLEAR_LINE); + m_prgspace->set_address(m_address & m_prgaddr_mask & ~1); + if (VERBOSE>7) LOG("tms9980a: set address bus %04x\n", m_address & m_prgaddr_mask & ~1); + m_prgspace->write_byte(m_address & 0x3ffe & ~1, (m_current_value >> 8)&0xff); + if (VERBOSE>7) LOG("tms9980a: memory write high byte %04x <- %02x\n", m_address & m_prgaddr_mask & ~1, (m_current_value >> 8)&0xff); + m_check_ready = true; + break; + case 2: + // no action here, just wait for READY + break; + case 3: + m_prgspace->set_address((m_address & m_prgaddr_mask) | 1); + if (VERBOSE>7) LOG("tms9980a: set address bus %04x\n", (m_address & m_prgaddr_mask) | 1); + m_prgspace->write_byte((m_address & m_prgaddr_mask) | 1, m_current_value & 0xff); + if (VERBOSE>7) LOG("tms9980a: memory write low byte %04x <- %02x\n", (m_address & m_prgaddr_mask) | 1, m_current_value & 0xff); + break; + case 4: + // no action here, just wait for READY + break; } - else - { - m_prgspace->write_byte(m_address & 0x3ffe, (m_current_value >> 8)&0xff); - if (VERBOSE>7) LOG("tms9980a: memory write high byte %04x <- %02x\n", m_address & m_prgaddr_mask, (m_current_value >> 8)&0xff); - m_lowbyte = true; - m_pass = 2; // make the CPU visit this method once more - } - pulse_clock(2); - m_check_ready = true; + pulse_clock(1); + m_mem_phase = (m_mem_phase % 4) +1; } void tms9980a_device::acquire_instruction() { - if (!m_lowbyte) + if (m_mem_phase == 1) { m_iaq_line(ASSERT_LINE); m_address = PC; m_first_cycle = m_icount; - mem_read(); } - else + mem_read(); + + if (m_mem_phase == 1) // changed by mem_read and wrapped { - mem_read(); decode(m_current_value); if (VERBOSE>3) LOG("tms9980a: ===== Next operation %04x (%s) at %04x =====\n", IR, opname[m_command], PC); debugger_instruction_hook(this, PC); @@ -205,6 +228,8 @@ void tms9980a_device::acquire_instruction() // IAQ will be cleared in the main loop } + + /**************************************************************************/ UINT32 tms9980a_device::execute_min_cycles() const { diff --git a/src/emu/cpu/tms9900/tms9995.c b/src/emu/cpu/tms9900/tms9995.c index 14356fd6bcd..52aa701ec9c 100644 --- a/src/emu/cpu/tms9900/tms9995.c +++ b/src/emu/cpu/tms9900/tms9995.c @@ -158,8 +158,8 @@ void tms9995_device::device_start() m_external_operation.resolve(conf->external_callback, *this); m_iaq_line.resolve(conf->iaq_line, *this); m_clock_out_line.resolve(conf->clock_out, *this); - m_wait_line.resolve(conf->wait_line, *this); m_holda_line.resolve(conf->holda_line, *this); + m_dbin_line.resolve(conf->dbin_line, *this); m_mp9537 = (conf->mode==NO_INTERNAL_RAM); m_check_overflow = (conf->overflow==OVERFLOW_INT); @@ -1086,10 +1086,9 @@ void tms9995_device::execute_run() do { // Normal operation - if (m_check_ready && m_ready_state == false) + if (m_check_ready && m_ready == false) { // We are in a wait state - set_wait_state(true); if (VERBOSE>2) LOG("tms9995: wait state\n"); // The clock output should be used to change the state of an outer // device which operates the READY line @@ -1105,7 +1104,6 @@ void tms9995_device::execute_run() } else { - set_wait_state(false); set_hold_state(false); m_check_ready = false; @@ -1189,9 +1187,15 @@ inline void tms9995_device::pulse_clock(int count) for (int i=0; i < count; i++) { m_clock_out_line(ASSERT_LINE); + m_ready = m_ready_bufd && !m_request_auto_wait_state; // get the latched READY state m_clock_out_line(CLEAR_LINE); m_icount--; // This is the only location where we count down the cycles. - if (VERBOSE>7) LOG("tms9995: pulse_clock\n"); + if (VERBOSE>7) + { + if (m_check_ready) LOG("tms9995: pulse_clock, READY=%d, auto_wait=%d\n", m_ready_bufd? 1:0, m_auto_wait? 1:0); + else LOG("tms9995: pulse_clock\n"); + } + m_request_auto_wait_state = false; if (m_flag[0] == false && m_flag[1] == true) trigger_decrementer(); } } @@ -1210,12 +1214,13 @@ void tms9995_device::set_hold(int state) } /* - Signal READY to the CPU. When cleared, the CPU enters wait states. + Signal READY to the CPU. When cleared, the CPU enters wait states. This + becomes effective on a clock pulse. */ void tms9995_device::set_ready(int state) { - if (VERBOSE>5) LOG("tms9995: set READY = %d\n", state); - m_ready_state = (state==ASSERT_LINE); + m_ready_bufd = (state==ASSERT_LINE); + if (VERBOSE>7) LOG("tms9995: set READY = %d\n", m_ready_bufd? 1 : 0); } /* @@ -1227,16 +1232,7 @@ void tms9995_device::abort_operation() // And don't forget that prefetch is a 2-pass operation, so this method // will be called a second time. Only when the lowbyte has been fetched, // continue with the next step - if (!m_lowbyte) command_completed(); -} - -/* - Enter or leave the wait state. We only operate the WAIT line when there is a change. -*/ -inline void tms9995_device::set_wait_state(bool state) -{ - if (m_wait_state != state) m_wait_line(state? ASSERT_LINE : CLEAR_LINE); - m_wait_state = state; + if (m_mem_phase==1) command_completed(); } /* @@ -1307,56 +1303,55 @@ void tms9995_device::int_prefetch_and_decode() bool check_int = (m_instruction->command != XOP && m_instruction->command != BLWP); int intmask = ST & 0x000f; - if (m_lowbyte) + if (m_mem_phase == 1) { - prefetch_and_decode(); - return; - } - - // Check interrupt lines - if (m_nmi_active) - { - if (VERBOSE>7) LOG("tms9995: Checking interrupts ... NMI active\n"); - m_int_pending |= PENDING_NMI; - m_idle_state = false; - PC = (PC + 2) & 0xfffe; // we have not prefetched the next instruction - } - else - { - m_int_pending = 0; - - if (m_int1_active && intmask >= 1 && check_int) m_int_pending |= PENDING_LEVEL1; - if (m_int_overflow && intmask >= 2 && check_int) m_int_pending |= PENDING_OVERFLOW; - if (m_int_decrementer && intmask >= 3 && check_int) m_int_pending |= PENDING_DECR; - if (m_int4_active && intmask >= 4 && check_int) m_int_pending |= PENDING_LEVEL4; - - if (m_int_pending!=0) + // Check interrupt lines + if (m_nmi_active) { - if (m_idle_state) - { - m_idle_state = false; - if (VERBOSE>7) LOG("tms9995: Interrupt occured, terminate IDLE state\n"); - } - PC = PC + 2; // PC must be advanced (see flow chart), but no prefetch - if (VERBOSE>7) LOG("tms9995: Interrupts pending; no prefetch; advance PC to %04x\n", PC); + if (VERBOSE>7) LOG("tms9995: Checking interrupts ... NMI active\n"); + m_int_pending |= PENDING_NMI; + m_idle_state = false; + PC = (PC + 2) & 0xfffe; // we have not prefetched the next instruction + return; } else { - if (VERBOSE>7) LOG("tms9995: Checking interrupts ... none pending\n"); - // No pending interrupts - if (check_idle && m_idle_state) + m_int_pending = 0; + + if (m_int1_active && intmask >= 1 && check_int) m_int_pending |= PENDING_LEVEL1; + if (m_int_overflow && intmask >= 2 && check_int) m_int_pending |= PENDING_OVERFLOW; + if (m_int_decrementer && intmask >= 3 && check_int) m_int_pending |= PENDING_DECR; + if (m_int4_active && intmask >= 4 && check_int) m_int_pending |= PENDING_LEVEL4; + + if (m_int_pending!=0) { - if (VERBOSE>7) LOG("tms9995: IDLE state\n"); - // We are IDLE, stay in the loop and do not advance the PC - m_pass = 2; - pulse_clock(1); + if (m_idle_state) + { + m_idle_state = false; + if (VERBOSE>7) LOG("tms9995: Interrupt occured, terminate IDLE state\n"); + } + PC = PC + 2; // PC must be advanced (see flow chart), but no prefetch + if (VERBOSE>7) LOG("tms9995: Interrupts pending; no prefetch; advance PC to %04x\n", PC); + return; } else { - prefetch_and_decode(); + if (VERBOSE>7) LOG("tms9995: Checking interrupts ... none pending\n"); + // No pending interrupts + if (check_idle && m_idle_state) + { + if (VERBOSE>7) LOG("tms9995: IDLE state\n"); + // We are IDLE, stay in the loop and do not advance the PC + m_pass = 2; + pulse_clock(1); + return; + } } } } + + // We reach this point in phase 1 if there is no interrupt and in all other phases + prefetch_and_decode(); } /* @@ -1367,46 +1362,28 @@ void tms9995_device::int_prefetch_and_decode() */ void tms9995_device::prefetch_and_decode() { - if (m_lowbyte) - { - // Second pass for getting the instruction - if (VERBOSE>6) LOG("tms9995: Prefetch memory access (second pass)\n"); - word_read(); - decode(m_current_value); // This is for free; in reality it is in parallel with the next memory operation - m_address = m_address_copy; // restore m_address - m_current_value = m_value_copy; // restore m_current_value - PC = (PC + 2) & 0xfffe; // advance PC - m_iaq_line(CLEAR_LINE); - if (VERBOSE>5) LOG("tms9995: ++++ Prefetch done ++++\n"); - m_lowbyte = false; - } - else + if (m_mem_phase==1) { // Fetch next instruction // Save these values; they have been computed during the current instruction execution m_address_copy = m_address; m_value_copy = m_current_value; - m_iaq_line(ASSERT_LINE); - m_address = PC; - if (VERBOSE>5) LOG("tms9995: **** Prefetching new instruction at %04x ****\n", PC); + } - m_lowbyte = false; // for mem_read - word_read(); // this is where the clock pulses occur + word_read(); - if (!m_lowbyte) - { - // Only if we got the word in one pass - decode(m_current_value); // This is for free; in reality it is in parallel with the next memory operation - - m_address = m_address_copy; // restore m_address - m_current_value = m_value_copy; // restore m_current_value - PC = (PC + 2) & 0xfffe; // advance PC - - m_iaq_line(CLEAR_LINE); - } + if (m_mem_phase==1) + { + // We're back in phase 1, i.e. the whole prefetch is done + decode(m_current_value); // This is for free; in reality it is in parallel with the next memory operation + m_address = m_address_copy; // restore m_address + m_current_value = m_value_copy; // restore m_current_value + PC = (PC + 2) & 0xfffe; // advance PC + m_iaq_line(CLEAR_LINE); + if (VERBOSE>5) LOG("tms9995: ++++ Prefetch done ++++\n"); } } @@ -1447,10 +1424,11 @@ void tms9995_device::command_completed() // Pseudo state at the end of the current instruction cycle sequence if (VERBOSE>4) { - LOG("tms9995: +++++ Instruction %04x (%s) completed +++++\n", m_instruction->IR, opname[m_instruction->command]); + LOG("tms9995: +++++ Instruction %04x (%s) completed", m_instruction->IR, opname[m_instruction->command]); int cycles = m_first_cycle - m_icount; // Avoid nonsense values due to expired and resumed main loop - if (cycles > 0 && cycles < 10000) LOG("tms9995: Consumed %d cycles\n", cycles); + if (cycles > 0 && cycles < 10000) LOG(", consumed %d cycles", cycles); + LOG(" +++++\n"); } if (m_int_pending != 0) @@ -1484,8 +1462,7 @@ void tms9995_device::service_interrupt() m_nmi_state = false; m_hold_state = false; - m_wait_state = false; - m_lowbyte = false; + m_mem_phase = 1; m_check_hold = false; m_word_access = false; m_int4_active = false; @@ -1500,9 +1477,10 @@ void tms9995_device::service_interrupt() // The auto-wait state generation is turned on when the READY line is cleared // on RESET. - m_auto_wait_state = !m_ready_state; - if (VERBOSE>0) LOG("tms9995: RESET; automatic wait state creation is %s\n", m_auto_wait_state? "enabled":"disabled"); - m_ready_state = true; + m_auto_wait = !m_ready_bufd; + if (VERBOSE>0) LOG("tms9995: RESET; automatic wait state creation is %s\n", m_auto_wait? "enabled":"disabled"); + // We reset the READY flag, or the CPU will not start + m_ready_bufd = true; } else { @@ -1574,6 +1552,7 @@ void tms9995_device::service_interrupt() m_instruction->byteop = false; m_instruction->command = INTR; m_pass = m_reset? 1 : 2; + m_from_reset = m_reset; if (m_reset) { @@ -1604,7 +1583,7 @@ void tms9995_device::service_interrupt() void tms9995_device::mem_read() { // First determine whether the memory is inside the CPU - // On-chip memory is F000 ... F0F9, F0FC-FFF9 = off-chip, FFFA/B = Decrementer + // On-chip memory is F000 ... F0F9, F0FA-FFF9 = off-chip, FFFA/B = Decrementer // FFFC-FFFF = NMI vector (on-chip) // There is a variant of the TMS9995 with no on-chip RAM which was used // for the TI-99/8 (9537). @@ -1641,42 +1620,55 @@ void tms9995_device::mem_read() } else { - // This is a off-chip access + // This is an off-chip access m_check_ready = true; - if (m_lowbyte) + UINT8 value; + UINT16 address = m_address; + + switch (m_mem_phase) { - // This is always the odd address - // With the OR we can ensure that we do not skip to an even address - // when we try to read a word from an odd address - m_current_value |= m_prgspace->read_byte(m_address | 0x0001); - m_lowbyte = false; - if (VERBOSE>3) LOG("tms9995: read external memory, second pass (address %04x, complete word = %04x)\n", m_address | 1, m_current_value); - m_check_hold = true; - } - else - { - UINT16 address = m_address; + case 1: + // Set address + // If this is a word access, 4 passes, else 2 passes + m_dbin_line(ASSERT_LINE); if (m_word_access || !m_instruction->byteop) { - // We have to come here a second time; do not advance the MPC - // if the address value is even - m_lowbyte = true; - m_pass = 2; + m_pass = 4; + // For word accesses, we always start at the even address address &= 0xfffe; - m_check_hold = false; - } - m_current_value = m_prgspace->read_byte(address) << 8; - if (VERBOSE>3) - { - if (m_pass==2) LOG("tms9995: read external memory, first pass (address %04x, value %02x)\n", address, (m_current_value>>8)&0xff); - else LOG("tms9995: read external memory (single pass), address %04x, value=%04x)\n", address, m_current_value); } + else m_pass = 2; + + m_check_hold = false; + if (VERBOSE>7) LOG("tms9995: set address bus %04x\n", m_address & ~1); + m_prgspace->set_address(address); + m_request_auto_wait_state = m_auto_wait; + break; + case 2: + // Sample the value on the data bus (high byte) + if (m_word_access || !m_instruction->byteop) address &= 0xfffe; + value = m_prgspace->read_byte(address); + if (VERBOSE>7) LOG("tms9995: memory read byte %04x -> %02x\n", m_address & ~1, value); + m_current_value = (value << 8) & 0xff00; + break; + case 3: + // Set address + 1 (unless byte command) + if (VERBOSE>7) LOG("tms9995: set address bus %04x\n", m_address | 1); + m_prgspace->set_address(m_address | 1); + break; + case 4: + // Read low byte + value = m_prgspace->read_byte(m_address | 1); + m_current_value |= value; + if (VERBOSE>3) LOG("tms9995: memory read byte %04x -> %02x, complete word = %04x\n", m_address | 1, value, m_current_value); + m_check_hold = true; + break; } - if (m_auto_wait_state) - { - if (VERBOSE>7) LOG("tms9995: Next pulse is auto wait\n"); - pulse_clock(1); - } + + m_mem_phase = (m_mem_phase % 4) +1; + + // Reset to 1 when we are done + if (m_pass==1) m_mem_phase = 1; } pulse_clock(1); } @@ -1738,7 +1730,6 @@ void tms9995_device::mem_write() pulse_clock(1); return; } - bool onchip = (((m_address & 0xff00)==0xf000 && (m_address < 0xf0fc)) || ((m_address & 0xfffc)==0xfffc)) && !m_mp9537; if (onchip) @@ -1753,41 +1744,50 @@ void tms9995_device::mem_write() } else { + // This is an off-chip access m_check_ready = true; + UINT16 address = m_address; + switch (m_mem_phase) + { + case 1: + // Set address + // If this is a word access, 4 passes, else 2 passes + m_dbin_line(CLEAR_LINE); - if (m_lowbyte) - { - // see above in mem_read - m_prgspace->write_byte(m_address | 0x0001, m_current_value & 0xff); - m_lowbyte = false; - if (VERBOSE>3) LOG("tms9995: write second pass (address %04x, value %02x)\n", m_address | 0x0001, m_current_value & 0xff); - m_check_hold = true; - } - else - { - UINT16 address = m_address; if (m_word_access || !m_instruction->byteop) { - // We have to come here a second time; do not advance the MPC - // if the address value is even - m_lowbyte = true; - m_pass = 2; + m_pass = 4; address &= 0xfffe; - m_check_hold = false; } - if (VERBOSE>3) - { - if (m_pass==2) LOG("tms9995: write external memory, first pass (address %04x, value %02x)\n", address, (m_current_value>>8)&0xff); - else LOG("tms9995: write external memory (single pass), address %04x, value=%02x\n", address, (m_current_value>>8)&0xff); - } - m_prgspace->write_byte(address, (m_current_value >> 8)& 0xff); + else m_pass = 2; + + m_check_hold = false; + if (VERBOSE>7) LOG("tms9995: set address bus %04x\n", address); + m_prgspace->set_address(address); + if (VERBOSE>7) LOG("tms9995: memory write byte %04x <- %02x\n", address, (m_current_value >> 8)&0xff); + m_prgspace->write_byte(address, (m_current_value >> 8)&0xff); + break; + + case 2: + // no action here, just wait for READY + break; + case 3: + // Set address + 1 (unless byte command) + if (VERBOSE>7) LOG("tms9995: set address bus %04x\n", m_address | 1); + m_prgspace->set_address(m_address | 1); + if (VERBOSE>7) LOG("tms9995: memory write byte %04x <- %02x\n", m_address | 1, m_current_value & 0xff); + m_prgspace->write_byte(m_address | 1, m_current_value & 0xff); + break; + case 4: + // no action here, just wait for READY + m_check_hold = true; + break; } - if (m_auto_wait_state) - { - if (VERBOSE>7) LOG("tms9995: Next pulse is auto wait\n"); - pulse_clock(1); - } + m_mem_phase = (m_mem_phase % 4) +1; + + // Reset to 1 when we are done + if (m_pass==1) m_mem_phase = 1; } pulse_clock(1); } @@ -2072,7 +2072,7 @@ void tms9995_device::operand_address_subprogram() } m_get_destination = true; - m_lowbyte = false; + m_mem_phase = 1; m_address_add = 0; MPC--; // will be increased in the mail loop if (VERBOSE>8) LOG("tms9995: *** Operand address derivation; address=%04x; index=%d\n", m_address, MPC+1); @@ -2087,7 +2087,7 @@ void tms9995_device::increment_register() m_address_saved = m_current_value; // need a special return so we do not lose the value m_current_value += m_instruction->byteop? 1 : 2; m_address = (WP + (m_regnumber<<1)) & 0xffff; - m_lowbyte = false; + m_mem_phase = 1; pulse_clock(1); } @@ -2101,7 +2101,7 @@ void tms9995_device::indexed_addressing() m_address_add = m_current_value; m_address = PC; PC = (PC + 2) & 0xfffe; - m_lowbyte = false; + m_mem_phase = 1; pulse_clock(1); } @@ -2112,7 +2112,7 @@ void tms9995_device::set_immediate() m_address = PC; m_source_value = m_current_value; // needed for AI, ANDI, ORI PC = (PC + 2) & 0xfffe; - m_lowbyte = false; + m_mem_phase = 1; } /************************************************************************** @@ -3227,7 +3227,7 @@ void tms9995_device::alu_int() if (((m_int_pending & PENDING_MID)!=0) && m_nmi_active) { - if (VERBOSE>5) LOG("tms9995: interrupt service (5): NMI active after context switch\n"); + if (VERBOSE>5) LOG("tms9995: interrupt service (6): NMI active after context switch\n"); m_int_pending &= ~PENDING_MID; m_address = 0xfffc; m_intmask = 0; @@ -3235,11 +3235,11 @@ void tms9995_device::alu_int() } else { - if (m_reset) + if (m_from_reset) { - if (VERBOSE>5) LOG("tms9995: interrupt service (5): RESET completed\n"); + if (VERBOSE>5) LOG("tms9995: interrupt service (6): RESET completed\n"); // We came from the RESET interrupt - m_reset = false; + m_from_reset = false; ST &= 0x01ff; m_mid_flag = false; // FLAG0 and FLAG1 are also set to zero after RESET ([1], sect. 2.3.1.2.2) diff --git a/src/emu/cpu/tms9900/tms9995.h b/src/emu/cpu/tms9900/tms9995.h index ea06b37a837..686cecb47f4 100644 --- a/src/emu/cpu/tms9900/tms9995.h +++ b/src/emu/cpu/tms9900/tms9995.h @@ -65,8 +65,8 @@ struct tms9995_config devcb_write8 external_callback; devcb_write_line iaq_line; devcb_write_line clock_out; - devcb_write_line wait_line; devcb_write_line holda_line; + devcb_write_line dbin_line; int mode; int overflow; }; @@ -151,18 +151,23 @@ private: bool m_idle_state; bool m_nmi_state; bool m_irq_state; - bool m_ready_state; - bool m_wait_state; bool m_hold_state; + // READY handling. The READY line is operated before the clock + // pulse falls. As the ready line is only set once in this emulation we + // keep the level in a buffer (like a latch) + bool m_ready_bufd; // buffered state + bool m_ready; // sampled value + // Auto-wait state generation - bool m_auto_wait_state; + bool m_request_auto_wait_state; + bool m_auto_wait; // Cycle counter int m_icount; - // The next memory access will address the low byte - bool m_lowbyte; + // Phase of the memory access + int m_mem_phase; // Check the READY line? bool m_check_ready; @@ -195,6 +200,7 @@ private: bool m_int_overflow; bool m_reset; + bool m_from_reset; bool m_mid_flag; // Flag field @@ -213,10 +219,7 @@ private: // Issue clock pulses. The TMS9995 uses one (output) clock cycle per machine cycle. inline void pulse_clock(int count); - // Signal the wait state via the external line - inline void set_wait_state(bool state); - - // Signal the wait state via the external line + // Signal the hold state via the external line inline void set_hold_state(bool state); // Only used for the DIV(S) operations. It seems sufficient to let the @@ -434,11 +437,11 @@ private: // Clock output. devcb_resolved_write_line m_clock_out_line; - // Wait output. When asserted (high), the CPU is in a wait state. - devcb_resolved_write_line m_wait_line; - // Asserted when the CPU is in a HOLD state devcb_resolved_write_line m_holda_line; + + // DBIN line. When asserted (high), the CPU has disabled the data bus output buffers. + devcb_resolved_write_line m_dbin_line; }; // device type definition