diff options
Diffstat (limited to 'src/devices/cpu/tms9900/tms9995.cpp')
-rw-r--r-- | src/devices/cpu/tms9900/tms9995.cpp | 132 |
1 files changed, 72 insertions, 60 deletions
diff --git a/src/devices/cpu/tms9900/tms9995.cpp b/src/devices/cpu/tms9900/tms9995.cpp index 2c24f7e7697..7f721c25197 100644 --- a/src/devices/cpu/tms9900/tms9995.cpp +++ b/src/devices/cpu/tms9900/tms9995.cpp @@ -124,30 +124,30 @@ enum Add the desired LOG aspect to the VERBOSE line ******************************************************************/ -#define LOG_OP (1U<<1) // Current instruction -#define LOG_EXEC (1U<<2) // Address of current instruction -#define LOG_CONFIG (1U<<3) // Configuration -#define LOG_CYCLES (1U<<4) // Cycles -#define LOG_WARN (1U<<5) // Illegal operation or other condition -#define LOG_MEM (1U<<6) // Memory access -#define LOG_CONTEXT (1U<<7) // Context switch -#define LOG_INT (1U<<8) // Interrupts -#define LOG_READY (1U<<9) // READY line input -#define LOG_CLOCK (1U<<10) // Clock pulses -#define LOG_ADDRESSBUS (1U<<11) // Address bus operation -#define LOG_STATUS (1U<<12) // Status register -#define LOG_CRU (1U<<13) // CRU operations -#define LOG_DEC (1U<<14) // Decrementer -#define LOG_WAIT (1U<<15) // Wait states -#define LOG_HOLD (1U<<16) // Hold states -#define LOG_IDLE (1U<<17) // Idle states -#define LOG_EMU (1U<<18) // Emulation details -#define LOG_MICRO (1U<<19) // Microinstruction processing -#define LOG_INTD (1U<<20) // Interrupts (detailed phases) -#define LOG_DETAIL (1U<<31) // Increased detail +#define LOG_OP (1U << 1) // Current instruction +#define LOG_EXEC (1U << 2) // Address of current instruction +#define LOG_CONFIG (1U << 3) // Configuration +#define LOG_CYCLES (1U << 4) // Cycles +#define LOG_WARN (1U << 5) // Illegal operation or other condition +#define LOG_MEM (1U << 6) // Memory access +#define LOG_CONTEXT (1U << 7) // Context switch +#define LOG_INT (1U << 8) // Interrupts +#define LOG_READY (1U << 9) // READY line input +#define LOG_CLOCK (1U << 10) // Clock pulses +#define LOG_ADDRESSBUS (1U << 11) // Address bus operation +#define LOG_STATUS (1U << 12) // Status register +#define LOG_CRU (1U << 13) // CRU operations +#define LOG_DEC (1U << 14) // Decrementer +#define LOG_WAIT (1U << 15) // Wait states +#define LOG_HOLD (1U << 16) // Hold states +#define LOG_IDLE (1U << 17) // Idle states +#define LOG_EMU (1U << 18) // Emulation details +#define LOG_MICRO (1U << 19) // Microinstruction processing +#define LOG_INTD (1U << 20) // Interrupts (detailed phases) +#define LOG_DETAIL (1U << 31) // Increased detail // Minimum log should be config and warnings -#define VERBOSE ( LOG_CONFIG | LOG_WARN ) +#define VERBOSE (LOG_CONFIG | LOG_WARN) #include "logmacro.h" @@ -194,17 +194,10 @@ enum void tms9995_device::device_start() { - // TODO: Restore save state suport - m_prgspace = &space(AS_PROGRAM); m_setaddr = has_space(AS_SETADDRESS) ? &space(AS_SETADDRESS) : nullptr; m_cru = &space(AS_IO); - // Resolve our external connections - m_external_operation.resolve(); - m_clock_out_line.resolve(); - m_holda_line.resolve(); - // set our instruction counter set_icountptr(m_icount); @@ -216,10 +209,14 @@ void tms9995_device::device_start() m_nmi_active = false; m_int_overflow = false; + m_reset = false; + m_idle_state = false; m_source_value = 0; + m_index = 0; + // add the states for the debugger for (int i=0; i < 20; i++) { @@ -227,8 +224,8 @@ void tms9995_device::device_start() // callexport = need to use the state_export method to read the state variable state_add(i, s_statename[i], m_state_any).callimport().callexport().formatstr("%04X"); } - state_add(STATE_GENPC, "GENPC", PC_debug).formatstr("%4s").noshow(); - state_add(STATE_GENPCBASE, "CURPC", PC_debug).formatstr("%4s").noshow(); + state_add(STATE_GENPC, "GENPC", PC_debug).noshow(); + state_add(STATE_GENPCBASE, "CURPC", PC_debug).noshow(); state_add(STATE_GENFLAGS, "status", m_state_any).callimport().callexport().formatstr("%16s").noshow(); // Set up the lookup table for command decoding @@ -242,7 +239,7 @@ void tms9995_device::device_start() save_item(NAME(PC)); save_item(NAME(ST)); // save_item(NAME(PC_debug)); // only for debugger output - save_pointer(NAME(m_onchip_memory),256); + save_item(NAME(m_onchip_memory)); save_item(NAME(m_idle_state)); save_item(NAME(m_nmi_state)); save_item(NAME(m_hold_state)); @@ -285,7 +282,7 @@ void tms9995_device::device_start() save_item(NAME(m_cru_address)); save_item(NAME(m_cru_value)); save_item(NAME(m_cru_first_read)); - save_pointer(NAME(m_flag),16); + save_item(NAME(m_flag)); save_item(NAME(IR)); save_item(NAME(m_pre_IR)); save_item(NAME(m_command)); @@ -381,7 +378,7 @@ void tms9995_device::state_string_export(const device_state_entry &entry, std::s { static char const statestr[] = "LAECOPX-----IIII"; char flags[17]; - memset(flags, 0x00, ARRAY_LENGTH(flags)); + std::fill(std::begin(flags), std::end(flags), 0x00); uint16_t val = 0x8000; if (entry.index()==STATE_GENFLAGS) { @@ -1228,7 +1225,7 @@ void tms9995_device::execute_run() if (m_check_ready && m_ready == false) { // We are in a wait state - LOGMASKED(LOG_WAIT, "wait state\n"); + LOGMASKED(LOG_WAIT, "wait\n"); // The clock output should be used to change the state of an outer // device which operates the READY line pulse_clock(1); @@ -1341,7 +1338,7 @@ void tms9995_device::execute_set_input(int irqline, int state) /* Triggers a RESET. */ -WRITE_LINE_MEMBER( tms9995_device::reset_line ) +void tms9995_device::reset_line(int state) { if (state==ASSERT_LINE) { @@ -1360,9 +1357,9 @@ void tms9995_device::pulse_clock(int count) { for (int i=0; i < count; i++) { - if (!m_clock_out_line.isnull()) m_clock_out_line(ASSERT_LINE); + if (!m_clock_out_line.isunset()) m_clock_out_line(ASSERT_LINE); m_ready = m_ready_bufd && !m_request_auto_wait_state; // get the latched READY state - if (!m_clock_out_line.isnull()) m_clock_out_line(CLEAR_LINE); + if (!m_clock_out_line.isunset()) m_clock_out_line(CLEAR_LINE); m_icount--; // This is the only location where we count down the cycles. if (m_check_ready) @@ -1384,13 +1381,13 @@ void tms9995_device::pulse_clock(int count) /* Enter the hold state. */ -WRITE_LINE_MEMBER( tms9995_device::hold_line ) +void tms9995_device::hold_line(int state) { m_hold_requested = (state==ASSERT_LINE); LOGMASKED(LOG_HOLD, "set HOLD = %d\n", state); if (!m_hold_requested) { - if (!m_holda_line.isnull()) m_holda_line(CLEAR_LINE); + if (!m_holda_line.isunset()) m_holda_line(CLEAR_LINE); } } @@ -1398,7 +1395,7 @@ WRITE_LINE_MEMBER( tms9995_device::hold_line ) Signal READY to the CPU. When cleared, the CPU enters wait states. This becomes effective on a clock pulse. */ -WRITE_LINE_MEMBER( tms9995_device::ready_line ) +void tms9995_device::ready_line(int state) { bool newready = (state==ASSERT_LINE); @@ -1434,7 +1431,7 @@ void tms9995_device::abort_operation() void tms9995_device::set_hold_state(bool state) { if (m_hold_state != state) - if (!m_holda_line.isnull()) m_holda_line(state? ASSERT_LINE : CLEAR_LINE); + if (!m_holda_line.isunset()) m_holda_line(state? ASSERT_LINE : CLEAR_LINE); m_hold_state = state; } @@ -1632,11 +1629,13 @@ void tms9995_device::command_completed() // Pseudo state at the end of the current instruction cycle sequence if (LOG_CYCLES & VERBOSE) { - logerror("+++++ Instruction %04x (%s) completed", IR, opname[m_command]); + // logerror("+++++ 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) logerror(", consumed %d cycles", cycles); - logerror(" +++++\n"); + // if (cycles > 0 && cycles < 10000) logerror(", consumed %d cycles", cycles); + // logerror(" +++++\n"); + if (cycles > 0 && cycles < 10000) logerror("%04x %s [%02d]\n", PC_debug, opname[m_command], cycles); + else logerror("%04x %s [ ?]\n", PC_debug, opname[m_command]); } if (m_int_pending != 0) @@ -1827,12 +1826,22 @@ void tms9995_device::mem_read() // Ignore the READY state m_check_ready = false; + // We put fffc-ffff back into the f000-f0ff area - m_current_value = m_onchip_memory[m_address & 0x00ff]<<8; - if (m_word_access || !m_byteop) + offs_t intaddr = m_address & 0x00fe; + + // An on-chip memory access is also visible to the outside world ([1], 2.3.1.2) + // but only on word boundary, as only full words are read. + if (m_setaddr) + m_setaddr->write_byte(m_address & 0xfffe, (TMS99xx_BUS_DBIN | (m_iaq? TMS99xx_BUS_IAQ : 0))); + + // Always read a word from internal memory + m_current_value = (m_onchip_memory[intaddr] << 8) | m_onchip_memory[intaddr + 1]; + + if (!m_word_access && m_byteop) { - // We have a word operation; add the low byte right here (just 1 cycle) - m_current_value |= (m_onchip_memory[(m_address & 0x00ff)+1] & 0xff); + if ((m_address & 1)==1) m_current_value = m_current_value << 8; + m_current_value &= 0xff00; } pulse_clock(1); } @@ -1940,8 +1949,10 @@ void tms9995_device::mem_write() // will result in the data byte being written into the byte specifically addressed // and random bits being written into the other byte of the decrementer." - // So we just don't care about the low byte. - if (m_address == 0xfffb) m_current_value >>= 8; + // Tests on a real 9995 show that both bytes have the same value + // after a byte operation + u16 decbyte = m_current_value & 0xff00; + m_current_value = decbyte | (decbyte >> 8); // dito: "This also loads the Decrementing Register with the same count." m_starting_count_storage_register = m_decrementer_value = m_current_value; @@ -1963,6 +1974,12 @@ void tms9995_device::mem_write() if (m_word_access || !m_byteop) m_address &= 0xfffe; LOGMASKED(LOG_MEM, "write to onchip memory (single pass, address %04x, value=%04x)\n", m_address, m_current_value); + + // An on-chip memory access is also visible to the outside world ([1], 2.3.1.2) + // but only on word boundary + if (m_setaddr) + m_setaddr->write_byte(m_address & 0xfffe, TMS99xx_BUS_WRITE); + m_check_ready = false; m_onchip_memory[m_address & 0x00ff] = (m_current_value >> 8) & 0xff; if (m_word_access || !m_byteop) @@ -2723,7 +2740,7 @@ void tms9995_device::alu_external() LOGMASKED(LOG_OP, "RSET, new ST = %04x\n", ST); } - if (!m_external_operation.isnull()) + if (!m_external_operation.isunset()) m_external_operation((IR >> 5) & 0x07, 1, 0xff); } @@ -3232,7 +3249,7 @@ void tms9995_device::alu_single_arithm() set_status_bit(ST_OV, src_val == 0x8000); break; case SWPB: - m_current_value = ((m_current_value << 8) | (m_current_value >> 8)) & 0xffff; + m_current_value = swapendian_int16(m_current_value); // I don't know what they are doing right now, but we lose a lot of cycles // according to the spec (which can indeed be proved on a real system) @@ -3481,7 +3498,7 @@ void tms9995_device::alu_int() The minimum number of cycles applies to a command like SETO R0 with R0 in on-chip RAM. */ -uint32_t tms9995_device::execute_min_cycles() const +uint32_t tms9995_device::execute_min_cycles() const noexcept { return 3; } @@ -3490,16 +3507,11 @@ uint32_t tms9995_device::execute_min_cycles() const The maximum number of cycles applies to a STCR command with the destination operand off-chip and 16 bits of transfer. */ -uint32_t tms9995_device::execute_max_cycles() const +uint32_t tms9995_device::execute_max_cycles() const noexcept { return 47; } -uint32_t tms9995_device::execute_input_lines() const -{ - return 2; -} - std::unique_ptr<util::disasm_interface> tms9995_device::create_disassembler() { return std::make_unique<tms9900_disassembler>(TMS9995_ID); |