summaryrefslogtreecommitdiffstatshomepage
path: root/src/devices/cpu/cosmac/cosmac.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/devices/cpu/cosmac/cosmac.cpp')
-rw-r--r--src/devices/cpu/cosmac/cosmac.cpp287
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