CPUs now utilizing split addressing (setaddress ... read/write)
This commit is contained in:
parent
32a57a15b4
commit
6c863c2d56
@ -177,6 +177,7 @@ void tms99xx_device::device_start()
|
|||||||
m_clock_out_line.resolve(conf->clock_out, *this);
|
m_clock_out_line.resolve(conf->clock_out, *this);
|
||||||
m_wait_line.resolve(conf->wait_line, *this);
|
m_wait_line.resolve(conf->wait_line, *this);
|
||||||
m_holda_line.resolve(conf->holda_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
|
// set our instruction counter
|
||||||
m_icountptr = &m_icount;
|
m_icountptr = &m_icount;
|
||||||
@ -202,7 +203,7 @@ void tms99xx_device::device_start()
|
|||||||
void tms99xx_device::device_stop()
|
void tms99xx_device::device_stop()
|
||||||
{
|
{
|
||||||
int k = 0;
|
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++];
|
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] != MEMORY_READ && m_program[MPC] != MEMORY_WRITE &&
|
||||||
m_program[MPC] != REG_READ && m_program[MPC] != REG_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();
|
if (!m_hold_acknowledged) acknowledge_hold();
|
||||||
pulse_clock(1);
|
pulse_clock(1);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// Normal operation
|
// Normal operation
|
||||||
if (m_check_ready && m_ready_state == false)
|
if (m_check_ready && m_ready == false)
|
||||||
{
|
{
|
||||||
// We are in a wait state
|
// We are in a wait state
|
||||||
set_wait_state(true);
|
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
|
// The clock output should be used to change the state of an outer
|
||||||
// device which operates the READY line
|
// device which operates the READY line
|
||||||
pulse_clock(1);
|
pulse_clock(1);
|
||||||
@ -1157,6 +1158,7 @@ void tms99xx_device::execute_run()
|
|||||||
{
|
{
|
||||||
m_pass = 1;
|
m_pass = 1;
|
||||||
MPC++;
|
MPC++;
|
||||||
|
m_mem_phase = 1;
|
||||||
m_iaq_line(CLEAR_LINE);
|
m_iaq_line(CLEAR_LINE);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1214,10 +1216,11 @@ void tms99xx_device::service_interrupt()
|
|||||||
m_command = INTR;
|
m_command = INTR;
|
||||||
m_idle_state = false;
|
m_idle_state = false;
|
||||||
m_external_operation(IDLE_OP, 0);
|
m_external_operation(IDLE_OP, 0);
|
||||||
m_lowbyte = false;
|
|
||||||
|
|
||||||
m_state = 0;
|
m_state = 0;
|
||||||
|
|
||||||
|
m_dbin_line(ASSERT_LINE);
|
||||||
|
|
||||||
// If reset, we just start with execution, otherwise we put the MPC
|
// If reset, we just start with execution, otherwise we put the MPC
|
||||||
// on the first microinstruction, which also means that the main loop shall
|
// 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.
|
// 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_irq_level = RESET_INT;
|
||||||
|
|
||||||
m_ready_state = true;
|
m_ready_bufd = true;
|
||||||
|
m_ready = true;
|
||||||
m_load_state = false;
|
m_load_state = false;
|
||||||
m_hold_state = false;
|
m_hold_state = false;
|
||||||
m_hold_acknowledged = false;
|
m_hold_acknowledged = false;
|
||||||
m_wait_state = false;
|
m_wait_state = false;
|
||||||
IR = 0;
|
IR = 0;
|
||||||
ST = 0;
|
ST = 0;
|
||||||
|
m_mem_phase = 1;
|
||||||
|
|
||||||
m_reset = false;
|
m_reset = false;
|
||||||
}
|
}
|
||||||
@ -1251,9 +1256,14 @@ void tms99xx_device::pulse_clock(int count)
|
|||||||
for (int i=0; i < count; i++)
|
for (int i=0; i < count; i++)
|
||||||
{
|
{
|
||||||
m_clock_out_line(ASSERT_LINE);
|
m_clock_out_line(ASSERT_LINE);
|
||||||
|
m_ready = m_ready_bufd; // get the latched READY state
|
||||||
m_clock_out_line(CLEAR_LINE);
|
m_clock_out_line(CLEAR_LINE);
|
||||||
m_icount--; // This is the only location where we count down the cycles.
|
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)
|
void tms99xx_device::set_ready(int state)
|
||||||
{
|
{
|
||||||
m_ready_state = (state==ASSERT_LINE);
|
m_ready_bufd = (state==ASSERT_LINE);
|
||||||
}
|
}
|
||||||
|
|
||||||
void tms99xx_device::abort_operation()
|
void tms99xx_device::abort_operation()
|
||||||
@ -1344,7 +1355,7 @@ void tms99xx_device::decode(UINT16 inst)
|
|||||||
m_program = decoded->prog;
|
m_program = decoded->prog;
|
||||||
MPC = -1;
|
MPC = -1;
|
||||||
m_command = decoded->id;
|
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
|
// Byte operations are either format 1 with the byte flag set
|
||||||
// or format 4 (CRU multi bit operations) with 1-8 bits to transfer.
|
// or format 4 (CRU multi bit operations) with 1-8 bits to transfer.
|
||||||
m_byteop = ((decoded->format==1 && ((IR & 0x1000)!=0))
|
m_byteop = ((decoded->format==1 && ((IR & 0x1000)!=0))
|
||||||
@ -1360,77 +1371,107 @@ inline bool tms99xx_device::byte_operation()
|
|||||||
|
|
||||||
void tms99xx_device::acquire_instruction()
|
void tms99xx_device::acquire_instruction()
|
||||||
{
|
{
|
||||||
m_iaq_line(ASSERT_LINE);
|
if (m_mem_phase == 1)
|
||||||
m_address = PC;
|
{
|
||||||
m_first_cycle = m_icount;
|
m_iaq_line(ASSERT_LINE);
|
||||||
|
m_address = PC;
|
||||||
|
m_first_cycle = m_icount;
|
||||||
|
}
|
||||||
|
|
||||||
mem_read();
|
mem_read();
|
||||||
decode(m_current_value);
|
|
||||||
if (VERBOSE>3) LOG("tms99xx: ===== Next operation %04x (%s) at %04x =====\n", IR, opname[m_command], PC);
|
if (m_mem_phase == 1)
|
||||||
debugger_instruction_hook(this, PC);
|
{
|
||||||
PC = (PC + 2) & 0xfffe & m_prgaddr_mask;
|
decode(m_current_value);
|
||||||
// IAQ will be cleared in the main loop
|
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:
|
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)
|
|
||||||
|
|
||||||
Clock cycles: 2 + W, W = number of wait states
|
Clock cycles: 2 + W, W = number of wait states
|
||||||
*/
|
*/
|
||||||
void tms99xx_device::mem_read()
|
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
|
// 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
|
// READY in order to put the CPU into wait state before the read_word
|
||||||
// operation will be performed
|
// operation will be performed
|
||||||
// set_address and read_word should pass the same address as argument
|
// 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(1); // Concludes the first cycle
|
||||||
pulse_clock(2);
|
// If READY has been found to be low, the CPU will now stay in the wait state loop
|
||||||
m_check_ready = true;
|
}
|
||||||
if (VERBOSE>7) LOG("tms99xx: memory read %04x -> %04x\n", m_address, m_current_value);
|
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()
|
void tms99xx_device::mem_write()
|
||||||
{
|
{
|
||||||
// see mem_read
|
if (m_mem_phase==1)
|
||||||
m_prgspace->set_address(m_address & m_prgaddr_mask & 0xfffe);
|
{
|
||||||
|
m_dbin_line(CLEAR_LINE);
|
||||||
m_prgspace->write_word(m_address & m_prgaddr_mask & 0xfffe, m_current_value);
|
// When writing, the data bus is asserted immediately after the address bus
|
||||||
pulse_clock(2);
|
if (VERBOSE>7) LOG("tms99xx: set address bus %04x\n", m_address);
|
||||||
m_check_ready = true;
|
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);
|
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()
|
void tms99xx_device::register_read()
|
||||||
{
|
{
|
||||||
// Need to set m_address for F1/F3 (we don't know what the data_derive did)
|
// 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();
|
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:
|
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
|
Clock cycles: 2 + W, W = number of wait states
|
||||||
*/
|
*/
|
||||||
void tms99xx_device::register_write()
|
void tms99xx_device::register_write()
|
||||||
{
|
{
|
||||||
|
// This will be called twice; m_pass is set by the embedded mem_write
|
||||||
UINT16 addr_save = m_address;
|
UINT16 addr_save = m_address;
|
||||||
m_address = (WP + (m_regnumber<<1)) & m_prgaddr_mask & 0xfffe;
|
m_address = (WP + (m_regnumber<<1)) & m_prgaddr_mask & 0xfffe;
|
||||||
mem_write();
|
mem_write();
|
||||||
m_check_ready = true;
|
|
||||||
m_address = addr_save;
|
m_address = addr_save;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1530,13 +1571,13 @@ void tms99xx_device::command_completed()
|
|||||||
// Pseudo state at the end of the current instruction cycle sequence
|
// Pseudo state at the end of the current instruction cycle sequence
|
||||||
if (VERBOSE>4)
|
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;
|
int cycles = m_first_cycle - m_icount;
|
||||||
// Avoid nonsense values due to expired and resumed main loop
|
// 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_program = NULL;
|
||||||
m_lowbyte = false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -117,6 +117,7 @@ struct tms99xx_config
|
|||||||
devcb_write_line clock_out;
|
devcb_write_line clock_out;
|
||||||
devcb_write_line wait_line;
|
devcb_write_line wait_line;
|
||||||
devcb_write_line holda_line;
|
devcb_write_line holda_line;
|
||||||
|
devcb_write_line dbin_line;
|
||||||
};
|
};
|
||||||
|
|
||||||
#define TMS99xx_CONFIG(name) \
|
#define TMS99xx_CONFIG(name) \
|
||||||
@ -208,12 +209,12 @@ protected:
|
|||||||
// Data bus width. Needed for TMS9980.
|
// Data bus width. Needed for TMS9980.
|
||||||
int m_databus_width;
|
int m_databus_width;
|
||||||
|
|
||||||
// Needed for TMS9980
|
|
||||||
bool m_lowbyte;
|
|
||||||
|
|
||||||
// Check the READY line?
|
// Check the READY line?
|
||||||
bool m_check_ready;
|
bool m_check_ready;
|
||||||
|
|
||||||
|
// Phase of the memory access
|
||||||
|
int m_mem_phase;
|
||||||
|
|
||||||
// Max address
|
// Max address
|
||||||
const UINT16 m_prgaddr_mask;
|
const UINT16 m_prgaddr_mask;
|
||||||
const UINT16 m_cruaddr_mask;
|
const UINT16 m_cruaddr_mask;
|
||||||
@ -238,13 +239,22 @@ protected:
|
|||||||
// Get the value of the interrupt level lines
|
// Get the value of the interrupt level lines
|
||||||
devcb_resolved_read8 m_get_intlevel;
|
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:
|
private:
|
||||||
// Indicates if this is a byte-oriented command
|
// Indicates if this is a byte-oriented command
|
||||||
inline bool byte_operation();
|
inline bool byte_operation();
|
||||||
|
|
||||||
// Processor states
|
// Processor states
|
||||||
bool m_idle_state;
|
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_wait_state;
|
||||||
bool m_hold_state;
|
bool m_hold_state;
|
||||||
|
|
||||||
|
@ -147,56 +147,79 @@ int tms9980a_device::get_intlevel(int state)
|
|||||||
void tms9980a_device::mem_read()
|
void tms9980a_device::mem_read()
|
||||||
{
|
{
|
||||||
UINT8 value;
|
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);
|
value = m_prgspace->read_byte((m_address & m_prgaddr_mask) | 1);
|
||||||
m_current_value = m_current_value | (value & 0x00ff);
|
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);
|
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);
|
||||||
m_lowbyte = false;
|
break;
|
||||||
}
|
}
|
||||||
else
|
pulse_clock(1);
|
||||||
{
|
m_mem_phase = (m_mem_phase % 4) +1;
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void tms9980a_device::mem_write()
|
void tms9980a_device::mem_write()
|
||||||
{
|
{
|
||||||
if (m_lowbyte)
|
switch (m_mem_phase)
|
||||||
{
|
{
|
||||||
m_prgspace->write_byte((m_address & 0x3ffe) | 1, m_current_value & 0xff);
|
case 1:
|
||||||
if (VERBOSE>7) LOG("tms9980a: memory write low byte %04x <- %02x\n", (m_address & m_prgaddr_mask) | 1, m_current_value & 0xff);
|
m_pass = 4; // make the CPU visit this method once more
|
||||||
m_lowbyte = false;
|
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
|
pulse_clock(1);
|
||||||
{
|
m_mem_phase = (m_mem_phase % 4) +1;
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void tms9980a_device::acquire_instruction()
|
void tms9980a_device::acquire_instruction()
|
||||||
{
|
{
|
||||||
if (!m_lowbyte)
|
if (m_mem_phase == 1)
|
||||||
{
|
{
|
||||||
m_iaq_line(ASSERT_LINE);
|
m_iaq_line(ASSERT_LINE);
|
||||||
m_address = PC;
|
m_address = PC;
|
||||||
m_first_cycle = m_icount;
|
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);
|
decode(m_current_value);
|
||||||
if (VERBOSE>3) LOG("tms9980a: ===== Next operation %04x (%s) at %04x =====\n", IR, opname[m_command], PC);
|
if (VERBOSE>3) LOG("tms9980a: ===== Next operation %04x (%s) at %04x =====\n", IR, opname[m_command], PC);
|
||||||
debugger_instruction_hook(this, PC);
|
debugger_instruction_hook(this, PC);
|
||||||
@ -205,6 +228,8 @@ void tms9980a_device::acquire_instruction()
|
|||||||
// IAQ will be cleared in the main loop
|
// IAQ will be cleared in the main loop
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**************************************************************************/
|
/**************************************************************************/
|
||||||
UINT32 tms9980a_device::execute_min_cycles() const
|
UINT32 tms9980a_device::execute_min_cycles() const
|
||||||
{
|
{
|
||||||
|
@ -158,8 +158,8 @@ void tms9995_device::device_start()
|
|||||||
m_external_operation.resolve(conf->external_callback, *this);
|
m_external_operation.resolve(conf->external_callback, *this);
|
||||||
m_iaq_line.resolve(conf->iaq_line, *this);
|
m_iaq_line.resolve(conf->iaq_line, *this);
|
||||||
m_clock_out_line.resolve(conf->clock_out, *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_holda_line.resolve(conf->holda_line, *this);
|
||||||
|
m_dbin_line.resolve(conf->dbin_line, *this);
|
||||||
|
|
||||||
m_mp9537 = (conf->mode==NO_INTERNAL_RAM);
|
m_mp9537 = (conf->mode==NO_INTERNAL_RAM);
|
||||||
m_check_overflow = (conf->overflow==OVERFLOW_INT);
|
m_check_overflow = (conf->overflow==OVERFLOW_INT);
|
||||||
@ -1086,10 +1086,9 @@ void tms9995_device::execute_run()
|
|||||||
do
|
do
|
||||||
{
|
{
|
||||||
// Normal operation
|
// Normal operation
|
||||||
if (m_check_ready && m_ready_state == false)
|
if (m_check_ready && m_ready == false)
|
||||||
{
|
{
|
||||||
// We are in a wait state
|
// We are in a wait state
|
||||||
set_wait_state(true);
|
|
||||||
if (VERBOSE>2) LOG("tms9995: wait state\n");
|
if (VERBOSE>2) LOG("tms9995: wait state\n");
|
||||||
// The clock output should be used to change the state of an outer
|
// The clock output should be used to change the state of an outer
|
||||||
// device which operates the READY line
|
// device which operates the READY line
|
||||||
@ -1105,7 +1104,6 @@ void tms9995_device::execute_run()
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
set_wait_state(false);
|
|
||||||
set_hold_state(false);
|
set_hold_state(false);
|
||||||
|
|
||||||
m_check_ready = false;
|
m_check_ready = false;
|
||||||
@ -1189,9 +1187,15 @@ inline void tms9995_device::pulse_clock(int count)
|
|||||||
for (int i=0; i < count; i++)
|
for (int i=0; i < count; i++)
|
||||||
{
|
{
|
||||||
m_clock_out_line(ASSERT_LINE);
|
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_clock_out_line(CLEAR_LINE);
|
||||||
m_icount--; // This is the only location where we count down the cycles.
|
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();
|
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)
|
void tms9995_device::set_ready(int state)
|
||||||
{
|
{
|
||||||
if (VERBOSE>5) LOG("tms9995: set READY = %d\n", state);
|
m_ready_bufd = (state==ASSERT_LINE);
|
||||||
m_ready_state = (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
|
// 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,
|
// will be called a second time. Only when the lowbyte has been fetched,
|
||||||
// continue with the next step
|
// continue with the next step
|
||||||
if (!m_lowbyte) command_completed();
|
if (m_mem_phase==1) 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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -1307,56 +1303,55 @@ void tms9995_device::int_prefetch_and_decode()
|
|||||||
bool check_int = (m_instruction->command != XOP && m_instruction->command != BLWP);
|
bool check_int = (m_instruction->command != XOP && m_instruction->command != BLWP);
|
||||||
int intmask = ST & 0x000f;
|
int intmask = ST & 0x000f;
|
||||||
|
|
||||||
if (m_lowbyte)
|
if (m_mem_phase == 1)
|
||||||
{
|
{
|
||||||
prefetch_and_decode();
|
// Check interrupt lines
|
||||||
return;
|
if (m_nmi_active)
|
||||||
}
|
|
||||||
|
|
||||||
// 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)
|
|
||||||
{
|
{
|
||||||
if (m_idle_state)
|
if (VERBOSE>7) LOG("tms9995: Checking interrupts ... NMI active\n");
|
||||||
{
|
m_int_pending |= PENDING_NMI;
|
||||||
m_idle_state = false;
|
m_idle_state = false;
|
||||||
if (VERBOSE>7) LOG("tms9995: Interrupt occured, terminate IDLE state\n");
|
PC = (PC + 2) & 0xfffe; // we have not prefetched the next instruction
|
||||||
}
|
return;
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (VERBOSE>7) LOG("tms9995: Checking interrupts ... none pending\n");
|
m_int_pending = 0;
|
||||||
// No pending interrupts
|
|
||||||
if (check_idle && m_idle_state)
|
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");
|
if (m_idle_state)
|
||||||
// We are IDLE, stay in the loop and do not advance the PC
|
{
|
||||||
m_pass = 2;
|
m_idle_state = false;
|
||||||
pulse_clock(1);
|
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
|
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()
|
void tms9995_device::prefetch_and_decode()
|
||||||
{
|
{
|
||||||
if (m_lowbyte)
|
if (m_mem_phase==1)
|
||||||
{
|
|
||||||
// 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
|
|
||||||
{
|
{
|
||||||
// Fetch next instruction
|
// Fetch next instruction
|
||||||
// Save these values; they have been computed during the current instruction execution
|
// Save these values; they have been computed during the current instruction execution
|
||||||
m_address_copy = m_address;
|
m_address_copy = m_address;
|
||||||
m_value_copy = m_current_value;
|
m_value_copy = m_current_value;
|
||||||
|
|
||||||
m_iaq_line(ASSERT_LINE);
|
m_iaq_line(ASSERT_LINE);
|
||||||
|
|
||||||
m_address = PC;
|
m_address = PC;
|
||||||
|
|
||||||
if (VERBOSE>5) LOG("tms9995: **** Prefetching new instruction at %04x ****\n", PC);
|
if (VERBOSE>5) LOG("tms9995: **** Prefetching new instruction at %04x ****\n", PC);
|
||||||
|
}
|
||||||
|
|
||||||
m_lowbyte = false; // for mem_read
|
word_read();
|
||||||
word_read(); // this is where the clock pulses occur
|
|
||||||
|
|
||||||
if (!m_lowbyte)
|
if (m_mem_phase==1)
|
||||||
{
|
{
|
||||||
// Only if we got the word in one pass
|
// 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
|
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_address = m_address_copy; // restore m_address
|
m_current_value = m_value_copy; // restore m_current_value
|
||||||
m_current_value = m_value_copy; // restore m_current_value
|
PC = (PC + 2) & 0xfffe; // advance PC
|
||||||
PC = (PC + 2) & 0xfffe; // advance PC
|
m_iaq_line(CLEAR_LINE);
|
||||||
|
if (VERBOSE>5) LOG("tms9995: ++++ Prefetch done ++++\n");
|
||||||
m_iaq_line(CLEAR_LINE);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1447,10 +1424,11 @@ void tms9995_device::command_completed()
|
|||||||
// Pseudo state at the end of the current instruction cycle sequence
|
// Pseudo state at the end of the current instruction cycle sequence
|
||||||
if (VERBOSE>4)
|
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;
|
int cycles = m_first_cycle - m_icount;
|
||||||
// Avoid nonsense values due to expired and resumed main loop
|
// 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)
|
if (m_int_pending != 0)
|
||||||
@ -1484,8 +1462,7 @@ void tms9995_device::service_interrupt()
|
|||||||
|
|
||||||
m_nmi_state = false;
|
m_nmi_state = false;
|
||||||
m_hold_state = false;
|
m_hold_state = false;
|
||||||
m_wait_state = false;
|
m_mem_phase = 1;
|
||||||
m_lowbyte = false;
|
|
||||||
m_check_hold = false;
|
m_check_hold = false;
|
||||||
m_word_access = false;
|
m_word_access = false;
|
||||||
m_int4_active = 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
|
// The auto-wait state generation is turned on when the READY line is cleared
|
||||||
// on RESET.
|
// on RESET.
|
||||||
m_auto_wait_state = !m_ready_state;
|
m_auto_wait = !m_ready_bufd;
|
||||||
if (VERBOSE>0) LOG("tms9995: RESET; automatic wait state creation is %s\n", m_auto_wait_state? "enabled":"disabled");
|
if (VERBOSE>0) LOG("tms9995: RESET; automatic wait state creation is %s\n", m_auto_wait? "enabled":"disabled");
|
||||||
m_ready_state = true;
|
// We reset the READY flag, or the CPU will not start
|
||||||
|
m_ready_bufd = true;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -1574,6 +1552,7 @@ void tms9995_device::service_interrupt()
|
|||||||
m_instruction->byteop = false;
|
m_instruction->byteop = false;
|
||||||
m_instruction->command = INTR;
|
m_instruction->command = INTR;
|
||||||
m_pass = m_reset? 1 : 2;
|
m_pass = m_reset? 1 : 2;
|
||||||
|
m_from_reset = m_reset;
|
||||||
|
|
||||||
if (m_reset)
|
if (m_reset)
|
||||||
{
|
{
|
||||||
@ -1604,7 +1583,7 @@ void tms9995_device::service_interrupt()
|
|||||||
void tms9995_device::mem_read()
|
void tms9995_device::mem_read()
|
||||||
{
|
{
|
||||||
// First determine whether the memory is inside the CPU
|
// 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)
|
// FFFC-FFFF = NMI vector (on-chip)
|
||||||
// There is a variant of the TMS9995 with no on-chip RAM which was used
|
// There is a variant of the TMS9995 with no on-chip RAM which was used
|
||||||
// for the TI-99/8 (9537).
|
// for the TI-99/8 (9537).
|
||||||
@ -1641,42 +1620,55 @@ void tms9995_device::mem_read()
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// This is a off-chip access
|
// This is an off-chip access
|
||||||
m_check_ready = true;
|
m_check_ready = true;
|
||||||
if (m_lowbyte)
|
UINT8 value;
|
||||||
|
UINT16 address = m_address;
|
||||||
|
|
||||||
|
switch (m_mem_phase)
|
||||||
{
|
{
|
||||||
// This is always the odd address
|
case 1:
|
||||||
// With the OR we can ensure that we do not skip to an even address
|
// Set address
|
||||||
// when we try to read a word from an odd address
|
// If this is a word access, 4 passes, else 2 passes
|
||||||
m_current_value |= m_prgspace->read_byte(m_address | 0x0001);
|
m_dbin_line(ASSERT_LINE);
|
||||||
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;
|
|
||||||
if (m_word_access || !m_instruction->byteop)
|
if (m_word_access || !m_instruction->byteop)
|
||||||
{
|
{
|
||||||
// We have to come here a second time; do not advance the MPC
|
m_pass = 4;
|
||||||
// if the address value is even
|
// For word accesses, we always start at the even address
|
||||||
m_lowbyte = true;
|
|
||||||
m_pass = 2;
|
|
||||||
address &= 0xfffe;
|
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)
|
|
||||||
{
|
m_mem_phase = (m_mem_phase % 4) +1;
|
||||||
if (VERBOSE>7) LOG("tms9995: Next pulse is auto wait\n");
|
|
||||||
pulse_clock(1);
|
// Reset to 1 when we are done
|
||||||
}
|
if (m_pass==1) m_mem_phase = 1;
|
||||||
}
|
}
|
||||||
pulse_clock(1);
|
pulse_clock(1);
|
||||||
}
|
}
|
||||||
@ -1738,7 +1730,6 @@ void tms9995_device::mem_write()
|
|||||||
pulse_clock(1);
|
pulse_clock(1);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool onchip = (((m_address & 0xff00)==0xf000 && (m_address < 0xf0fc)) || ((m_address & 0xfffc)==0xfffc)) && !m_mp9537;
|
bool onchip = (((m_address & 0xff00)==0xf000 && (m_address < 0xf0fc)) || ((m_address & 0xfffc)==0xfffc)) && !m_mp9537;
|
||||||
|
|
||||||
if (onchip)
|
if (onchip)
|
||||||
@ -1753,41 +1744,50 @@ void tms9995_device::mem_write()
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
// This is an off-chip access
|
||||||
m_check_ready = true;
|
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)
|
if (m_word_access || !m_instruction->byteop)
|
||||||
{
|
{
|
||||||
// We have to come here a second time; do not advance the MPC
|
m_pass = 4;
|
||||||
// if the address value is even
|
|
||||||
m_lowbyte = true;
|
|
||||||
m_pass = 2;
|
|
||||||
address &= 0xfffe;
|
address &= 0xfffe;
|
||||||
m_check_hold = false;
|
|
||||||
}
|
}
|
||||||
if (VERBOSE>3)
|
else m_pass = 2;
|
||||||
{
|
|
||||||
if (m_pass==2) LOG("tms9995: write external memory, first pass (address %04x, value %02x)\n", address, (m_current_value>>8)&0xff);
|
m_check_hold = false;
|
||||||
else LOG("tms9995: write external memory (single pass), address %04x, value=%02x\n", address, (m_current_value>>8)&0xff);
|
if (VERBOSE>7) LOG("tms9995: set address bus %04x\n", address);
|
||||||
}
|
m_prgspace->set_address(address);
|
||||||
m_prgspace->write_byte(address, (m_current_value >> 8)& 0xff);
|
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)
|
m_mem_phase = (m_mem_phase % 4) +1;
|
||||||
{
|
|
||||||
if (VERBOSE>7) LOG("tms9995: Next pulse is auto wait\n");
|
// Reset to 1 when we are done
|
||||||
pulse_clock(1);
|
if (m_pass==1) m_mem_phase = 1;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
pulse_clock(1);
|
pulse_clock(1);
|
||||||
}
|
}
|
||||||
@ -2072,7 +2072,7 @@ void tms9995_device::operand_address_subprogram()
|
|||||||
}
|
}
|
||||||
|
|
||||||
m_get_destination = true;
|
m_get_destination = true;
|
||||||
m_lowbyte = false;
|
m_mem_phase = 1;
|
||||||
m_address_add = 0;
|
m_address_add = 0;
|
||||||
MPC--; // will be increased in the mail loop
|
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);
|
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_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_current_value += m_instruction->byteop? 1 : 2;
|
||||||
m_address = (WP + (m_regnumber<<1)) & 0xffff;
|
m_address = (WP + (m_regnumber<<1)) & 0xffff;
|
||||||
m_lowbyte = false;
|
m_mem_phase = 1;
|
||||||
pulse_clock(1);
|
pulse_clock(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2101,7 +2101,7 @@ void tms9995_device::indexed_addressing()
|
|||||||
m_address_add = m_current_value;
|
m_address_add = m_current_value;
|
||||||
m_address = PC;
|
m_address = PC;
|
||||||
PC = (PC + 2) & 0xfffe;
|
PC = (PC + 2) & 0xfffe;
|
||||||
m_lowbyte = false;
|
m_mem_phase = 1;
|
||||||
pulse_clock(1);
|
pulse_clock(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2112,7 +2112,7 @@ void tms9995_device::set_immediate()
|
|||||||
m_address = PC;
|
m_address = PC;
|
||||||
m_source_value = m_current_value; // needed for AI, ANDI, ORI
|
m_source_value = m_current_value; // needed for AI, ANDI, ORI
|
||||||
PC = (PC + 2) & 0xfffe;
|
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 (((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_int_pending &= ~PENDING_MID;
|
||||||
m_address = 0xfffc;
|
m_address = 0xfffc;
|
||||||
m_intmask = 0;
|
m_intmask = 0;
|
||||||
@ -3235,11 +3235,11 @@ void tms9995_device::alu_int()
|
|||||||
}
|
}
|
||||||
else
|
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
|
// We came from the RESET interrupt
|
||||||
m_reset = false;
|
m_from_reset = false;
|
||||||
ST &= 0x01ff;
|
ST &= 0x01ff;
|
||||||
m_mid_flag = false;
|
m_mid_flag = false;
|
||||||
// FLAG0 and FLAG1 are also set to zero after RESET ([1], sect. 2.3.1.2.2)
|
// FLAG0 and FLAG1 are also set to zero after RESET ([1], sect. 2.3.1.2.2)
|
||||||
|
@ -65,8 +65,8 @@ struct tms9995_config
|
|||||||
devcb_write8 external_callback;
|
devcb_write8 external_callback;
|
||||||
devcb_write_line iaq_line;
|
devcb_write_line iaq_line;
|
||||||
devcb_write_line clock_out;
|
devcb_write_line clock_out;
|
||||||
devcb_write_line wait_line;
|
|
||||||
devcb_write_line holda_line;
|
devcb_write_line holda_line;
|
||||||
|
devcb_write_line dbin_line;
|
||||||
int mode;
|
int mode;
|
||||||
int overflow;
|
int overflow;
|
||||||
};
|
};
|
||||||
@ -151,18 +151,23 @@ private:
|
|||||||
bool m_idle_state;
|
bool m_idle_state;
|
||||||
bool m_nmi_state;
|
bool m_nmi_state;
|
||||||
bool m_irq_state;
|
bool m_irq_state;
|
||||||
bool m_ready_state;
|
|
||||||
bool m_wait_state;
|
|
||||||
bool m_hold_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
|
// Auto-wait state generation
|
||||||
bool m_auto_wait_state;
|
bool m_request_auto_wait_state;
|
||||||
|
bool m_auto_wait;
|
||||||
|
|
||||||
// Cycle counter
|
// Cycle counter
|
||||||
int m_icount;
|
int m_icount;
|
||||||
|
|
||||||
// The next memory access will address the low byte
|
// Phase of the memory access
|
||||||
bool m_lowbyte;
|
int m_mem_phase;
|
||||||
|
|
||||||
// Check the READY line?
|
// Check the READY line?
|
||||||
bool m_check_ready;
|
bool m_check_ready;
|
||||||
@ -195,6 +200,7 @@ private:
|
|||||||
bool m_int_overflow;
|
bool m_int_overflow;
|
||||||
|
|
||||||
bool m_reset;
|
bool m_reset;
|
||||||
|
bool m_from_reset;
|
||||||
bool m_mid_flag;
|
bool m_mid_flag;
|
||||||
|
|
||||||
// Flag field
|
// Flag field
|
||||||
@ -213,10 +219,7 @@ private:
|
|||||||
// Issue clock pulses. The TMS9995 uses one (output) clock cycle per machine cycle.
|
// Issue clock pulses. The TMS9995 uses one (output) clock cycle per machine cycle.
|
||||||
inline void pulse_clock(int count);
|
inline void pulse_clock(int count);
|
||||||
|
|
||||||
// Signal the wait state via the external line
|
// Signal the hold state via the external line
|
||||||
inline void set_wait_state(bool state);
|
|
||||||
|
|
||||||
// Signal the wait state via the external line
|
|
||||||
inline void set_hold_state(bool state);
|
inline void set_hold_state(bool state);
|
||||||
|
|
||||||
// Only used for the DIV(S) operations. It seems sufficient to let the
|
// Only used for the DIV(S) operations. It seems sufficient to let the
|
||||||
@ -434,11 +437,11 @@ private:
|
|||||||
// Clock output.
|
// Clock output.
|
||||||
devcb_resolved_write_line m_clock_out_line;
|
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
|
// Asserted when the CPU is in a HOLD state
|
||||||
devcb_resolved_write_line m_holda_line;
|
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
|
// device type definition
|
||||||
|
Loading…
Reference in New Issue
Block a user