mirror of
https://github.com/holub/mame
synced 2025-04-16 21:44:32 +03:00
New working machines
-------------------- Mark 1 FORTH Computer [AJR]
This commit is contained in:
parent
b17a4396e1
commit
7002aae556
@ -3482,3 +3482,20 @@ if (CPUS["LC57"]~=null or _OPTIONS["with-tools"]) then
|
||||
table.insert(disasm_files , MAME_DIR .. "src/devices/cpu/lc57/lc57d.cpp")
|
||||
table.insert(disasm_files , MAME_DIR .. "src/devices/cpu/lc57/lc57d.h")
|
||||
end
|
||||
|
||||
--------------------------------------------------
|
||||
-- Mark I (Andrew Holme)
|
||||
--@src/devices/cpu/mk1/mk1.h,CPUS["MK1"] = true
|
||||
--------------------------------------------------
|
||||
|
||||
if (CPUS["MK1"]~=null) then
|
||||
files {
|
||||
MAME_DIR .. "src/devices/cpu/mk1/mk1.cpp",
|
||||
MAME_DIR .. "src/devices/cpu/mk1/mk1.h",
|
||||
}
|
||||
end
|
||||
|
||||
if (CPUS["MK1"]~=null or _OPTIONS["with-tools"]) then
|
||||
table.insert(disasm_files , MAME_DIR .. "src/devices/cpu/mk1/mk1dasm.cpp")
|
||||
table.insert(disasm_files , MAME_DIR .. "src/devices/cpu/mk1/mk1dasm.h")
|
||||
end
|
||||
|
@ -168,6 +168,7 @@ CPUS["XAVIX2"] = true
|
||||
CPUS["UPD78K"] = true
|
||||
CPUS["ROMP"] = true
|
||||
CPUS["COPS1"] = true
|
||||
CPUS["MK1"] = true
|
||||
|
||||
--------------------------------------------------
|
||||
-- specify available sound cores; some of these are
|
||||
@ -2562,6 +2563,7 @@ files {
|
||||
MAME_DIR .. "src/mame/drivers/lft_phasor.cpp",
|
||||
MAME_DIR .. "src/mame/drivers/dcebridge.cpp",
|
||||
MAME_DIR .. "src/mame/drivers/homez80.cpp",
|
||||
MAME_DIR .. "src/mame/drivers/mk1forth.cpp",
|
||||
MAME_DIR .. "src/mame/drivers/p112.cpp",
|
||||
MAME_DIR .. "src/mame/drivers/phunsy.cpp",
|
||||
MAME_DIR .. "src/mame/drivers/pimps.cpp",
|
||||
|
521
src/devices/cpu/mk1/mk1.cpp
Normal file
521
src/devices/cpu/mk1/mk1.cpp
Normal file
@ -0,0 +1,521 @@
|
||||
// license:BSD-3-Clause
|
||||
// copyright-holders:AJR
|
||||
/***************************************************************************
|
||||
|
||||
Mark 1 FORTH Computer TTL CPU
|
||||
|
||||
This emulates the vertically microcoded CPU of Andrew Holme's Mark 1.
|
||||
Each instruction executes in one cycle of the quadrature clock. All
|
||||
data paths other than the address bus are 8 bits wide, even though
|
||||
16-bit words are the nominal basic data type.
|
||||
|
||||
The instruction set is very rudimentary. ALU operations require the
|
||||
operands and function to be loaded in three separate steps. 0 is the
|
||||
only immediate operand that can be moved into a register. The only
|
||||
program transfer operations allowed by the microcode sequencer are
|
||||
conditional forward skips, direct jumps to within the first 16
|
||||
instructions and an indirect jump to one of 256 subroutines beginning
|
||||
on 16-word boundaries. The stack pointers can only be initialized by
|
||||
the hardware RESET signal.
|
||||
|
||||
Though the CPU decodes microinstructions without the aid of any
|
||||
microprocessor, gate arrays, PLDs or PROMs, it does use a 7x16 diode
|
||||
matrix ROM to generate 74LS181 function codes for the ALU.
|
||||
|
||||
The W and IP index registers are implemented on identical boards using
|
||||
four 74LS169 counters each. A jumper and LS157 selector are used to
|
||||
associate each board with the correct set of decode signals.
|
||||
|
||||
The parameter and return stacks logically hold 256 16-bit words each,
|
||||
but the stack board actually implements them using a pair of dedicated
|
||||
byte-wide 6116 or 6264 static RAMs. This emulation uses a single
|
||||
address space for both stacks.
|
||||
|
||||
The ALU's overflow checker and interrupt feature are not actually
|
||||
needed by the current microcode. They are emulated here for the sake of
|
||||
completeness.
|
||||
|
||||
***************************************************************************/
|
||||
|
||||
#include "emu.h"
|
||||
#include "mk1.h"
|
||||
#include "mk1dasm.h"
|
||||
|
||||
|
||||
// device type definition
|
||||
DEFINE_DEVICE_TYPE(MK1_CPU, mk1_cpu_device, "mk1_cpu", "Mark 1 CPU")
|
||||
|
||||
mk1_cpu_device::mk1_cpu_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock)
|
||||
: cpu_device(mconfig, MK1_CPU, tag, owner, clock)
|
||||
, m_program_config("program", ENDIANNESS_LITTLE, 8, 12, 0)
|
||||
, m_data_config("data", ENDIANNESS_LITTLE, 8, 16, 0)
|
||||
, m_stack_config("stack", ENDIANNESS_LITTLE, 8, 10, 0)
|
||||
, m_pc(0)
|
||||
, m_inst(0)
|
||||
, m_op_latch(0)
|
||||
, m_index_reg{0, 0}
|
||||
, m_sp{0, 0}
|
||||
, m_alu_a(0)
|
||||
, m_alu_b(0)
|
||||
, m_alu_function(0b1111111)
|
||||
, m_alu_result(0)
|
||||
, m_cond_flags(0b1010)
|
||||
, m_irq_asserted(false)
|
||||
, m_irq_enabled(false)
|
||||
, m_icount(0)
|
||||
{
|
||||
}
|
||||
|
||||
std::unique_ptr<util::disasm_interface> mk1_cpu_device::create_disassembler()
|
||||
{
|
||||
return std::make_unique<mk1_disassembler>();
|
||||
}
|
||||
|
||||
device_memory_interface::space_config_vector mk1_cpu_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),
|
||||
std::make_pair(AS_STACK, &m_stack_config)
|
||||
};
|
||||
}
|
||||
|
||||
void mk1_cpu_device::device_start()
|
||||
{
|
||||
// Hook address spaces
|
||||
space(AS_PROGRAM).cache(m_cache);
|
||||
space(AS_DATA).specific(m_data);
|
||||
space(AS_STACK).specific(m_stack);
|
||||
|
||||
set_icountptr(m_icount);
|
||||
|
||||
// Register debug state
|
||||
state_add(MK1_PC, "PC", m_pc).mask(0xfff);
|
||||
state_add(STATE_GENPC, "GENPC", m_pc).noshow().mask(0xfff);
|
||||
state_add(STATE_GENPCBASE, "CURPC", m_pc).noshow().mask(0xfff);
|
||||
state_add(STATE_GENFLAGS, "FLAGS", m_cond_flags).noshow().mask(0b1111).formatstr("%4s");
|
||||
state_add(MK1_OP, "OP", m_op_latch);
|
||||
state_add(MK1_W, "W", m_index_reg[0]);
|
||||
state_add(MK1_IP, "IP", m_index_reg[1]);
|
||||
state_add<u16>(MK1_TOS, "TOS",
|
||||
[this]() { auto dis = machine().disable_side_effects(); return m_stack.read_word(u16(m_sp[0]) << 1); },
|
||||
[this](u16 data) { auto dis = machine().disable_side_effects(); m_stack.write_word(u16(m_sp[0]) << 1, data); }
|
||||
);
|
||||
state_add<u16>(MK1_RS, "RS",
|
||||
[this]() { auto dis = machine().disable_side_effects(); return m_stack.read_word(0x200 | u16(m_sp[1]) << 1); },
|
||||
[this](u16 data) { auto dis = machine().disable_side_effects(); m_stack.write_word(0x200 | u16(m_sp[1]) << 1, data); }
|
||||
);
|
||||
state_add(MK1_PSP, "PSP", m_sp[0]);
|
||||
state_add(MK1_RSP, "RSP", m_sp[1]);
|
||||
state_add(MK1_A, "A", m_alu_a, [this](u8 data) { m_alu_a = data; alu_update(); });
|
||||
state_add(MK1_B, "B", m_alu_b, [this](u8 data) { m_alu_b = data; alu_update(); });
|
||||
state_add(MK1_ALU, "ALU", m_alu_function, [this](u8 data) { m_alu_function = data; alu_update(); }).mask(0b1111111).formatstr("%3s");
|
||||
state_add(MK1_F, "F", m_alu_result).readonly();
|
||||
state_add(MK1_IE, "IE", m_irq_enabled, [this](bool state) { set_irq_enable(state); });
|
||||
|
||||
// Save internal state
|
||||
save_item(NAME(m_pc));
|
||||
save_item(NAME(m_inst));
|
||||
save_item(NAME(m_op_latch));
|
||||
save_item(NAME(m_index_reg));
|
||||
save_item(NAME(m_sp));
|
||||
save_item(NAME(m_alu_a));
|
||||
save_item(NAME(m_alu_b));
|
||||
save_item(NAME(m_alu_function));
|
||||
save_item(NAME(m_alu_result));
|
||||
save_item(NAME(m_cond_flags));
|
||||
save_item(NAME(m_irq_asserted));
|
||||
save_item(NAME(m_irq_enabled));
|
||||
}
|
||||
|
||||
void mk1_cpu_device::device_reset()
|
||||
{
|
||||
// Reset microprogram counter
|
||||
m_pc = 0;
|
||||
m_inst = 0;
|
||||
|
||||
// Reset stack pointers
|
||||
m_sp[0] = m_sp[1] = 0;
|
||||
}
|
||||
|
||||
// ALU functions decoded by 7x16 diode matrix ROM
|
||||
const u8 mk1_cpu_device::s_alu_decode[16] =
|
||||
{
|
||||
0b11101001, // ADD
|
||||
0b01101001, // ADC
|
||||
0b10100110, // SUB
|
||||
0b01100110, // SBB
|
||||
0b11101100, // ASL
|
||||
0b01101100, // ROL
|
||||
0b11101111,
|
||||
0b11101111,
|
||||
|
||||
0b11111111, // A
|
||||
0b11111010, // B
|
||||
0b11111011, // AND
|
||||
0b11111110, // OR
|
||||
0b11010000, // NOT
|
||||
0b11110110, // XOR
|
||||
0b11111001, // A=B
|
||||
0b11111111
|
||||
};
|
||||
|
||||
void mk1_cpu_device::alu_update()
|
||||
{
|
||||
// Ultra-complete emulation of 74LS181 ALU functions (most not actually used here)
|
||||
bool carry = !BIT(m_alu_function, 6);
|
||||
switch (BIT(m_alu_function, 0, 4))
|
||||
{
|
||||
case 0b0000:
|
||||
if (BIT(m_alu_function, 4))
|
||||
m_alu_result = ~m_alu_a;
|
||||
else
|
||||
m_alu_result = m_alu_a + (carry ? 1 : 0);
|
||||
carry = carry && m_alu_a == 0xff;
|
||||
break;
|
||||
|
||||
case 0b0001:
|
||||
if (BIT(m_alu_function, 4))
|
||||
m_alu_result = ~(m_alu_a | m_alu_b);
|
||||
else
|
||||
m_alu_result = (m_alu_a | m_alu_b) + (carry ? 1 : 0);
|
||||
carry = carry && (m_alu_a | m_alu_b) == 0xff;
|
||||
break;
|
||||
|
||||
case 0b0010:
|
||||
if (BIT(m_alu_function, 4))
|
||||
m_alu_result = ~m_alu_a & m_alu_b;
|
||||
else
|
||||
m_alu_result = (m_alu_a | ~m_alu_b) + (carry ? 1 : 0);
|
||||
carry = carry && (m_alu_a | ~m_alu_b) == 0xff;
|
||||
break;
|
||||
|
||||
case 0b0011:
|
||||
m_alu_result = BIT(m_alu_function, 4) || carry ? 0 : -1;
|
||||
break;
|
||||
|
||||
case 0b0100:
|
||||
if (BIT(m_alu_function, 4))
|
||||
m_alu_result = ~(m_alu_a & m_alu_b);
|
||||
else
|
||||
m_alu_result = m_alu_a + (m_alu_a & ~m_alu_b) + (carry ? 1 : 0);
|
||||
carry = u16(m_alu_a) + u16(m_alu_a & ~m_alu_b) + (carry ? 1 : 0) >= 0x100;
|
||||
break;
|
||||
|
||||
case 0b0101:
|
||||
if (BIT(m_alu_function, 4))
|
||||
m_alu_result = ~m_alu_b;
|
||||
else
|
||||
m_alu_result = (m_alu_a | m_alu_b) + (m_alu_a & ~m_alu_b) + (carry ? 1 : 0);
|
||||
carry = u16(m_alu_a | m_alu_b) + u16(m_alu_a & ~m_alu_b) + (carry ? 1 : 0) >= 0x100;
|
||||
break;
|
||||
|
||||
case 0b0110:
|
||||
if (BIT(m_alu_function, 4))
|
||||
m_alu_result = m_alu_a ^ m_alu_b;
|
||||
else
|
||||
m_alu_result = m_alu_a - m_alu_b - (carry ? 0 : 1);
|
||||
carry = m_alu_a >= m_alu_b + (carry ? 0 : 1);
|
||||
break;
|
||||
|
||||
case 0b0111:
|
||||
m_alu_result = (m_alu_a & ~m_alu_b) - (BIT(m_alu_function, 4) || carry ? 0 : 1);
|
||||
carry = carry || (m_alu_a & ~m_alu_b) != 0;
|
||||
break;
|
||||
|
||||
case 0b1000:
|
||||
if (BIT(m_alu_function, 4))
|
||||
m_alu_result = ~m_alu_a | m_alu_b;
|
||||
else
|
||||
m_alu_result = m_alu_a + (m_alu_a & m_alu_b) + (carry ? 1 : 0);
|
||||
carry = u16(m_alu_a) + u16(m_alu_a & m_alu_b) + (carry ? 1 : 0) >= 0x100;
|
||||
break;
|
||||
|
||||
case 0b1001:
|
||||
if (BIT(m_alu_function, 4))
|
||||
m_alu_result = ~(m_alu_a ^ m_alu_b);
|
||||
else
|
||||
m_alu_result = m_alu_a + m_alu_b + (carry ? 1 : 0);
|
||||
carry = u16(m_alu_a) + u16(m_alu_b) + (carry ? 1 : 0) >= 0x100;
|
||||
break;
|
||||
|
||||
case 0b1010:
|
||||
if (BIT(m_alu_function, 4))
|
||||
m_alu_result = m_alu_b;
|
||||
else
|
||||
m_alu_result = (m_alu_a | ~m_alu_b) + (m_alu_a & m_alu_b) + (carry ? 1 : 0);
|
||||
carry = u16(m_alu_a | ~m_alu_b) + u16(m_alu_a & m_alu_b) + (carry ? 1 : 0) >= 0x100;
|
||||
break;
|
||||
|
||||
case 0b1011:
|
||||
m_alu_result = (m_alu_a & m_alu_b) - (BIT(m_alu_function, 4) || carry ? 0 : 1);
|
||||
carry = carry || (m_alu_a & m_alu_b) != 0;
|
||||
break;
|
||||
|
||||
case 0b1100:
|
||||
if (BIT(m_alu_function, 4))
|
||||
m_alu_result = 0xff;
|
||||
else
|
||||
m_alu_result = m_alu_a + m_alu_a + (carry ? 1 : 0);
|
||||
carry = u16(m_alu_a) + u16(m_alu_a) + (carry ? 1 : 0) >= 0x100;
|
||||
break;
|
||||
|
||||
case 0b1101:
|
||||
if (BIT(m_alu_function, 4))
|
||||
m_alu_result = m_alu_a | ~m_alu_b;
|
||||
else
|
||||
m_alu_result = (m_alu_a | m_alu_b) + m_alu_a + (carry ? 1 : 0);
|
||||
carry = u16(m_alu_a | m_alu_b) + u16(m_alu_a) + (carry ? 1 : 0) >= 0x100;
|
||||
break;
|
||||
|
||||
case 0b1110:
|
||||
if (BIT(m_alu_function, 4))
|
||||
m_alu_result = m_alu_a | m_alu_b;
|
||||
else
|
||||
m_alu_result = (m_alu_a | ~m_alu_b) + m_alu_a + (carry ? 1 : 0);
|
||||
carry = u16(m_alu_a | ~m_alu_b) + u16(m_alu_a) + (carry ? 1 : 0) >= 0x100;
|
||||
break;
|
||||
|
||||
case 0b1111:
|
||||
m_alu_result = m_alu_a - (BIT(m_alu_function, 4) || carry ? 0 : 1);
|
||||
carry = carry || m_alu_a != 0;
|
||||
break;
|
||||
}
|
||||
|
||||
// Update flags
|
||||
m_cond_flags = (m_cond_flags & 8) | (m_alu_result == 0xff ? 4 : 0) | (carry ? 0 : 2) | (BIT(m_alu_result, 7) ? 0 : 1);
|
||||
if (BIT(m_alu_function, 5) && BIT(m_alu_a ^ m_alu_b ^ m_alu_result, 7) == carry)
|
||||
m_cond_flags ^= 1;
|
||||
}
|
||||
|
||||
void mk1_cpu_device::set_alu_function(u8 data)
|
||||
{
|
||||
// D6 and D7 determine carry semantics
|
||||
// Previous carry flag is latched at this time; this may convert ADC to ADD, SBB to SUB or ROL to ASL
|
||||
if (data < 0x80)
|
||||
m_alu_function = (data & 0x3f) | (BIT(m_cond_flags, 1) ? 0x40 : 0x00);
|
||||
else
|
||||
m_alu_function = data & 0x7f;
|
||||
alu_update();
|
||||
}
|
||||
|
||||
void mk1_cpu_device::set_irq_enable(bool state)
|
||||
{
|
||||
// Clock bit into IRQ enable flip-flop
|
||||
m_irq_enabled = state;
|
||||
if (!state)
|
||||
m_cond_flags |= 8;
|
||||
}
|
||||
|
||||
void mk1_cpu_device::execute_one()
|
||||
{
|
||||
if (m_inst < 0x80)
|
||||
{
|
||||
// Fetch source for MOV
|
||||
u8 data;
|
||||
switch (BIT(m_inst, 3, 3))
|
||||
{
|
||||
case 0: case 1:
|
||||
{
|
||||
const u16 &index_reg = m_index_reg[BIT(m_inst, 3)];
|
||||
if (BIT(m_inst, 6))
|
||||
data = index_reg >> 8;
|
||||
else
|
||||
data = index_reg & 0x00ff;
|
||||
break;
|
||||
}
|
||||
|
||||
case 2: case 3:
|
||||
data = m_stack.read_byte((BIT(m_inst, 3) ? 0x200 : 0) | u16(m_sp[BIT(m_inst, 3)]) << 1 | BIT(m_inst, 6));
|
||||
break;
|
||||
|
||||
case 4: case 5:
|
||||
data = m_data.read_byte(m_index_reg[BIT(m_inst, 3)]);
|
||||
break;
|
||||
|
||||
case 6: default:
|
||||
data = 0;
|
||||
break;
|
||||
|
||||
case 7:
|
||||
data = m_alu_result;
|
||||
break;
|
||||
}
|
||||
|
||||
// Move data to destination
|
||||
switch (BIT(m_inst, 0, 3))
|
||||
{
|
||||
case 0: case 1:
|
||||
{
|
||||
u16 &index_reg = m_index_reg[BIT(m_inst, 0)];
|
||||
if (BIT(m_inst, 6))
|
||||
index_reg = (index_reg & 0x00ff) | u16(data) << 8;
|
||||
else
|
||||
index_reg = (index_reg & 0xff00) | data;
|
||||
break;
|
||||
}
|
||||
|
||||
case 2: case 3:
|
||||
m_stack.write_byte((BIT(m_inst, 0) ? 0x200 : 0) | u16(m_sp[BIT(m_inst, 0)]) << 1 | BIT(m_inst, 6), data);
|
||||
break;
|
||||
|
||||
case 4:
|
||||
m_data.write_byte(m_index_reg[0], data);
|
||||
break;
|
||||
|
||||
case 5:
|
||||
m_op_latch = data;
|
||||
break;
|
||||
|
||||
case 6:
|
||||
m_alu_a = data;
|
||||
alu_update();
|
||||
break;
|
||||
|
||||
case 7:
|
||||
m_alu_b = data;
|
||||
alu_update();
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if (m_inst < 0x90)
|
||||
{
|
||||
if (BIT(m_inst, 2))
|
||||
set_irq_enable(BIT(m_inst, 3));
|
||||
else
|
||||
{
|
||||
// 8-bit or 16-bit increment or decrement
|
||||
if (BIT(m_inst, 1))
|
||||
m_sp[BIT(m_inst, 0)] += BIT(m_inst, 3) ? 1 : -1;
|
||||
else
|
||||
m_index_reg[BIT(m_inst, 0)] += BIT(m_inst, 3) ? 1 : -1;
|
||||
}
|
||||
}
|
||||
else if (m_inst < 0xa0)
|
||||
{
|
||||
// Jump direct to within first 16 bytes of microcode
|
||||
m_pc = BIT(m_inst, 0, 4);
|
||||
}
|
||||
else if (m_inst < 0xb0)
|
||||
{
|
||||
// ALU function specified by decode matrix
|
||||
set_alu_function(s_alu_decode[BIT(m_inst, 0, 4)]);
|
||||
}
|
||||
else if (m_inst < 0xc0)
|
||||
{
|
||||
// XOP clears the lower 4 bits of PC while loading the upper 8
|
||||
m_pc = u16(m_op_latch) << 4;
|
||||
}
|
||||
}
|
||||
|
||||
void mk1_cpu_device::execute_run()
|
||||
{
|
||||
do
|
||||
{
|
||||
if ((m_inst & 0xcf) > 0xc0 && !BIT(m_cond_flags, BIT(m_inst, 4, 2)))
|
||||
{
|
||||
// Lower half of microinstruction register becomes a synchronous down counter when a skip is taken
|
||||
--m_inst;
|
||||
m_pc = (m_pc + 1) & 0xfff;
|
||||
}
|
||||
else
|
||||
{
|
||||
debugger_instruction_hook(m_pc);
|
||||
|
||||
// Fetch the next microinstruction and latch IRQ flag
|
||||
const bool was_skip = m_inst >= 0xc0;
|
||||
m_inst = m_cache.read_byte(m_pc);
|
||||
if (!was_skip && m_irq_enabled && m_inst >= 0xc0)
|
||||
m_cond_flags = (m_cond_flags & 7) | (m_irq_asserted ? 0 : 8);
|
||||
m_pc = (m_pc + 1) & 0xfff;
|
||||
|
||||
execute_one();
|
||||
}
|
||||
} while (--m_icount > 0);
|
||||
}
|
||||
|
||||
void mk1_cpu_device::execute_set_input(int linenum, int state)
|
||||
{
|
||||
if (linenum == IRQ_LINE)
|
||||
m_irq_asserted = (state != CLEAR_LINE);
|
||||
}
|
||||
|
||||
void mk1_cpu_device::state_string_export(const device_state_entry &entry, std::string &str) const
|
||||
{
|
||||
switch (entry.index())
|
||||
{
|
||||
case STATE_GENFLAGS:
|
||||
str = util::string_format("%c%c%c%c",
|
||||
BIT(m_cond_flags, 3) ? '.' : 'I',
|
||||
BIT(m_cond_flags, 2) ? '=' : '.',
|
||||
BIT(m_cond_flags, 1) ? '.' : 'C',
|
||||
BIT(m_cond_flags, 0) ? '.' : (BIT(m_alu_function, 5) ? '<' : 'S')
|
||||
);
|
||||
break;
|
||||
|
||||
case MK1_ALU:
|
||||
switch (m_alu_function)
|
||||
{
|
||||
case 0b1101001:
|
||||
str = "ADD";
|
||||
break;
|
||||
|
||||
case 0b0101001:
|
||||
str = "ADC";
|
||||
break;
|
||||
|
||||
case 0b0100110:
|
||||
str = "SUB";
|
||||
break;
|
||||
|
||||
case 0b1100110:
|
||||
str = "SBB";
|
||||
break;
|
||||
|
||||
case 0b1101100:
|
||||
str = "ASL";
|
||||
break;
|
||||
|
||||
case 0b0101100:
|
||||
str = "ROL";
|
||||
break;
|
||||
|
||||
case 0b1111111:
|
||||
str = "A ";
|
||||
break;
|
||||
|
||||
case 0b1011111:
|
||||
str = "0< ";
|
||||
break;
|
||||
|
||||
case 0b1111010:
|
||||
str = "B ";
|
||||
break;
|
||||
|
||||
case 0b1111011:
|
||||
str = "AND";
|
||||
break;
|
||||
|
||||
case 0b1111110:
|
||||
str = "OR ";
|
||||
break;
|
||||
|
||||
case 0b1010000:
|
||||
str = "NOT";
|
||||
break;
|
||||
|
||||
case 0b1110110:
|
||||
str = "XOR";
|
||||
break;
|
||||
|
||||
case 0b1111001:
|
||||
str = "A=B";
|
||||
break;
|
||||
|
||||
default:
|
||||
str = "???";
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
81
src/devices/cpu/mk1/mk1.h
Normal file
81
src/devices/cpu/mk1/mk1.h
Normal file
@ -0,0 +1,81 @@
|
||||
// license:BSD-3-Clause
|
||||
// copyright-holders:AJR
|
||||
|
||||
#ifndef MAME_CPU_MK1_MK1_H
|
||||
#define MAME_CPU_MK1_MK1_H
|
||||
|
||||
#pragma once
|
||||
|
||||
class mk1_cpu_device : public cpu_device
|
||||
{
|
||||
public:
|
||||
enum {
|
||||
MK1_PC, MK1_OP,
|
||||
MK1_W, MK1_IP,
|
||||
MK1_TOS, MK1_RS,
|
||||
MK1_PSP, MK1_RSP,
|
||||
MK1_A, MK1_B, MK1_ALU, MK1_F,
|
||||
MK1_IE
|
||||
};
|
||||
|
||||
static constexpr int AS_STACK = AS_DATA + 1;
|
||||
static constexpr int IRQ_LINE = 0;
|
||||
|
||||
// device type constructor
|
||||
mk1_cpu_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock);
|
||||
|
||||
protected:
|
||||
// device-level overrides
|
||||
virtual void device_start() override;
|
||||
virtual void device_reset() override;
|
||||
|
||||
// device_execute_interface overrides
|
||||
virtual void execute_run() override;
|
||||
virtual void execute_set_input(int linenum, int state) override;
|
||||
|
||||
// device_memory_interface overrides
|
||||
virtual space_config_vector memory_space_config() const override;
|
||||
|
||||
// device_state_interface overrides
|
||||
virtual void state_string_export(const device_state_entry &entry, std::string &str) const override;
|
||||
|
||||
// device_disasm_interface overrides
|
||||
virtual std::unique_ptr<util::disasm_interface> create_disassembler() override;
|
||||
|
||||
private:
|
||||
// internal helpers
|
||||
void alu_update();
|
||||
void set_alu_function(u8 data);
|
||||
void set_irq_enable(bool state);
|
||||
void execute_one();
|
||||
|
||||
static const u8 s_alu_decode[16];
|
||||
|
||||
// address spaces
|
||||
const address_space_config m_program_config;
|
||||
const address_space_config m_data_config;
|
||||
const address_space_config m_stack_config;
|
||||
memory_access<12, 0, 0, ENDIANNESS_LITTLE>::cache m_cache;
|
||||
memory_access<16, 0, 0, ENDIANNESS_LITTLE>::specific m_data;
|
||||
memory_access<10, 0, 0, ENDIANNESS_LITTLE>::specific m_stack;
|
||||
|
||||
// internal state
|
||||
u16 m_pc;
|
||||
u8 m_inst;
|
||||
u8 m_op_latch;
|
||||
u16 m_index_reg[2];
|
||||
u8 m_sp[2];
|
||||
u8 m_alu_a;
|
||||
u8 m_alu_b;
|
||||
u8 m_alu_function;
|
||||
u8 m_alu_result;
|
||||
u8 m_cond_flags;
|
||||
bool m_irq_asserted;
|
||||
bool m_irq_enabled;
|
||||
s32 m_icount;
|
||||
};
|
||||
|
||||
// device type declaration
|
||||
DECLARE_DEVICE_TYPE(MK1_CPU, mk1_cpu_device)
|
||||
|
||||
#endif // MAME_CPU_MK1_MK1_H
|
151
src/devices/cpu/mk1/mk1dasm.cpp
Normal file
151
src/devices/cpu/mk1/mk1dasm.cpp
Normal file
@ -0,0 +1,151 @@
|
||||
// license:BSD-3-Clause
|
||||
// copyright-holders:AJR
|
||||
/***************************************************************************
|
||||
|
||||
Mark 1 FORTH Computer microcode disassembler
|
||||
|
||||
***************************************************************************/
|
||||
|
||||
#include "emu.h"
|
||||
#include "mk1dasm.h"
|
||||
|
||||
mk1_disassembler::mk1_disassembler()
|
||||
: util::disasm_interface()
|
||||
{
|
||||
}
|
||||
|
||||
u32 mk1_disassembler::opcode_alignment() const
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
std::string_view s_srcs[8] = { "w", "ip", "tos", "rs", "[w]", "[ip]", "0", "f" };
|
||||
std::string_view s_dsts[8] = { "w", "ip", "tos", "rs", "[w]", "op", "a", "b" };
|
||||
|
||||
// "b??" is the actual mnemonic Andrew Holme's assembler uses (condition is sign or less depending on operation)
|
||||
std::string_view s_skips[4] = { "b??", "bcs", "bne", "bis" };
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
offs_t mk1_disassembler::disassemble(std::ostream &stream, offs_t pc, const mk1_disassembler::data_buffer &opcodes, const mk1_disassembler::data_buffer ¶ms)
|
||||
{
|
||||
const u8 uop = opcodes.r8(pc);
|
||||
|
||||
if (uop < 0x80)
|
||||
{
|
||||
// Move LSB/MSB
|
||||
util::stream_format(stream, "mov %s%s, %s%s",
|
||||
s_dsts[BIT(uop, 0, 3)],
|
||||
BIT(uop, 2) ? "" : (BIT(uop, 6) ? ".h" : ".l"),
|
||||
s_srcs[BIT(uop, 3, 3)],
|
||||
BIT(uop, 5) ? "" : (BIT(uop, 6) ? ".h" : ".l"));
|
||||
return 1 | SUPPORTED;
|
||||
}
|
||||
else if (uop >= 0xc0)
|
||||
{
|
||||
// Conditional skip
|
||||
const u8 disp = BIT(uop, 0, 4);
|
||||
util::stream_format(stream, "%s %d ; $%03x", s_skips[BIT(uop, 4, 2)], disp, (pc + 1 + disp) & 0xfff);
|
||||
return 1 | SUPPORTED;
|
||||
}
|
||||
else if (uop >= 0xb0)
|
||||
{
|
||||
// Jump indirect
|
||||
stream << "xop";
|
||||
return 1 | SUPPORTED;
|
||||
}
|
||||
else if (uop >= 0xa0)
|
||||
{
|
||||
// Set ALU function
|
||||
stream << "alu ";
|
||||
switch (BIT(uop, 0, 4))
|
||||
{
|
||||
case 0:
|
||||
stream << "add";
|
||||
break;
|
||||
|
||||
case 1:
|
||||
stream << "adc";
|
||||
break;
|
||||
|
||||
case 2:
|
||||
stream << "sub";
|
||||
break;
|
||||
|
||||
case 3:
|
||||
stream << "sbb";
|
||||
break;
|
||||
|
||||
case 4:
|
||||
stream << "asl";
|
||||
break;
|
||||
|
||||
case 5:
|
||||
stream << "rol";
|
||||
break;
|
||||
|
||||
case 8:
|
||||
stream << "a"; // was also to have been "0="
|
||||
break;
|
||||
|
||||
case 9:
|
||||
stream << "b";
|
||||
break;
|
||||
|
||||
case 10:
|
||||
stream << "and";
|
||||
break;
|
||||
|
||||
case 11:
|
||||
stream << "or";
|
||||
break;
|
||||
|
||||
case 12:
|
||||
stream << "not";
|
||||
break;
|
||||
|
||||
case 13:
|
||||
stream << "xor";
|
||||
break;
|
||||
|
||||
case 14:
|
||||
stream << "a=b";
|
||||
break;
|
||||
|
||||
default:
|
||||
util::stream_format(stream, "%d", BIT(uop, 0, 4));
|
||||
break;
|
||||
}
|
||||
return 1 | SUPPORTED;
|
||||
}
|
||||
else if (uop >= 0x90)
|
||||
{
|
||||
// Jump direct
|
||||
util::stream_format(stream, "jmp %d", BIT(uop, 0, 4));
|
||||
return 1 | STEP_OUT | SUPPORTED;
|
||||
}
|
||||
else if (BIT(uop, 2))
|
||||
{
|
||||
// Disable/enable IRQ
|
||||
if (BIT(uop, 3))
|
||||
stream << "eni";
|
||||
else
|
||||
stream << "dis";
|
||||
return 1 | SUPPORTED;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Increment/decrement
|
||||
if (BIT(uop, 3))
|
||||
stream << "inc ";
|
||||
else
|
||||
stream << "dec ";
|
||||
if (BIT(uop, 1))
|
||||
util::stream_format(stream, "%csp", BIT(uop, 0) ? 'r' : 'p');
|
||||
else
|
||||
stream << s_dsts[BIT(uop, 0)];
|
||||
return 1 | SUPPORTED;
|
||||
}
|
||||
}
|
18
src/devices/cpu/mk1/mk1dasm.h
Normal file
18
src/devices/cpu/mk1/mk1dasm.h
Normal file
@ -0,0 +1,18 @@
|
||||
// license:BSD-3-Clause
|
||||
// copyright-holders:AJR
|
||||
|
||||
#ifndef MAME_CPU_MK1_MK1DASM_H
|
||||
#define MAME_CPU_MK1_MK1DASM_H
|
||||
|
||||
#pragma once
|
||||
|
||||
class mk1_disassembler : public util::disasm_interface
|
||||
{
|
||||
public:
|
||||
mk1_disassembler();
|
||||
|
||||
virtual u32 opcode_alignment() const override;
|
||||
virtual offs_t disassemble(std::ostream &stream, offs_t pc, const data_buffer &opcodes, const data_buffer ¶ms) override;
|
||||
};
|
||||
|
||||
#endif // MAME_CPU_MK1_MK1DASM_H
|
101
src/mame/drivers/mk1forth.cpp
Normal file
101
src/mame/drivers/mk1forth.cpp
Normal file
@ -0,0 +1,101 @@
|
||||
// license:BSD-3-Clause
|
||||
// copyright-holders:AJR
|
||||
/***************************************************************************
|
||||
|
||||
Mark 1 FORTH Computer (Copyright © Andrew Holme, 2003)
|
||||
|
||||
This is a homebrew fig-FORTH interpreter using an original TTL CPU,
|
||||
operating at 1 microinstruction per microsecond. Neither the microcode
|
||||
nor the FORTH bytecode fills very much of the 2764 EPROMs specified.
|
||||
|
||||
Notes on the I/O board design:
|
||||
* The second peripheral was supposed to be a 8255, but the port pins
|
||||
were never connected to anything and its socket was left empty.
|
||||
* RxC and TxC rates are generated by a HEF4060BP divider. The original
|
||||
plan was to operate the 82C51 in 1x mode at 38400, 19200 or 9600
|
||||
baud, but reliable 1x asynchronous reception could not be obtained.
|
||||
9600 baud is the maximum rate obtainable in 16x mode.
|
||||
* Before the ALU was designed, the RxRDY signal was buffered directly
|
||||
onto the data bus to be tested as a skip condition. Since this could
|
||||
not be guaranteed to be stable through an entire instruction cycle,
|
||||
an open-drain IRQ signal had to be added to the bus, with the
|
||||
requisite termination and synchronization provided on the ALU board.
|
||||
|
||||
***************************************************************************/
|
||||
|
||||
#include "emu.h"
|
||||
#include "bus/rs232/rs232.h"
|
||||
#include "cpu/mk1/mk1.h"
|
||||
#include "machine/clock.h"
|
||||
#include "machine/input_merger.h"
|
||||
#include "machine/i8251.h"
|
||||
|
||||
class mk1forth_state : public driver_device
|
||||
{
|
||||
public:
|
||||
mk1forth_state(const machine_config &mconfig, device_type type, const char *tag)
|
||||
: driver_device(mconfig, type, tag)
|
||||
{
|
||||
}
|
||||
|
||||
void mk1forth(machine_config &config);
|
||||
|
||||
private:
|
||||
void ucode_map(address_map &map);
|
||||
void data_map(address_map &map);
|
||||
void stack_map(address_map &map);
|
||||
};
|
||||
|
||||
void mk1forth_state::ucode_map(address_map &map)
|
||||
{
|
||||
map(0x000, 0xfff).rom().region("ucode", 0);
|
||||
}
|
||||
|
||||
void mk1forth_state::data_map(address_map &map)
|
||||
{
|
||||
map(0x0000, 0x1fff).rom().region("forth", 0);
|
||||
map(0x2000, 0x7fff).ram();
|
||||
map(0xe000, 0xe001).mirror(0xffe).rw("uart", FUNC(i8251_device::read), FUNC(i8251_device::write));
|
||||
//map(0xf000, 0xf003).mirror(0xffc).rw("pio", FUNC(i8255_device::read), FUNC(i8255_device::write));
|
||||
}
|
||||
|
||||
void mk1forth_state::stack_map(address_map &map)
|
||||
{
|
||||
map(0x000, 0x3ff).ram();
|
||||
}
|
||||
|
||||
void mk1forth_state::mk1forth(machine_config &config)
|
||||
{
|
||||
mk1_cpu_device &maincpu(MK1_CPU(config, "maincpu", 2_MHz_XTAL / 2));
|
||||
maincpu.set_addrmap(AS_PROGRAM, &mk1forth_state::ucode_map);
|
||||
maincpu.set_addrmap(AS_DATA, &mk1forth_state::data_map);
|
||||
maincpu.set_addrmap(mk1_cpu_device::AS_STACK, &mk1forth_state::stack_map);
|
||||
|
||||
INPUT_MERGER_ANY_HIGH(config, "uartirq").output_handler().set_inputline("maincpu", mk1_cpu_device::IRQ_LINE);
|
||||
|
||||
i8251_device &uart(I8251(config, "uart", 2.4576_MHz_XTAL));
|
||||
uart.rxrdy_handler().set("uartirq", FUNC(input_merger_device::in_w<0>));
|
||||
uart.txrdy_handler().set("uartirq", FUNC(input_merger_device::in_w<1>));
|
||||
uart.txd_handler().set("rs232", FUNC(rs232_port_device::write_txd));
|
||||
uart.dtr_handler().set("rs232", FUNC(rs232_port_device::write_rts));
|
||||
uart.dtr_handler().append("rs232", FUNC(rs232_port_device::write_dtr));
|
||||
uart.write_cts(0);
|
||||
|
||||
clock_device &clock(CLOCK(config, "baudclock", 2.4576_MHz_XTAL / 16));
|
||||
clock.signal_handler().set("uart", FUNC(i8251_device::write_rxc));
|
||||
clock.signal_handler().append("uart", FUNC(i8251_device::write_txc));
|
||||
|
||||
rs232_port_device &rs232(RS232_PORT(config, "rs232", default_rs232_devices, "terminal"));
|
||||
rs232.rxd_handler().set("uart", FUNC(i8251_device::write_rxd));
|
||||
rs232.cts_handler().set("uart", FUNC(i8251_device::write_dsr));
|
||||
}
|
||||
|
||||
ROM_START(mk1forth)
|
||||
ROM_REGION(0x2000, "ucode", 0)
|
||||
ROM_LOAD("rom.bin", 0x0000, 0x2000, CRC(d55d8a09) SHA1(8043a276826d40aa35b4ab8c94b99e3862fb62e7))
|
||||
|
||||
ROM_REGION(0x2000, "forth", 0)
|
||||
ROM_LOAD("forth.bin", 0x0000, 0x2000, CRC(8b2863fa) SHA1(cc1d0662f10cf767f3f2581270ca2b4711121ffc))
|
||||
ROM_END
|
||||
|
||||
COMP(2003, mk1forth, 0, 0, mk1forth, 0, mk1forth_state, empty_init, "Andrew Holme", "Mark 1 FORTH Computer", MACHINE_NO_SOUND_HW | MACHINE_SUPPORTS_SAVE)
|
@ -23353,6 +23353,9 @@ mjsenpu
|
||||
@source:mjsister.cpp
|
||||
mjsister // (c) 1986 Toaplan
|
||||
|
||||
@source:mk1forth.cpp
|
||||
mk1forth // (c) 2003 Andrew Holme
|
||||
|
||||
@source:mk14.cpp
|
||||
mk14 //
|
||||
|
||||
|
@ -615,6 +615,7 @@ minitel_2_rpic.cpp
|
||||
mips.cpp
|
||||
mits680b.cpp
|
||||
miuchiz.cpp
|
||||
mk1forth.cpp
|
||||
mk14.cpp
|
||||
mk85.cpp
|
||||
mk90.cpp
|
||||
|
@ -36,6 +36,7 @@ using util::BIT;
|
||||
#include "cpu/cop400/cop420ds.h"
|
||||
#include "cpu/cop400/cop424ds.h"
|
||||
#include "cpu/cop400/cop444ds.h"
|
||||
#include "cpu/cops1/cops1d.h"
|
||||
#include "cpu/cosmac/cosdasm.h"
|
||||
#include "cpu/cp1610/1610dasm.h"
|
||||
#include "cpu/cr16b/cr16bdasm.h"
|
||||
@ -112,7 +113,7 @@ using util::BIT;
|
||||
#include "cpu/minx/minxd.h"
|
||||
#include "cpu/mips/mips3dsm.h"
|
||||
#include "cpu/mips/mips1dsm.h"
|
||||
#include "cpu/cops1/cops1d.h"
|
||||
#include "cpu/mk1/mk1dasm.h"
|
||||
#include "cpu/mn1880/mn1880d.h"
|
||||
#include "cpu/mn10200/mn102dis.h"
|
||||
#include "cpu/msm65x2/msm65x2d.h"
|
||||
@ -498,6 +499,7 @@ static const dasm_table_entry dasm_table[] =
|
||||
{ "mips1le", le, 0, []() -> util::disasm_interface * { return new mips1_disassembler; } },
|
||||
{ "mips3be", be, 0, []() -> util::disasm_interface * { return new mips3_disassembler; } },
|
||||
{ "mips3le", le, 0, []() -> util::disasm_interface * { return new mips3_disassembler; } },
|
||||
{ "mk1", le, 0, []() -> util::disasm_interface * { return new mk1_disassembler; } },
|
||||
{ "mm5799", le, 0, []() -> util::disasm_interface * { return new mm5799_disassembler; } },
|
||||
{ "mm76", le, 0, []() -> util::disasm_interface * { return new mm76_disassembler; } },
|
||||
{ "mn10200", le, 0, []() -> util::disasm_interface * { return new mn10200_disassembler; } },
|
||||
|
Loading…
Reference in New Issue
Block a user