mirror of
https://github.com/holub/mame
synced 2025-05-10 16:21:42 +03:00
749 lines
16 KiB
C++
749 lines
16 KiB
C++
// license:BSD-3-Clause
|
|
// copyright-holders:Wilbert Pol, hap
|
|
/*
|
|
|
|
TMS1000 family - base/shared
|
|
|
|
TODO:
|
|
- accurate INIT pin (currently, just use INPUT_LINE_RESET)
|
|
|
|
|
|
The TMS0980 and TMS1000-family MCU cores are very similar. The TMS0980 has a
|
|
slightly bigger addressable area and uses 9bit instructions where the TMS1000
|
|
family uses 8bit instruction. The instruction set themselves are very similar
|
|
though.
|
|
|
|
Each instruction takes 12 cycles to execute in 2 phases: a fetch phase and an
|
|
execution phase. The execution phase takes place at the same time as the fetch
|
|
phase of the next instruction. So, during execution there are both fetch and
|
|
execution operations taking place. The operation can be split up as follows:
|
|
cycle #0
|
|
- Fetch:
|
|
1. ROM address 0
|
|
- Execute:
|
|
1. Read RAM
|
|
2. Clear ALU inputs
|
|
3. Execute BRANCH/CALL/RETN part #2
|
|
4. K input valid
|
|
cycle #1
|
|
- Fetch:
|
|
1. ROM address 1
|
|
- Execute:
|
|
1. Update ALU inputs
|
|
cycle #2
|
|
- Fetch:
|
|
1. nothing/wait(?)
|
|
- Execute:
|
|
1. Perform ALU operation
|
|
2. Write RAM
|
|
cycle #3
|
|
- Fetch:
|
|
1. Fetch/Update PC/RAM address #1
|
|
- Execute:
|
|
1. Register store part #1
|
|
cycle #4
|
|
- Fetch:
|
|
1. Fetch/Update PC/RAM address #2
|
|
- Execute:
|
|
1. Register store part #2
|
|
cycle #5
|
|
- Fetch:
|
|
1. Instruction decode
|
|
- Execute:
|
|
1. Execute BRANCH/CALL/RETN part #1
|
|
|
|
The MCU cores contains a set of fixed instructions and a set of
|
|
instructions created using microinstructions. A subset of the
|
|
instruction set could be defined from the microinstructions by
|
|
TI customers.
|
|
|
|
cycle #0: 15TN, ATN, CIN, CKN, CKP, DMTP, MTN, MTP, NATN, NDMTP, YTP
|
|
cycle #2: C8(?), CKM, NE(?), STO
|
|
cycle #3,#4: AUTA, AUTY
|
|
|
|
unknown cycle: CME, SSE, SSS
|
|
|
|
*/
|
|
|
|
#include "emu.h"
|
|
#include "tms1k_base.h"
|
|
#include "debugger.h"
|
|
|
|
tms1k_base_device::tms1k_base_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, u32 clock, u8 o_pins, u8 r_pins, u8 pc_bits, u8 byte_bits, u8 x_bits, int prgwidth, address_map_constructor program, int datawidth, address_map_constructor data)
|
|
: cpu_device(mconfig, type, tag, owner, clock)
|
|
, m_program_config("program", ENDIANNESS_BIG, byte_bits > 8 ? 16 : 8, prgwidth, byte_bits > 8 ? -1 : 0, program)
|
|
, m_data_config("data", ENDIANNESS_BIG, 8, datawidth, 0, data)
|
|
, m_mpla(*this, "mpla")
|
|
, m_ipla(*this, "ipla")
|
|
, m_opla(*this, "opla")
|
|
, m_spla(*this, "spla")
|
|
, m_o_pins(o_pins)
|
|
, m_r_pins(r_pins)
|
|
, m_pc_bits(pc_bits)
|
|
, m_byte_bits(byte_bits)
|
|
, m_x_bits(x_bits)
|
|
, m_output_pla_table(nullptr)
|
|
, m_read_k(*this)
|
|
, m_write_o(*this)
|
|
, m_write_r(*this)
|
|
, m_power_off(*this)
|
|
{
|
|
}
|
|
|
|
// disasm
|
|
void tms1k_base_device::state_string_export(const device_state_entry &entry, std::string &str) const
|
|
{
|
|
switch (entry.index())
|
|
{
|
|
case STATE_GENPC:
|
|
case STATE_GENPCBASE:
|
|
str = string_format("%03X", m_rom_address << ((m_byte_bits > 8) ? 1 : 0));
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
//-------------------------------------------------
|
|
// device_start - device-specific startup
|
|
//-------------------------------------------------
|
|
|
|
enum
|
|
{
|
|
TMS1XXX_PC=1, TMS1XXX_SR, TMS1XXX_PA, TMS1XXX_PB,
|
|
TMS1XXX_A, TMS1XXX_X, TMS1XXX_Y, TMS1XXX_STATUS
|
|
};
|
|
|
|
void tms1k_base_device::device_start()
|
|
{
|
|
m_program = &space(AS_PROGRAM);
|
|
m_data = &space(AS_DATA);
|
|
|
|
m_o_mask = (1 << m_o_pins) - 1;
|
|
m_r_mask = (1 << m_r_pins) - 1;
|
|
m_pc_mask = (1 << m_pc_bits) - 1;
|
|
m_x_mask = (1 << m_x_bits) - 1;
|
|
|
|
// resolve callbacks
|
|
m_read_k.resolve_safe(0);
|
|
m_write_o.resolve_safe();
|
|
m_write_r.resolve_safe();
|
|
m_power_off.resolve_safe();
|
|
|
|
// zerofill
|
|
m_pc = 0;
|
|
m_sr = 0;
|
|
m_pa = 0;
|
|
m_pb = 0;
|
|
m_ps = 0;
|
|
m_a = 0;
|
|
m_x = 0;
|
|
m_y = 0;
|
|
m_ca = 0;
|
|
m_cb = 0;
|
|
m_cs = 0;
|
|
m_r = 0;
|
|
m_o = 0;
|
|
m_o_index = 0;
|
|
m_halt = false;
|
|
m_cki_bus = 0;
|
|
m_c4 = 0;
|
|
m_p = 0;
|
|
m_n = 0;
|
|
m_adder_out = 0;
|
|
m_carry_in = 0;
|
|
m_carry_out = 0;
|
|
m_status = 0;
|
|
m_status_latch = 0;
|
|
m_eac = 0;
|
|
m_clatch = 0;
|
|
m_add = 0;
|
|
m_bl = 0;
|
|
|
|
m_ram_in = 0;
|
|
m_dam_in = 0;
|
|
m_ram_out = 0;
|
|
m_ram_address = 0;
|
|
m_rom_address = 0;
|
|
m_opcode = 0;
|
|
m_fixed = 0;
|
|
m_micro = 0;
|
|
m_subcycle = 0;
|
|
|
|
// register for savestates
|
|
save_item(NAME(m_pc));
|
|
save_item(NAME(m_sr));
|
|
save_item(NAME(m_pa));
|
|
save_item(NAME(m_pb));
|
|
save_item(NAME(m_ps));
|
|
save_item(NAME(m_a));
|
|
save_item(NAME(m_x));
|
|
save_item(NAME(m_y));
|
|
save_item(NAME(m_ca));
|
|
save_item(NAME(m_cb));
|
|
save_item(NAME(m_cs));
|
|
save_item(NAME(m_r));
|
|
save_item(NAME(m_o));
|
|
save_item(NAME(m_o_index));
|
|
save_item(NAME(m_halt));
|
|
save_item(NAME(m_cki_bus));
|
|
save_item(NAME(m_c4));
|
|
save_item(NAME(m_p));
|
|
save_item(NAME(m_n));
|
|
save_item(NAME(m_adder_out));
|
|
save_item(NAME(m_carry_in));
|
|
save_item(NAME(m_carry_out));
|
|
save_item(NAME(m_status));
|
|
save_item(NAME(m_status_latch));
|
|
save_item(NAME(m_eac));
|
|
save_item(NAME(m_clatch));
|
|
save_item(NAME(m_add));
|
|
save_item(NAME(m_bl));
|
|
|
|
save_item(NAME(m_ram_in));
|
|
save_item(NAME(m_dam_in));
|
|
save_item(NAME(m_ram_out));
|
|
save_item(NAME(m_ram_address));
|
|
save_item(NAME(m_rom_address));
|
|
save_item(NAME(m_opcode));
|
|
save_item(NAME(m_fixed));
|
|
save_item(NAME(m_micro));
|
|
save_item(NAME(m_subcycle));
|
|
|
|
// register state for debugger
|
|
state_add(TMS1XXX_PC, "PC", m_pc ).formatstr("%02X");
|
|
state_add(TMS1XXX_SR, "SR", m_sr ).formatstr("%01X");
|
|
state_add(TMS1XXX_PA, "PA", m_pa ).formatstr("%01X");
|
|
state_add(TMS1XXX_PB, "PB", m_pb ).formatstr("%01X");
|
|
state_add(TMS1XXX_A, "A", m_a ).formatstr("%01X");
|
|
state_add(TMS1XXX_X, "X", m_x ).formatstr("%01X");
|
|
state_add(TMS1XXX_Y, "Y", m_y ).formatstr("%01X");
|
|
state_add(TMS1XXX_STATUS, "STATUS", m_status).formatstr("%01X");
|
|
|
|
state_add(STATE_GENPC, "GENPC", m_rom_address).formatstr("%03X").noshow();
|
|
state_add(STATE_GENPCBASE, "CURPC", m_rom_address).formatstr("%03X").noshow();
|
|
state_add(STATE_GENFLAGS, "GENFLAGS", m_sr).formatstr("%8s").noshow();
|
|
|
|
m_icountptr = &m_icount;
|
|
}
|
|
|
|
device_memory_interface::space_config_vector tms1k_base_device::memory_space_config() const
|
|
{
|
|
return space_config_vector {
|
|
std::make_pair(AS_PROGRAM, &m_program_config),
|
|
std::make_pair(AS_DATA, &m_data_config)
|
|
};
|
|
}
|
|
|
|
|
|
|
|
//-------------------------------------------------
|
|
// device_reset - device-specific reset
|
|
//-------------------------------------------------
|
|
|
|
void tms1k_base_device::device_reset()
|
|
{
|
|
m_pa = 0xf;
|
|
m_pb = 0xf;
|
|
m_pc = 0;
|
|
m_ca = 0;
|
|
m_cb = 0;
|
|
m_cs = 0;
|
|
|
|
m_eac = 0;
|
|
m_bl = 0;
|
|
m_add = 0;
|
|
m_status = 0;
|
|
m_clatch = 0;
|
|
|
|
m_opcode = 0;
|
|
m_micro = 0;
|
|
m_fixed = 0;
|
|
|
|
m_subcycle = 0;
|
|
|
|
// clear outputs
|
|
m_r = 0;
|
|
m_write_r(0, m_r & m_r_mask, 0xffff);
|
|
write_o_output(0);
|
|
m_write_r(0, m_r & m_r_mask, 0xffff);
|
|
m_power_off(0);
|
|
}
|
|
|
|
|
|
|
|
//-------------------------------------------------
|
|
// program counter/opcode decode
|
|
//-------------------------------------------------
|
|
|
|
void tms1k_base_device::next_pc()
|
|
{
|
|
// The program counter is a LFSR. To put it simply, the feedback bit is a XOR of the two highest bits,
|
|
// but it makes an exception when all low bits are set (eg. in TMS1000 case, when PC is 0x1f or 0x3f).
|
|
int high = 1 << (m_pc_bits - 1);
|
|
int fb = (m_pc << 1 & high) == (m_pc & high);
|
|
|
|
if (m_pc == (m_pc_mask >> 1))
|
|
fb = 1;
|
|
else if (m_pc == m_pc_mask)
|
|
fb = 0;
|
|
|
|
m_pc = (m_pc << 1 | fb) & m_pc_mask;
|
|
}
|
|
|
|
void tms1k_base_device::read_opcode()
|
|
{
|
|
debugger_instruction_hook(this, m_rom_address);
|
|
m_opcode = m_program->read_byte(m_rom_address);
|
|
m_c4 = bitswap<8>(m_opcode,7,6,5,4,0,1,2,3) & 0xf; // opcode operand is bitswapped for most opcodes
|
|
|
|
m_fixed = m_fixed_decode[m_opcode];
|
|
m_micro = m_micro_decode[m_opcode];
|
|
|
|
next_pc();
|
|
}
|
|
|
|
|
|
|
|
//-------------------------------------------------
|
|
// i/o handling
|
|
//-------------------------------------------------
|
|
|
|
void tms1k_base_device::execute_set_input(int line, int state)
|
|
{
|
|
if (line != TMS1XXX_INPUT_LINE_HALT)
|
|
return;
|
|
|
|
// HALT pin (CMOS only)
|
|
m_halt = bool(state);
|
|
}
|
|
|
|
void tms1k_base_device::write_o_output(u8 index)
|
|
{
|
|
// a hardcoded table is supported if the output pla is unknown
|
|
m_o_index = index;
|
|
m_o = (m_output_pla_table == nullptr) ? m_opla->read(index) : m_output_pla_table[index];
|
|
m_write_o(0, m_o & m_o_mask, 0xffff);
|
|
}
|
|
|
|
u8 tms1k_base_device::read_k_input()
|
|
{
|
|
// K1,2,4,8 (KC test pin is not emulated)
|
|
return m_read_k(0, 0xff) & 0xf;
|
|
}
|
|
|
|
void tms1k_base_device::set_cki_bus()
|
|
{
|
|
switch (m_opcode & 0xf8)
|
|
{
|
|
// 00001XXX: K-inputs
|
|
case 0x08:
|
|
m_cki_bus = read_k_input();
|
|
break;
|
|
|
|
// 0011XXXX: select bit
|
|
case 0x30: case 0x38:
|
|
m_cki_bus = 1 << (m_c4 >> 2) ^ 0xf;
|
|
break;
|
|
|
|
// 01XXXXXX: constant
|
|
case 0x00: // R2,3,4 are NANDed with eachother, and then ORed with R1, making 00000XXX valid too
|
|
case 0x40: case 0x48: case 0x50: case 0x58: case 0x60: case 0x68: case 0x70: case 0x78:
|
|
m_cki_bus = m_c4;
|
|
break;
|
|
|
|
default:
|
|
m_cki_bus = 0;
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
//-------------------------------------------------
|
|
// fixed opcode set
|
|
//-------------------------------------------------
|
|
|
|
// handle branches:
|
|
|
|
// TMS1000/common
|
|
// note: add(latch) and bl(branch latch) are specific to 0980 series,
|
|
// c(chapter) bits are specific to 1100(and 1400) series
|
|
|
|
void tms1k_base_device::op_br()
|
|
{
|
|
// BR/BL: conditional branch
|
|
if (m_status)
|
|
{
|
|
if (m_clatch == 0)
|
|
m_pa = m_pb;
|
|
m_ca = m_cb;
|
|
m_pc = m_opcode & m_pc_mask;
|
|
}
|
|
}
|
|
|
|
void tms1k_base_device::op_call()
|
|
{
|
|
// CALL/CALLL: conditional call
|
|
if (m_status)
|
|
{
|
|
u8 prev_pa = m_pa;
|
|
|
|
if (m_clatch == 0)
|
|
{
|
|
m_clatch = 1;
|
|
m_sr = m_pc;
|
|
m_pa = m_pb;
|
|
m_cs = m_ca;
|
|
}
|
|
m_ca = m_cb;
|
|
m_pb = prev_pa;
|
|
m_pc = m_opcode & m_pc_mask;
|
|
}
|
|
}
|
|
|
|
void tms1k_base_device::op_retn()
|
|
{
|
|
// RETN: return from subroutine
|
|
if (m_clatch == 1)
|
|
{
|
|
m_clatch = 0;
|
|
m_pc = m_sr;
|
|
m_ca = m_cs;
|
|
}
|
|
m_add = 0;
|
|
m_bl = 0;
|
|
m_pa = m_pb;
|
|
}
|
|
|
|
|
|
// TMS1400/TMS1000C 3-level stack version
|
|
|
|
void tms1k_base_device::op_br3()
|
|
{
|
|
// BR/BL: conditional branch
|
|
if (m_status)
|
|
{
|
|
m_pa = m_pb; // don't care about clatch
|
|
m_ca = m_cb;
|
|
m_pc = m_opcode & m_pc_mask;
|
|
}
|
|
}
|
|
|
|
void tms1k_base_device::op_call3()
|
|
{
|
|
// CALL/CALLL: conditional call
|
|
if (m_status)
|
|
{
|
|
// mask clatch 3 bits (no need to mask others)
|
|
m_clatch = (m_clatch << 1 | 1) & 7;
|
|
|
|
m_sr = m_sr << m_pc_bits | m_pc;
|
|
m_pc = m_opcode & m_pc_mask;
|
|
|
|
m_ps = m_ps << 4 | m_pa;
|
|
m_pa = m_pb;
|
|
|
|
m_cs = m_cs << 2 | m_ca;
|
|
m_ca = m_cb;
|
|
}
|
|
else
|
|
{
|
|
m_pb = m_pa;
|
|
m_cb = m_ca;
|
|
}
|
|
}
|
|
|
|
void tms1k_base_device::op_retn3()
|
|
{
|
|
// RETN: return from subroutine
|
|
if (m_clatch & 1)
|
|
{
|
|
m_clatch >>= 1;
|
|
|
|
m_pc = m_sr & m_pc_mask;
|
|
m_sr >>= m_pc_bits;
|
|
|
|
m_pa = m_pb = m_ps & 0xf;
|
|
m_ps >>= 4;
|
|
|
|
m_ca = m_cb = m_cs & 3;
|
|
m_cs >>= 2;
|
|
}
|
|
}
|
|
|
|
|
|
// handle other:
|
|
|
|
// TMS1000/common
|
|
|
|
void tms1k_base_device::op_sbit()
|
|
{
|
|
// SBIT: set memory bit
|
|
if (m_ram_out == -1)
|
|
m_ram_out = m_ram_in;
|
|
m_ram_out |= (m_cki_bus ^ 0xf);
|
|
}
|
|
|
|
void tms1k_base_device::op_rbit()
|
|
{
|
|
// RBIT: reset memory bit
|
|
if (m_ram_out == -1)
|
|
m_ram_out = m_ram_in;
|
|
m_ram_out &= m_cki_bus;
|
|
}
|
|
|
|
void tms1k_base_device::op_setr()
|
|
{
|
|
// SETR: set one R-output line
|
|
m_r = m_r | (1 << m_y);
|
|
m_write_r(0, m_r & m_r_mask, 0xffff);
|
|
}
|
|
|
|
void tms1k_base_device::op_rstr()
|
|
{
|
|
// RSTR: reset one R-output line
|
|
m_r = m_r & ~(1 << m_y);
|
|
m_write_r(0, m_r & m_r_mask, 0xffff);
|
|
}
|
|
|
|
void tms1k_base_device::op_tdo()
|
|
{
|
|
// TDO: transfer accumulator and status latch to O-output
|
|
write_o_output(m_status_latch << 4 | m_a);
|
|
}
|
|
|
|
void tms1k_base_device::op_clo()
|
|
{
|
|
// CLO: clear O-output
|
|
write_o_output(0);
|
|
}
|
|
|
|
void tms1k_base_device::op_ldx()
|
|
{
|
|
// LDX: load X register with (x_bits) constant
|
|
m_x = m_c4 >> (4-m_x_bits);
|
|
}
|
|
|
|
void tms1k_base_device::op_comx()
|
|
{
|
|
// COMX: complement X register
|
|
m_x ^= m_x_mask;
|
|
}
|
|
|
|
void tms1k_base_device::op_comx8()
|
|
{
|
|
// COMX8: complement MSB of X register
|
|
// note: on TMS1100, the mnemonic is simply called "COMX"
|
|
m_x ^= 1 << (m_x_bits-1);
|
|
}
|
|
|
|
void tms1k_base_device::op_ldp()
|
|
{
|
|
// LDP: load page buffer with constant
|
|
m_pb = m_c4;
|
|
}
|
|
|
|
|
|
// TMS1100-specific
|
|
|
|
void tms1k_base_device::op_comc()
|
|
{
|
|
// COMC: complement chapter buffer
|
|
m_cb ^= 1;
|
|
}
|
|
|
|
|
|
// TMS1400-specific
|
|
|
|
void tms1k_base_device::op_tpc()
|
|
{
|
|
// TPC: transfer page buffer to chapter buffer
|
|
m_cb = m_pb & 3;
|
|
}
|
|
|
|
|
|
// TMS0980-specific (and possibly child classes)
|
|
|
|
void tms1k_base_device::op_xda()
|
|
{
|
|
// XDA: exchange DAM and A
|
|
// note: setting A to DAM is done with DMTP and AUTA during this instruction
|
|
m_ram_address |= (0x10 << (m_x_bits-1));
|
|
}
|
|
|
|
void tms1k_base_device::op_off()
|
|
{
|
|
// OFF: request auto power-off
|
|
m_power_off(1);
|
|
}
|
|
|
|
void tms1k_base_device::op_seac()
|
|
{
|
|
// SEAC: set end around carry
|
|
m_eac = 1;
|
|
}
|
|
|
|
void tms1k_base_device::op_reac()
|
|
{
|
|
// REAC: reset end around carry
|
|
m_eac = 0;
|
|
}
|
|
|
|
void tms1k_base_device::op_sal()
|
|
{
|
|
// SAL: set add latch (reset is done with RETN)
|
|
m_add = 1;
|
|
}
|
|
|
|
void tms1k_base_device::op_sbl()
|
|
{
|
|
// SBL: set branch latch (reset is done with RETN)
|
|
m_bl = 1;
|
|
}
|
|
|
|
|
|
|
|
//-------------------------------------------------
|
|
// execute_run
|
|
//-------------------------------------------------
|
|
|
|
void tms1k_base_device::execute_run()
|
|
{
|
|
while (m_icount > 0)
|
|
{
|
|
m_icount--;
|
|
|
|
if (m_halt)
|
|
{
|
|
// not running (output pins remain unchanged)
|
|
m_icount = 0;
|
|
return;
|
|
}
|
|
|
|
switch (m_subcycle)
|
|
{
|
|
case 0:
|
|
// fetch: rom address 1/2
|
|
|
|
// execute: br/call 2/2
|
|
if (m_fixed & F_BR) op_br();
|
|
if (m_fixed & F_CALL) op_call();
|
|
if (m_fixed & F_RETN) op_retn();
|
|
|
|
// execute: k input valid, read ram, clear alu inputs
|
|
dynamic_output();
|
|
set_cki_bus();
|
|
m_ram_in = m_data->read_byte(m_ram_address) & 0xf;
|
|
m_dam_in = m_data->read_byte(m_ram_address | (0x10 << (m_x_bits-1))) & 0xf;
|
|
m_p = 0;
|
|
m_n = 0;
|
|
m_carry_in = 0;
|
|
|
|
break;
|
|
|
|
case 1:
|
|
// fetch: rom address 2/2
|
|
m_rom_address = (m_ca << (m_pc_bits+4)) | (m_pa << m_pc_bits) | m_pc;
|
|
|
|
// execute: update alu inputs
|
|
// N inputs
|
|
if (m_micro & M_15TN) m_n |= 0xf;
|
|
if (m_micro & M_ATN) m_n |= m_a;
|
|
if (m_micro & M_NATN) m_n |= (~m_a & 0xf);
|
|
if (m_micro & M_CKN) m_n |= m_cki_bus;
|
|
if (m_micro & M_MTN) m_n |= m_ram_in;
|
|
|
|
// P inputs
|
|
if (m_micro & M_CKP) m_p |= m_cki_bus;
|
|
if (m_micro & M_MTP) m_p |= m_ram_in;
|
|
if (m_micro & M_YTP) m_p |= m_y;
|
|
if (m_micro & M_DMTP) m_p |= m_dam_in;
|
|
if (m_micro & M_NDMTP) m_p |= (~m_dam_in & 0xf);
|
|
|
|
// carry input
|
|
if (m_micro & M_CIN) m_carry_in |= 1;
|
|
if (m_micro & M_SSS) m_carry_in |= m_eac;
|
|
|
|
break;
|
|
|
|
case 2:
|
|
{
|
|
// fetch: nothing
|
|
|
|
// execute: perform alu logic
|
|
// note: officially, only 1 alu operation is allowed per opcode
|
|
m_adder_out = m_p + m_n + m_carry_in;
|
|
int carry_out = m_adder_out >> 4 & 1;
|
|
int status = 1;
|
|
m_ram_out = -1;
|
|
|
|
if (m_micro & M_C8) status &= carry_out;
|
|
if (m_micro & M_NE) status &= (m_n != m_p); // COMP
|
|
if (m_micro & M_CKM) m_ram_out = m_cki_bus;
|
|
|
|
// special status circuit
|
|
if (m_micro & M_SSE)
|
|
{
|
|
m_eac = m_carry_out;
|
|
if (m_add)
|
|
m_eac |= carry_out;
|
|
}
|
|
m_carry_out = carry_out;
|
|
|
|
if (m_micro & M_STO || (m_micro & M_CME && m_eac == m_add))
|
|
m_ram_out = m_a;
|
|
|
|
// handle the other fixed opcodes here
|
|
if (m_fixed & F_SBIT) op_sbit();
|
|
if (m_fixed & F_RBIT) op_rbit();
|
|
if (m_fixed & F_SETR) op_setr();
|
|
if (m_fixed & F_RSTR) op_rstr();
|
|
if (m_fixed & F_TDO) op_tdo();
|
|
if (m_fixed & F_CLO) op_clo();
|
|
if (m_fixed & F_LDX) op_ldx();
|
|
if (m_fixed & F_COMX) op_comx();
|
|
if (m_fixed & F_COMX8) op_comx8();
|
|
if (m_fixed & F_LDP) op_ldp();
|
|
if (m_fixed & F_COMC) op_comc();
|
|
if (m_fixed & F_TPC) op_tpc();
|
|
if (m_fixed & F_OFF) op_off();
|
|
if (m_fixed & F_SEAC) op_seac();
|
|
if (m_fixed & F_REAC) op_reac();
|
|
if (m_fixed & F_SAL) op_sal();
|
|
if (m_fixed & F_SBL) op_sbl();
|
|
if (m_fixed & F_XDA) op_xda();
|
|
|
|
// after fixed opcode handling: store status, write ram
|
|
m_status = status;
|
|
if (m_ram_out != -1)
|
|
m_data->write_byte(m_ram_address, m_ram_out);
|
|
|
|
break;
|
|
}
|
|
|
|
case 3:
|
|
// fetch: update pc, ram address 1/2
|
|
// execute: register store 1/2
|
|
break;
|
|
|
|
case 4:
|
|
// execute: register store 2/2
|
|
if (m_micro & M_AUTA) m_a = m_adder_out & 0xf;
|
|
if (m_micro & M_AUTY) m_y = m_adder_out & 0xf;
|
|
if (m_micro & M_STSL) m_status_latch = m_status;
|
|
|
|
// fetch: update pc, ram address 2/2
|
|
read_opcode();
|
|
m_ram_address = m_x << 4 | m_y;
|
|
break;
|
|
|
|
case 5:
|
|
// fetch: instruction decode (handled above, before next_pc)
|
|
// execute: br/call 1/2
|
|
break;
|
|
}
|
|
|
|
m_subcycle = (m_subcycle + 1) % 6;
|
|
}
|
|
}
|