CPUs now utilizing split addressing (setaddress ... read/write)

This commit is contained in:
Michael Zapf 2013-09-30 23:14:38 +00:00
parent 32a57a15b4
commit 6c863c2d56
5 changed files with 334 additions and 255 deletions

View File

@ -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;
}
/*

View File

@ -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;

View File

@ -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
{

View File

@ -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)

View File

@ -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