diff options
Diffstat (limited to 'src/devices/cpu/cosmac/cosmac.cpp')
-rw-r--r-- | src/devices/cpu/cosmac/cosmac.cpp | 287 |
1 files changed, 194 insertions, 93 deletions
diff --git a/src/devices/cpu/cosmac/cosmac.cpp b/src/devices/cpu/cosmac/cosmac.cpp index 7038a26a470..eb1b73397ac 100644 --- a/src/devices/cpu/cosmac/cosmac.cpp +++ b/src/devices/cpu/cosmac/cosmac.cpp @@ -10,15 +10,13 @@ TODO: - is it useful to emulate I and N registers or can they just be defined as (m_op >> x & 0xf)? - 1804/5/6: extended opcode timing is wrong, multiple execute states - 1804/5/6: add more extended opcodes (05/06 supports more than 04) -- 1804/5/6: add counter/timer +- 1804/5/6: other counter modes - 1804/5: add internal address map (ram/rom) **********************************************************************/ #include "emu.h" -#include "debugger.h" #include "cosmac.h" -#include "coreutil.h" // permit our enums to be saved ALLOW_SAVE_TYPE(cosmac_device::cosmac_mode); @@ -253,10 +251,10 @@ cosmac_device::ophandler cdp1802_device::get_ophandler(uint16_t opcode) const const cosmac_device::ophandler cdp1804_device::s_opcodetable_ex[256] = { - &cdp1804_device::und, &cdp1804_device::und, &cdp1804_device::und, &cdp1804_device::und, - &cdp1804_device::und, &cdp1804_device::und, &cdp1804_device::ldc, &cdp1804_device::und, - &cdp1804_device::gec, &cdp1804_device::und, &cdp1804_device::und, &cdp1804_device::und, - &cdp1804_device::und, &cdp1804_device::und, &cdp1804_device::und, &cdp1804_device::und, + &cdp1804_device::stpc, &cdp1804_device::und, &cdp1804_device::spm2, &cdp1804_device::scm2, + &cdp1804_device::spm1, &cdp1804_device::scm1, &cdp1804_device::ldc, &cdp1804_device::stm, + &cdp1804_device::gec, &cdp1804_device::und, &cdp1804_device::xie, &cdp1804_device::xid, + &cdp1804_device::cie, &cdp1804_device::cid, &cdp1804_device::und, &cdp1804_device::und, &cdp1804_device::und, &cdp1804_device::und, &cdp1804_device::und, &cdp1804_device::und, &cdp1804_device::und, &cdp1804_device::und, &cdp1804_device::und, &cdp1804_device::und, @@ -360,31 +358,30 @@ DEFINE_DEVICE_TYPE(CDP1806, cdp1806_device, "cdp1806", "RCA CDP1806") // cosmac_device - constructor //------------------------------------------------- -cosmac_device::cosmac_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, uint32_t clock) - : cpu_device(mconfig, type, tag, owner, clock), - cosmac_disassembler::config(), - m_program_config("program", ENDIANNESS_LITTLE, 8, 16), - m_io_config("io", ENDIANNESS_LITTLE, 8, 3), - m_read_wait(*this), - m_read_clear(*this), - m_read_ef{{*this}, {*this}, {*this}, {*this}}, - m_write_q(*this), - m_read_dma(*this), - m_write_dma(*this), - m_write_sc(*this), - m_write_tpb(*this), - m_op(0), - m_state(cosmac_state::STATE_1_INIT), - m_mode(cosmac_mode::RESET), - m_pmode(cosmac_mode::RUN), - m_wait(true), - m_clear(true), - m_irq(CLEAR_LINE), - m_dmain(CLEAR_LINE), - m_dmaout(CLEAR_LINE), - m_program(nullptr), - m_io(nullptr), - m_cache(nullptr) +cosmac_device::cosmac_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, uint32_t clock) : + cpu_device(mconfig, type, tag, owner, clock), + cosmac_disassembler::config(), + m_program_config("program", ENDIANNESS_LITTLE, 8, 16), + m_io_config("io", ENDIANNESS_LITTLE, 8, 3), + m_read_wait(*this, 0), + m_read_clear(*this, 0), + m_read_int(*this, 0), + m_read_dma_in(*this, 0), + m_read_dma_out(*this, 0), + m_read_ef(*this, 0), + m_write_q(*this), + m_read_dma(*this, 0), + m_write_dma(*this), + m_write_sc(*this), + m_write_tpb(*this), + m_state(cosmac_state::STATE_1_INIT), + m_mode(cosmac_mode::RESET), + m_pmode(cosmac_mode::RUN), + m_wait(true), + m_clear(true), + m_irq(CLEAR_LINE), + m_dmain(CLEAR_LINE), + m_dmaout(CLEAR_LINE) { for (auto & elem : m_ef) elem = CLEAR_LINE; @@ -456,21 +453,38 @@ cdp1806_device::cdp1806_device(const machine_config &mconfig, const char *tag, d void cosmac_device::device_start() { - // resolve callbacks - m_read_wait.resolve(); - m_read_clear.resolve(); - for (auto &cb : m_read_ef) - cb.resolve(); - m_write_q.resolve_safe(); - m_read_dma.resolve_safe(0); - m_write_dma.resolve_safe(); - m_write_sc.resolve_safe(); - m_write_tpb.resolve_safe(); + // init uninitialized + m_pc = 0; + m_op = 0; + m_flagsio = 0; + + m_d = 0; + m_b = 0; + m_p = 0; + m_x = 0; + m_n = 0; + m_i = 0; + m_t = 0; + + m_df = 0; + m_ie = 0; + m_cie = 0; + m_xie = 0; + m_cil = 0; + m_q = 0; + + for (uint16_t &r : m_r) + r = 0; + + m_cnt_mode = 0; + m_cnt_load = 0; + m_cnt_count = 0; + m_cnt_timer = timer_alloc(FUNC(cosmac_device::cnt_timerout), this); // get our address spaces - m_program = &space(AS_PROGRAM); - m_cache = m_program->cache<0, 0, ENDIANNESS_LITTLE>(); - m_io = &space(AS_IO); + space(AS_PROGRAM).cache(m_cache); + space(AS_PROGRAM).specific(m_program); + space(AS_IO).specific(m_io); // register our state for the debugger state_add(STATE_GENPC, "GENPC", m_pc).callimport().callexport().noshow(); @@ -514,7 +528,13 @@ void cosmac_device::device_start() save_item(NAME(m_t)); save_item(NAME(m_df)); save_item(NAME(m_ie)); + save_item(NAME(m_cie)); + save_item(NAME(m_xie)); + save_item(NAME(m_cil)); save_item(NAME(m_q)); + save_item(NAME(m_cnt_mode)); + save_item(NAME(m_cnt_load)); + save_item(NAME(m_cnt_count)); // set our instruction counter set_icountptr(m_icount); @@ -527,13 +547,7 @@ void cosmac_device::device_start() void cosmac_device::device_reset() { - m_ie = 0; - set_q_flag(0); - m_df = 0; - m_p = 0; - - for (int i = 0; i < ARRAY_LENGTH(m_r); i++) - m_r[i] = machine().rand() & 0xffff; + // not here, it's done with WAIT and CLEAR lines } @@ -645,7 +659,7 @@ std::unique_ptr<util::disasm_interface> cdp1805_device::create_disassembler() inline uint8_t cosmac_device::read_opcode(offs_t pc) { - return m_cache->read_byte(pc); + return m_cache.read_byte(pc); } @@ -655,7 +669,7 @@ inline uint8_t cosmac_device::read_opcode(offs_t pc) inline uint8_t cosmac_device::read_byte(offs_t address) { - return m_program->read_byte(address); + return m_program.read_byte(address); } @@ -666,7 +680,7 @@ inline uint8_t cosmac_device::read_byte(offs_t address) inline uint8_t cosmac_device::read_io_byte(offs_t address) { - return m_io->read_byte(address); + return m_io.read_byte(address); } @@ -676,7 +690,7 @@ inline uint8_t cosmac_device::read_io_byte(offs_t address) inline void cosmac_device::write_byte(offs_t address, uint8_t data) { - m_program->write_byte(address, data); + m_program.write_byte(address, data); } @@ -687,7 +701,7 @@ inline void cosmac_device::write_byte(offs_t address, uint8_t data) inline void cosmac_device::write_io_byte(offs_t address, uint8_t data) { - m_io->write_byte(address, data); + m_io.write_byte(address, data); } @@ -711,7 +725,7 @@ offs_t cosmac_device::get_memory_address() // cycles it takes for one instruction to execute //------------------------------------------------- -uint32_t cosmac_device::execute_min_cycles() const +uint32_t cosmac_device::execute_min_cycles() const noexcept { return 8 * 2; } @@ -722,24 +736,13 @@ uint32_t cosmac_device::execute_min_cycles() const // cycles it takes for one instruction to execute //------------------------------------------------- -uint32_t cosmac_device::execute_max_cycles() const +uint32_t cosmac_device::execute_max_cycles() const noexcept { return 8 * 3; } //------------------------------------------------- -// execute_input_lines - return the number of -// input/interrupt lines -//------------------------------------------------- - -uint32_t cosmac_device::execute_input_lines() const -{ - return 7; -} - - -//------------------------------------------------- // execute_set_input - //------------------------------------------------- @@ -796,7 +799,7 @@ void cosmac_device::execute_run() // execute initialization cycle m_state = cosmac_state::STATE_1_INIT; - run(); + run_state(); // next state is IDLE m_state = cosmac_state::STATE_1_EXECUTE; @@ -812,11 +815,13 @@ void cosmac_device::execute_run() break; case cosmac_mode::RESET: + debugger_wait_hook(); reset_state(); m_icount--; break; case cosmac_mode::PAUSE: + debugger_wait_hook(); m_icount--; break; @@ -864,7 +869,7 @@ inline void cosmac_device::run_state() { case cosmac_state::STATE_0_FETCH: m_op = 0; - + [[fallthrough]]; case cosmac_state::STATE_0_FETCH_2ND: fetch_instruction(); break; @@ -876,7 +881,7 @@ inline void cosmac_device::run_state() case cosmac_state::STATE_1_EXECUTE: sample_ef_lines(); - + [[fallthrough]]; case cosmac_state::STATE_1_EXECUTE_2ND: execute_instruction(); debug(); @@ -906,7 +911,7 @@ inline void cosmac_device::run_state() inline void cosmac_device::debug() { - if ((device_t::machine().debug_flags & DEBUG_FLAG_ENABLED) && m_state == cosmac_state::STATE_0_FETCH) + if (debugger_enabled() && m_state == cosmac_state::STATE_0_FETCH) { debugger_instruction_hook(R[P]); } @@ -919,11 +924,33 @@ inline void cosmac_device::debug() inline void cosmac_device::sample_wait_clear() { - if (!m_read_wait.isnull()) m_wait = m_read_wait(); - if (!m_read_clear.isnull()) m_clear = m_read_clear(); + if (!m_read_wait.isunset()) m_wait = m_read_wait(); + if (!m_read_clear.isunset()) m_clear = m_read_clear(); m_pmode = m_mode; - m_mode = (cosmac_mode) ((m_clear << 1) | m_wait); + m_mode = cosmac_mode((m_clear << 1) | m_wait); +} + + +//------------------------------------------------- +// sample_interrupt - sample interrupt line +//------------------------------------------------- + +bool cosmac_device::sample_interrupt() +{ + if (!m_read_int.isunset()) m_irq = m_read_int(); + return bool(m_irq); +} + + +//------------------------------------------------- +// sample_dma_io - sample dma in/out lines +//------------------------------------------------- + +inline void cosmac_device::sample_dma_io() +{ + if (!m_read_dma_in.isunset()) m_dmain = m_read_dma_in(); + if (!m_read_dma_out.isunset()) m_dmaout = m_read_dma_out(); } @@ -934,7 +961,7 @@ inline void cosmac_device::sample_wait_clear() inline void cosmac_device::sample_ef_lines() { for (int i = 0; i < 4; i++) - EF[i] = m_read_ef[i].isnull() ? m_ef_line[i] : m_read_ef[i](); + EF[i] = m_read_ef[i].isunset() ? m_ef_line[i] : m_read_ef[i](); } @@ -1059,7 +1086,7 @@ inline void cosmac_device::fetch_instruction() // reset_state - handle reset state //------------------------------------------------- -inline void cosmac_device::reset_state() +void cosmac_device::reset_state() { m_state = cosmac_state::STATE_1_INIT; @@ -1073,6 +1100,16 @@ inline void cosmac_device::reset_state() output_state_code(); } +void cdp1804_device::reset_state() +{ + m_xie = 1; + m_cie = 1; + m_cil = 0; + stop_count(); + + cosmac_device::reset_state(); +} + //------------------------------------------------- // initialize - handle initialization state @@ -1080,6 +1117,7 @@ inline void cosmac_device::reset_state() inline void cosmac_device::initialize() { + T = (X << 4) | P; X = 0; P = 0; R[0] = 0; @@ -1089,6 +1127,8 @@ inline void cosmac_device::initialize() m_icount -= CLOCKS_INIT; + sample_dma_io(); + if (m_dmain) { m_state = cosmac_state::STATE_2_DMA_IN; @@ -1117,6 +1157,8 @@ inline void cosmac_device::execute_instruction() m_icount -= CLOCKS_EXECUTE; + sample_dma_io(); + if (m_state == cosmac_state::STATE_1_EXECUTE && (m_op >> 4) == 0xc) // "long" opcodes { m_state = cosmac_state::STATE_1_EXECUTE_2ND; @@ -1129,7 +1171,7 @@ inline void cosmac_device::execute_instruction() { m_state = cosmac_state::STATE_2_DMA_OUT; } - else if (IE && m_irq) + else if (check_irq()) { m_state = cosmac_state::STATE_3_INT; } @@ -1153,6 +1195,8 @@ inline void cosmac_device::dma_input() m_icount -= CLOCKS_DMA; + sample_dma_io(); + if (m_dmain) { m_state = cosmac_state::STATE_2_DMA_IN; @@ -1161,7 +1205,7 @@ inline void cosmac_device::dma_input() { m_state = cosmac_state::STATE_2_DMA_OUT; } - else if (IE && m_irq) + else if (check_irq()) { m_state = cosmac_state::STATE_3_INT; } @@ -1173,8 +1217,6 @@ inline void cosmac_device::dma_input() { m_state = cosmac_state::STATE_0_FETCH; } - - standard_irq_callback(COSMAC_INPUT_LINE_DMAIN); } @@ -1191,6 +1233,8 @@ inline void cosmac_device::dma_output() m_icount -= CLOCKS_DMA; + sample_dma_io(); + if (m_dmain) { m_state = cosmac_state::STATE_2_DMA_IN; @@ -1199,7 +1243,7 @@ inline void cosmac_device::dma_output() { m_state = cosmac_state::STATE_2_DMA_OUT; } - else if (IE && m_irq) + else if (check_irq()) { m_state = cosmac_state::STATE_3_INT; } @@ -1207,8 +1251,6 @@ inline void cosmac_device::dma_output() { m_state = cosmac_state::STATE_0_FETCH; } - - standard_irq_callback(COSMAC_INPUT_LINE_DMAOUT); } @@ -1218,6 +1260,8 @@ inline void cosmac_device::dma_output() inline void cosmac_device::interrupt() { + standard_irq_callback(COSMAC_INPUT_LINE_INT, R[P]); + T = (X << 4) | P; X = 2; P = 1; @@ -1228,6 +1272,8 @@ inline void cosmac_device::interrupt() m_icount -= CLOCKS_INTERRUPT; + sample_dma_io(); + if (m_dmain) { m_state = cosmac_state::STATE_2_DMA_IN; @@ -1240,8 +1286,6 @@ inline void cosmac_device::interrupt() { m_state = cosmac_state::STATE_0_FETCH; } - - standard_irq_callback(COSMAC_INPUT_LINE_INT); } @@ -1403,7 +1447,7 @@ void cosmac_device::lsie() { long_skip(IE); } // control instructions opcode handlers void cosmac_device::idl() { /* idle */ } void cosmac_device::nop() { } -void cosmac_device::und() { /* undefined opcode in CDP1801 */ } +void cosmac_device::und() { /* undefined opcode */ } void cosmac_device::sep() { P = N; } void cosmac_device::sex() { X = N; } void cosmac_device::seq() { set_q_flag(1); } @@ -1460,11 +1504,68 @@ void cosmac_device::rsxd() { RAM_W(R[X], R[N] & 0xff); R[X]--; RAM_W(R[X], R[N] void cosmac_device::rnx() { R[X] = R[N]; } -void cosmac_device::bci() { short_branch(1); } // wrong! tests CI flag -void cosmac_device::bxi() { short_branch(0); } // wrong! tests XI flag +void cosmac_device::bci() { short_branch(m_cil); m_cil = 0; } +void cosmac_device::bxi() { short_branch(sample_interrupt()); } + +void cosmac_device::xie() { m_xie = 1; } +void cosmac_device::xid() { m_xie = 0; } +void cosmac_device::cie() { m_cie = 1; } +void cosmac_device::cid() { m_cie = 0; } + +void cosmac_device::scm1() { stop_count(); m_cnt_mode = 2; } +void cosmac_device::scm2() { stop_count(); m_cnt_mode = 3; } +void cosmac_device::spm1() { stop_count(); m_cnt_mode = 4; } +void cosmac_device::spm2() { stop_count(); m_cnt_mode = 5; } + +void cosmac_device::gec() { D = get_count(); } +void cosmac_device::stpc() { stop_count(); } + +void cosmac_device::stm() +{ + if (m_cnt_mode != 1) + { + // start clocking counter in timer mode + m_cnt_mode = 1; + int count = (m_cnt_count == 0) ? 0x100 : m_cnt_count; + m_cnt_timer->adjust(attotime::from_ticks(0x100 * count, clock())); + } +} + +void cosmac_device::ldc() +{ + m_cnt_load = D; + if (m_cnt_mode == 0) + { + m_cnt_count = m_cnt_load; + m_cil = 0; + } +} -void cosmac_device::ldc() { /* logerror("LDC counter set: %X\n", D); */ } -void cosmac_device::gec() { D = machine().rand() & 0xf; } // wrong! +uint8_t cosmac_device::get_count() +{ + if (m_cnt_mode == 1) + { + // get current count of active timer (rounded up) + uint64_t ticks = m_cnt_timer->remaining().as_ticks(clock()); + return (ticks >> 8) + ((ticks & 0xff) ? 1 : 0); + } + else + return m_cnt_count; +} + +void cosmac_device::stop_count() +{ + m_cnt_count = get_count(); + m_cnt_mode = 0; + m_cnt_timer->adjust(attotime::never); +} + +TIMER_CALLBACK_MEMBER(cosmac_device::cnt_timerout) +{ + m_cil = 1; + int count = (m_cnt_load == 0) ? 0x100 : m_cnt_load; + m_cnt_timer->adjust(attotime::from_ticks(0x100 * count, clock())); +} // CDP1805/06 additional extended opcodes // TODO |