diff --git a/scripts/src/cpu.lua b/scripts/src/cpu.lua index 0ca72a6b3d7..db6c6853f4d 100644 --- a/scripts/src/cpu.lua +++ b/scripts/src/cpu.lua @@ -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 diff --git a/scripts/target/mame/mess.lua b/scripts/target/mame/mess.lua index c8d4d01e1f6..13fae035ace 100644 --- a/scripts/target/mame/mess.lua +++ b/scripts/target/mame/mess.lua @@ -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", diff --git a/src/devices/cpu/mk1/mk1.cpp b/src/devices/cpu/mk1/mk1.cpp new file mode 100644 index 00000000000..87fd7a73129 --- /dev/null +++ b/src/devices/cpu/mk1/mk1.cpp @@ -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 mk1_cpu_device::create_disassembler() +{ + return std::make_unique(); +} + +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(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(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; + } +} diff --git a/src/devices/cpu/mk1/mk1.h b/src/devices/cpu/mk1/mk1.h new file mode 100644 index 00000000000..dad3f3ab9f7 --- /dev/null +++ b/src/devices/cpu/mk1/mk1.h @@ -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 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 diff --git a/src/devices/cpu/mk1/mk1dasm.cpp b/src/devices/cpu/mk1/mk1dasm.cpp new file mode 100644 index 00000000000..4e6c4157dec --- /dev/null +++ b/src/devices/cpu/mk1/mk1dasm.cpp @@ -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; + } +} diff --git a/src/devices/cpu/mk1/mk1dasm.h b/src/devices/cpu/mk1/mk1dasm.h new file mode 100644 index 00000000000..6bf6f03c231 --- /dev/null +++ b/src/devices/cpu/mk1/mk1dasm.h @@ -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 diff --git a/src/mame/drivers/mk1forth.cpp b/src/mame/drivers/mk1forth.cpp new file mode 100644 index 00000000000..652b2bd0e46 --- /dev/null +++ b/src/mame/drivers/mk1forth.cpp @@ -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) diff --git a/src/mame/mame.lst b/src/mame/mame.lst index ffdee2746d8..e5c63603f09 100644 --- a/src/mame/mame.lst +++ b/src/mame/mame.lst @@ -23353,6 +23353,9 @@ mjsenpu @source:mjsister.cpp mjsister // (c) 1986 Toaplan +@source:mk1forth.cpp +mk1forth // (c) 2003 Andrew Holme + @source:mk14.cpp mk14 // diff --git a/src/mame/mess.flt b/src/mame/mess.flt index 801939fa2fe..2a869da53e9 100644 --- a/src/mame/mess.flt +++ b/src/mame/mess.flt @@ -615,6 +615,7 @@ minitel_2_rpic.cpp mips.cpp mits680b.cpp miuchiz.cpp +mk1forth.cpp mk14.cpp mk85.cpp mk90.cpp diff --git a/src/tools/unidasm.cpp b/src/tools/unidasm.cpp index 7bf28fec35c..6f41438eb14 100644 --- a/src/tools/unidasm.cpp +++ b/src/tools/unidasm.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; } },