summaryrefslogtreecommitdiffstatshomepage
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/devices/cpu/ht1130/ht1130.cpp1059
-rw-r--r--src/devices/cpu/ht1130/ht1130.h126
-rw-r--r--src/devices/cpu/ht1130/ht1130d.cpp668
-rw-r--r--src/devices/cpu/ht1130/ht1130d.h22
-rw-r--r--src/mame/handheld/hh_ht11xx.cpp108
-rw-r--r--src/mame/mame.lst3
-rw-r--r--src/tools/unidasm.cpp2
7 files changed, 1988 insertions, 0 deletions
diff --git a/src/devices/cpu/ht1130/ht1130.cpp b/src/devices/cpu/ht1130/ht1130.cpp
new file mode 100644
index 00000000000..c31ae697aac
--- /dev/null
+++ b/src/devices/cpu/ht1130/ht1130.cpp
@@ -0,0 +1,1059 @@
+// license:BSD-3-Clause
+// copyright-holders:David Haywood
+
+/*
+
+ TODO:
+ Interrupts (not used by brke23p2)
+ Sound (needs internal frequency ROM data?)
+ IO wake-up from HALT etc.
+ 1 machine cycle (eg. a 1 byte opcode) takes 4 system clock cycles (from OSC pins).
+ The timer rate can be configured with a mask option (system clock / 2^n), n=0-13 (except 6 for some reason). So, timer rate can be faster or slower than machine cycle rate.
+
+*/
+
+#include "emu.h"
+#include "ht1130.h"
+#include "ht1130d.h"
+
+#define LOG_UNHANDLED_OPS (1U << 1)
+#define LOG_UNHANDLED_SOUND_OPS (1U << 2)
+
+#define VERBOSE (LOG_UNHANDLED_OPS)
+#include "logmacro.h"
+
+// device type definitions
+DEFINE_DEVICE_TYPE(HT1130, ht1130_device, "ht1130", "Holtek HT1130")
+DEFINE_DEVICE_TYPE(HT1190, ht1190_device, "ht1190", "Holtek HT1190")
+
+
+
+ht1130_device::ht1130_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, u32 clock, address_map_constructor data)
+ : cpu_device(mconfig, type, tag, owner, clock)
+ , m_tempram(*this, "tempram")
+ , m_displayram(*this, "displayram")
+ , m_space_config("program", ENDIANNESS_LITTLE, 8, 12, 0, address_map_constructor(FUNC(ht1130_device::internal_map), this))
+ , m_data_config("data", ENDIANNESS_LITTLE, 8, 8, 0, data)
+ , m_pc(0)
+ , m_port_in_pm(*this, 0xff)
+ , m_port_in_ps(*this, 0xff)
+ , m_port_in_pp(*this, 0xff)
+ , m_port_out_pa(*this)
+ , m_display_data_out(*this)
+{
+}
+
+ht1130_device::ht1130_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock)
+ : ht1130_device(mconfig, HT1130, tag, owner, clock, address_map_constructor(FUNC(ht1130_device::internal_data_map), this))
+{
+}
+
+ht1190_device::ht1190_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock)
+ : ht1130_device(mconfig, HT1190, tag, owner, clock, address_map_constructor(FUNC(ht1190_device::internal_data_map_ht1190), this))
+{
+}
+
+std::unique_ptr<util::disasm_interface> ht1130_device::create_disassembler()
+{
+ return std::make_unique<ht1130_disassembler>();
+}
+
+device_memory_interface::space_config_vector ht1130_device::memory_space_config() const
+{
+ return space_config_vector{
+ std::make_pair(AS_PROGRAM, &m_space_config),
+ std::make_pair(AS_DATA, &m_data_config)
+ };
+}
+
+void ht1130_device::internal_map(address_map &map)
+{
+ map(0x000, 0xfff).rom();
+}
+
+inline void ht1130_device::tempram_w(offs_t offset, u8 data)
+{
+ m_tempram[offset] = data & 0xf;
+}
+
+inline void ht1130_device::displayram_w(offs_t offset, u8 data)
+{
+ m_displayram[offset] = data & 0xf;
+
+ // there might be a better way to do this
+ m_display_data_out(offset, m_displayram[offset]);
+}
+
+inline void ht1130_device::setreg(u8 which, u8 data)
+{
+ m_regs[which] = data & 0xf;
+}
+
+inline u8 ht1130_device::getreg(u8 which)
+{
+ return m_regs[which] & 0xf;
+}
+
+inline void ht1130_device::setacc(u8 data)
+{
+ m_acc = data & 0xf;
+}
+
+inline u8 ht1130_device::getacc()
+{
+ return m_acc & 0xf;
+}
+
+inline u8 ht1130_device::getcarry()
+{
+ return m_carry & 1;
+}
+
+inline void ht1130_device::setcarry()
+{
+ m_carry = 1;
+}
+
+inline void ht1130_device::clearcarry()
+{
+ m_carry = 0;
+}
+
+inline void ht1130_device::settimer(u8 data)
+{
+ m_timer = data;
+}
+
+inline void ht1130_device::settimer_upper(u8 data)
+{
+ m_timer = (m_timer & 0xf) | (data & 0xf) << 4;
+}
+
+inline void ht1130_device::settimer_lower(u8 data)
+{
+ m_timer = (m_timer & 0xf0) | (data & 0xf);
+}
+
+inline u8 ht1130_device::gettimer_upper()
+{
+ return (m_timer >> 4) & 0xf;
+}
+
+inline u8 ht1130_device::gettimer_lower()
+{
+ return m_timer & 0xf;
+}
+
+inline u8 ht1130_device::getr1r0()
+{
+ return (getreg(1)<<4) | getreg(0);
+}
+
+inline u8 ht1130_device::getr1r0_data()
+{
+ const u8 dataaddress = getr1r0();
+ return m_data.read_byte(dataaddress);
+}
+
+inline void ht1130_device::setr1r0_data(u8 data)
+{
+ const u8 dataaddress = getr1r0();
+ m_data.write_byte(dataaddress, data);
+}
+
+inline u8 ht1130_device::getr3r2()
+{
+ return (getreg(3)<<4) | getreg(2);
+}
+
+inline u8 ht1130_device::getr3r2_data()
+{
+ const u8 dataaddress = getr3r2();
+ return m_data.read_byte(dataaddress);
+}
+
+inline void ht1130_device::setr3r2_data(u8 data)
+{
+ const u8 dataaddress = getr3r2();
+ m_data.write_byte(dataaddress, data);
+}
+
+
+void ht1130_device::internal_data_map(address_map &map)
+{
+ map(0x00, 0x7f).ram().w(FUNC(ht1130_device::tempram_w)).share(m_tempram);
+ map(0xe0, 0xff).ram().w(FUNC(ht1130_device::displayram_w)).share(m_displayram);
+}
+
+void ht1190_device::internal_data_map_ht1190(address_map &map)
+{
+ map(0x00, 0x9f).ram().w(FUNC(ht1190_device::tempram_w)).share(m_tempram);
+ map(0xb0, 0xff).ram().w(FUNC(ht1190_device::displayram_w)).share(m_displayram);
+}
+
+void ht1130_device::device_start()
+{
+ space(AS_PROGRAM).specific(m_space);
+ space(AS_DATA).specific(m_data);
+
+ set_icountptr(m_icount);
+
+ state_add(HT1130_PC, "PC", m_pc);
+ state_add(STATE_GENPC, "GENPC", m_pc).noshow();
+ state_add(STATE_GENPCBASE, "CURPC", m_pc).noshow();
+
+ state_add(HT1130_ACC, "ACC", m_acc);
+
+ for (int i = 0; i < 5; i++)
+ state_add(HT1130_R0 + i, string_format("R%d", i).c_str(), m_regs[i]);
+
+ state_add(HT1130_TIMER_EN, "TIMER_EN", m_timer_en);
+ state_add(HT1130_TIMER, "TIMER", m_timer);
+
+
+ save_item(NAME(m_pc));
+ save_item(NAME(m_icount));
+
+ save_item(NAME(m_regs));
+ save_item(NAME(m_acc));
+ save_item(NAME(m_carry));
+ save_item(NAME(m_irqen));
+ save_item(NAME(m_timer_en));
+ save_item(NAME(m_inhalt));
+ save_item(NAME(m_timerover));
+ save_item(NAME(m_timer));
+ save_item(NAME(m_stackaddr));
+ save_item(NAME(m_stackcarry));
+}
+
+void ht1130_device::device_reset()
+{
+ m_pc = 0;
+ std::fill(std::begin(m_regs), std::end(m_regs), 0);
+ m_acc = 0;
+ m_carry = 0;
+ m_irqen = 0;
+ m_timer_en = 0;
+ m_inhalt = 0;
+ m_timerover = 0;
+ m_timer = 0;
+ m_stackaddr = 0;
+ m_stackcarry = 0;
+}
+
+void ht1130_device::cycle()
+{
+ m_icount--;
+ if (m_timer_en)
+ m_timer++;
+}
+
+u8 ht1130_device::fetch()
+{
+ cycle();
+ return m_space.read_byte(m_pc++);
+}
+
+void ht1130_device::do_op()
+{
+ const u8 inst = fetch();
+
+ switch (inst)
+ {
+ case 0b00001000: // ADC A,[R1R0] : Add data memory contents and carry to the accumulator
+ {
+ const u8 data = getr1r0_data();
+
+ u8 acc = getacc();
+ acc = acc + data + getcarry();
+ if (acc & 0x10)
+ setcarry();
+ else
+ clearcarry();
+ setacc(acc);
+
+ return;
+ }
+
+ case 0b00001001: // ADD A,[R1R0] : Add data memory contents to the accumulator
+ {
+ const u8 data = getr1r0_data();
+
+ u8 acc = getacc();
+ acc = acc + data;
+ if (acc & 0x10)
+ setcarry();
+ else
+ clearcarry();
+ setacc(acc);
+
+ return;
+ }
+
+ case 0b00011010: // AND A,[R1R0] : Logical AND accumulator with data memory
+ {
+ const u8 data = getr1r0_data();
+ const u8 acc = getacc();
+ setacc(acc & data);
+ return;
+ }
+
+ case 0b00011101: // AND [R1R0],A : Logical AND data memory with accumulator
+ {
+ const u8 data = getr1r0_data();
+ const u8 acc = getacc();
+ setr1r0_data(data & acc);
+ return;
+ }
+
+ case 0b00101010: // CLC : Clear carry flag
+ {
+ clearcarry();
+ return;
+ }
+
+ case 0b00110110: // DAA : Decimal-Adjust accumulator
+ {
+ u8 acc = getacc();
+ if (acc > 9 || getcarry())
+ {
+ acc = (acc + 6) & 0xf;
+ setcarry();
+ setacc(acc);
+ }
+ return;
+ }
+
+ case 0b00111111: // DEC A : Decrement accumulator
+ {
+ const u8 acc = getacc();
+ setacc(acc-1);
+ return;
+ }
+
+ case 0b00001101: // DEC [R1R0] : Decrement data memory
+ {
+ const u8 data = getr1r0_data();
+ setr1r0_data(data-1);
+ return;
+ }
+
+ case 0b00001111: // DEC [R3R2] : Decrement data memory
+ {
+ const u8 data = getr3r2_data();
+ setr3r2_data(data-1);
+ return;
+ }
+
+ case 0b00101101: // DI : Disable interrupt
+ {
+ m_irqen = 0;
+ return;
+ }
+
+ case 0b00101100: // EI : Enable interrupt
+ {
+ m_irqen = 1;
+ return;
+ }
+
+ case 0b00110010: // IN A,PM : Input port to accumulator (doesn't exist on HT1190, does on HT1130?)
+ {
+ const u8 data = m_port_in_pm() & 0xf;
+ setacc(data);
+ return;
+ }
+
+ case 0b00110011: // IN A,PS : Input port to accumulator
+ {
+ const u8 data = m_port_in_ps() & 0xf;
+ setacc(data);
+ return;
+ }
+
+ case 0b00110100: // IN A,PP : Input port to accumulator
+ {
+ const u8 data = m_port_in_pp() & 0xf;
+ setacc(data);
+ return;
+ }
+
+ case 0b00110001: // INC A : Increment accumulator
+ {
+ const u8 temp = getacc();
+ setacc(temp+1);
+ return;
+ }
+
+ case 0b00001100: // INC [R1R0] : Increment data memory
+ {
+ const u8 data = getr1r0_data();
+ setr1r0_data(data+1);
+ return;
+ }
+
+ case 0b00001110: // INC [R3R2] : Increment data memory
+ {
+ const u8 data = getr3r2_data();
+ setr3r2_data(data+1);
+ return;
+ }
+
+ case 0b00000100: // MOV A,[R1R0] : Move data memory to accumulator
+ {
+ const u8 data = getr1r0_data();
+ setacc(data);
+ return;
+ }
+
+ case 0b00000110: // MOV A,[R3R2] : Move data memory to accumulator
+ {
+ const u8 data = getr3r2_data();
+ setacc(data);
+ return;
+ }
+
+ case 0b00000101: // MOV [R1R0],A : Move accumulator to data memory
+ {
+ const u8 acc = getacc();
+ setr1r0_data(acc);
+ return;
+ }
+
+ case 0b00000111: // MOV [R3R2],A : Move accumulator to data memory
+ {
+ const u8 acc = getacc();
+ setr3r2_data(acc);
+ return;
+ }
+
+ case 0b00111110: // NOP : No operation
+ {
+ // nothing
+ return;
+ }
+
+ case 0b00011100: // OR A,[R1R0] : Logical OR accumulator with data memory
+ {
+ const u8 data = getr1r0_data();
+ const u8 acc = getacc();
+ setacc(acc | data);
+ return;
+ }
+
+ case 0b00011111: // OR [R1R0],A : Logically OR data memory with accumulator
+ {
+ const u8 data = getr1r0_data();
+ const u8 acc = getacc();
+ setr1r0_data(data | acc);
+ return;
+ }
+
+ case 0b00110000: // OUT PA,A : Output accumulator data to port A
+ {
+ const u8 data = getacc();
+ m_port_out_pa(data);
+ return;
+ }
+
+ case 0b01001110: // READ MR0A : Read ROM code of current page to M(R1,R0) and ACC
+ {
+ cycle();
+ const u16 dataddress = (m_pc & 0xf00) | (getacc() << 4) | (getreg(4));
+ const u8 data = m_space.read_byte(dataddress);
+ setr1r0_data((data >> 4) & 0xf);
+ setacc(data & 0xf);
+ return;
+ }
+
+ case 0b01001100: // READ R4A : Read ROM code of current page to R4 and accumulator
+ {
+ cycle();
+ const u16 dataddress = (m_pc & 0xf00) | (getacc() << 4) | (getr1r0_data());
+ const u8 data = m_space.read_byte(dataddress);
+ setreg(4, (data >> 4) & 0xf);
+ setacc(data & 0xf);
+ return;
+ }
+
+ case 0b01001111: // READF MR0A : Read ROM Code of page F to M(R1,R0) and ACC
+ {
+ cycle();
+ const u16 dataddress = 0xf00 | (getacc() << 4) | (getreg(4));
+ const u8 data = m_space.read_byte(dataddress);
+ setr1r0_data((data >> 4) & 0xf);
+ setacc(data & 0xf);
+ return;
+ }
+
+ case 0b01001101: // READF R4A : Read ROM code of page F to R4 and accumulator
+ {
+ cycle();
+ const u16 dataddress = 0xf00 | (getacc() << 4) | (getr1r0_data());
+ const u8 data = m_space.read_byte(dataddress);
+ setreg(4, (data >> 4) & 0xf);
+ setacc(data & 0xf);
+ return;
+ }
+
+ case 0b00000001: // RL A : Rotate accumulator left
+ {
+ u8 acc = getacc();
+ const u8 oldr3 = (acc & 8)>>3;
+ if (oldr3)
+ setcarry();
+ else
+ clearcarry();
+ acc = (acc << 1) | oldr3;
+ setacc(acc);
+ return;
+ }
+
+ case 0b00000011: // RLC A : Rotate accumulator left through carry
+ {
+ const u8 oldcarry = getcarry();
+ u8 acc = getacc();
+ if (acc & 0x8)
+ setcarry();
+ else
+ clearcarry();
+ acc = (acc << 1) | oldcarry;
+ setacc(acc);
+ return;
+ }
+
+ case 0b00000000: // RR A : Rotate accumulator right
+ {
+ u8 acc = getacc();
+ const u8 oldr0 = acc & 1;
+ if (oldr0)
+ setcarry();
+ else
+ clearcarry();
+ acc = (acc >> 1) | oldr0 << 3;
+ setacc(acc);
+ return;
+ }
+
+ case 0b00000010: // RRC A : Rotate accumulator right through carry
+ {
+ const u8 oldcarry = getcarry();
+ u8 acc = getacc();
+ if (acc & 0x1)
+ setcarry();
+ else
+ clearcarry();
+ acc = (acc >> 1) | (oldcarry << 3);
+ setacc(acc);
+ return;
+ }
+
+ case 0b00001010: // SBC A,[R1R0] : Subtract data memory contents and carry from ACC
+ {
+ const u8 data = getr1r0_data();
+
+ u8 acc = getacc();
+ acc = acc + (0xf - data) + getcarry();
+ if (acc & 0x10)
+ setcarry();
+ else
+ clearcarry();
+ setacc(acc);
+
+ return;
+ }
+
+ case 0b00101011: // STC : Set carry flag
+ {
+ setcarry();
+ return;
+ }
+
+ case 0b00001011: // SUB A,[R1R0] : Subtract data memory contents from accumulator
+ {
+ const u8 data = getr1r0_data();
+
+ u8 acc = getacc();
+ acc = acc + (0xf - data) + 1;
+ if (acc & 0x10)
+ setcarry();
+ else
+ clearcarry();
+ setacc(acc);
+
+ return;
+ }
+
+ case 0b00111001: // TIMER OFF : Set timer to stop counting
+ {
+ m_timer_en = 0;
+ return;
+ }
+
+ case 0b00111000: // TIMER ON : Set timer to start counting
+ {
+ m_timer_en = 1;
+ return;
+ }
+
+ case 0b00011011: // XOR A,[R1R0] : Logical XOR accumulator with data memory
+ {
+ const u8 data = getr1r0_data();
+ const u8 acc = getacc();
+ setacc(acc ^ data);
+ return;
+ }
+
+ case 0b00011110: // XOR [R1R0],A : Logical XOR data memory with accumulator
+ {
+ const u8 data = getr1r0_data();
+ const u8 acc = getacc();
+ setr1r0_data(data ^ acc);
+ return;
+ }
+
+ //// Opcodes with XH Immediates
+
+ case 0b01000000: // (with 4-bit immediate) : ADD A,XH : Add immediate data to the accumulator
+ {
+ const u8 operand = fetch() & 0x0f;
+ u8 acc = getacc();
+ acc += operand;
+ if (acc & 0x10)
+ setcarry();
+ else
+ clearcarry();
+ setacc(acc);
+ return;
+ }
+
+ case 0b01000010: // (with 4-bit immediate) : AND A,XH : Logical AND immediate data to accumulator
+ {
+ const u8 operand = fetch() & 0x0f;
+ const u8 acc = getacc();
+ setacc(acc & operand);
+ return;
+ }
+
+ case 0b01000110: // (with 4-bit immediate) : MOV R4,XH : Move immediate data to R4
+ {
+ const u8 operand = fetch() & 0x0f;
+ setreg(4, operand);
+ return;
+ }
+
+ case 0b01000100: // (with 4-bit immediate) : OR A,XH : Logical OR immediate data to accumulator
+ {
+ const u8 operand = fetch() & 0x0f;
+ const u8 acc = getacc();
+ setacc(acc | operand);
+ return;
+ }
+
+ case 0b01000001: // (with 4-bit immediate) : SUB A,XH : Subtract immediate data from accumulator0
+ {
+ const u8 operand = fetch() & 0x0f;
+ u8 acc = getacc();
+ acc += (0xf-operand) + 1;
+ if (acc & 0x10)
+ setcarry();
+ else
+ clearcarry();
+ setacc(acc);
+ return;
+ }
+
+ case 0b01000011: // (with 4-bit immediate) : XOR A,XH : Logical XOR immediate data to accumulator
+ {
+ const u8 operand = fetch() & 0x0f;
+ const u8 acc = getacc();
+ setacc(acc ^ operand);
+ return;
+ }
+
+ //case 0b0111dddd: // MOV A,XH : Move immediate data to accumulator
+ case 0b01110000: case 0b01110001: case 0b01110010: case 0b01110011:
+ case 0b01110100: case 0b01110101: case 0b01110110: case 0b01110111:
+ case 0b01111000: case 0b01111001: case 0b01111010: case 0b01111011:
+ case 0b01111100: case 0b01111101: case 0b01111110: case 0b01111111:
+ {
+ const u8 operand = inst & 0x0f;
+ setacc(operand);
+ return;
+ }
+
+ // Ops using registers
+
+ // case 0b0001nnn1: DEC Rn : Decrement register (R0-R4)
+ case 0b00010001: case 0b00010011: case 0b00010101: case 0b00010111:
+ case 0b00011001:
+ {
+ const u8 reg = (inst & 0x0e) >> 1;
+ const u8 temp = getreg(reg);
+ setreg(reg, temp-1);
+ return;
+ }
+
+ // case 0b0001nnn0: INC Rn : Increment register
+ case 0b00010000: case 0b00010010: case 0b00010100: case 0b00010110:
+ case 0b00011000:
+ {
+ const u8 reg = (inst & 0x0e) >> 1;
+ const u8 temp = getreg(reg);
+ setreg(reg, temp+1);
+ return;
+ }
+
+ // case 0b0010nnn1: MOV A,Rn : Move register to accumulator
+ case 0b00100001: case 0b00100011: case 0b00100101: case 0b00100111:
+ case 0b00101001:
+ {
+ const u8 reg = (inst & 0x0e) >> 1;
+ const u8 temp = getreg(reg);
+ setacc(temp);
+ return;
+ }
+
+ //case 0b0010nnn0: MOV Rn,A : Move accumulator to register
+ case 0b00100000: case 0b00100010: case 0b00100100: case 0b00100110:
+ case 0b00101000:
+ {
+ const u8 reg = (inst & 0x0e) >> 1;
+ const u8 temp = getacc();
+ setreg(reg, temp);
+ return;
+ }
+
+ ///////////////////////////////////////////////////////////////////////////////////////
+ // 2 reg Move ops
+ ///////////////////////////////////////////////////////////////////////////////////////
+
+ //case 0b0101dddd: // (with 4-bit immediate) : MOV R1R0,XXH : Move immediate data to R1 and R0
+ case 0b01010000: case 0b01010001: case 0b01010010: case 0b01010011:
+ case 0b01010100: case 0b01010101: case 0b01010110: case 0b01010111:
+ case 0b01011000: case 0b01011001: case 0b01011010: case 0b01011011:
+ case 0b01011100: case 0b01011101: case 0b01011110: case 0b01011111:
+ {
+ const u8 operand = fetch();
+ setreg(1, operand & 0xf);
+ setreg(0, inst & 0xf);
+ return;
+ }
+
+ //case 0b0110dddd: // (with 4-bit immediate) : MOV R3R2,XXH : Move immediate data to R3 and R2
+ case 0b01100000: case 0b01100001: case 0b01100010: case 0b01100011:
+ case 0b01100100: case 0b01100101: case 0b01100110: case 0b01100111:
+ case 0b01101000: case 0b01101001: case 0b01101010: case 0b01101011:
+ case 0b01101100: case 0b01101101: case 0b01101110: case 0b01101111:
+ {
+ const u8 operand = fetch();
+ setreg(3, operand & 0xf);
+ setreg(2, inst & 0xf);
+ return;
+ }
+
+ ///////////////////////////////////////////////////////////////////////////////////////
+ // Jump / Call Ops (full address)
+ ///////////////////////////////////////////////////////////////////////////////////////
+
+ // case 0b1111aaaa: // (with 8-bit immediate) : CALL address : Subroutine call
+ case 0b11110000: case 0b11110001: case 0b11110010: case 0b11110011:
+ case 0b11110100: case 0b11110101: case 0b11110110: case 0b11110111:
+ case 0b11111000: case 0b11111001: case 0b11111010: case 0b11111011:
+ case 0b11111100: case 0b11111101: case 0b11111110: case 0b11111111:
+ {
+ const u8 operand = fetch();
+ const uint16_t fulladdr = ((inst & 0x0f) << 8) | operand;
+ m_stackaddr = m_pc;
+ m_pc = fulladdr;
+ return;
+ }
+
+ // case 0b1110aaaa: // (with 8-bit immediate) : JMP address : Direct jump
+ case 0b11100000: case 0b11100001: case 0b11100010: case 0b11100011:
+ case 0b11100100: case 0b11100101: case 0b11100110: case 0b11100111:
+ case 0b11101000: case 0b11101001: case 0b11101010: case 0b11101011:
+ case 0b11101100: case 0b11101101: case 0b11101110: case 0b11101111:
+ {
+ const u8 operand = fetch();
+ const uint16_t fulladdr = ((inst & 0x0f) << 8) | operand;
+ m_pc = fulladdr;
+ return;
+ }
+
+ ///////////////////////////////////////////////////////////////////////////////////////
+ // Jump / Call Ops (partial address)
+ ///////////////////////////////////////////////////////////////////////////////////////
+
+ // case 0b11000aaa: (with 8-bit immediate) : JC address : Jump if carry is set
+ case 0b11000000: case 0b11000001: case 0b11000010: case 0b11000011:
+ case 0b11000100: case 0b11000101: case 0b11000110: case 0b11000111:
+ {
+ const u8 operand = fetch();
+ const uint16_t fulladdr = ((inst & 0x07) << 8) | operand | (m_pc & 0x800);
+
+ if (getcarry())
+ m_pc = fulladdr;
+
+ return;
+ }
+
+ // case 0b11001aaa: (with 8-bit immediate) : JNC address : Jump if carry is not set
+ case 0b11001000: case 0b11001001: case 0b11001010: case 0b11001011:
+ case 0b11001100: case 0b11001101: case 0b11001110: case 0b11001111:
+ {
+ const u8 operand = fetch();
+ const uint16_t fulladdr = ((inst & 0x07) << 8) | operand | (m_pc & 0x800);
+
+ if (!getcarry())
+ m_pc = fulladdr;
+
+ return;
+ }
+
+ // case 0b10111aaa: (with 8-bit immediate) : JNZ A,address : Jump if accumulator is not 0
+ case 0b10111000: case 0b10111001: case 0b10111010: case 0b10111011:
+ case 0b10111100: case 0b10111101: case 0b10111110: case 0b10111111:
+ {
+ const u8 operand = fetch();
+ const uint16_t fulladdr = ((inst & 0x07) << 8) | operand | (m_pc & 0x800);
+
+ if (getacc())
+ m_pc = fulladdr;
+
+ return;
+ }
+
+ // case 0b10100aaa: (with 8-bit immediate) : JNZ R0,address : Jump if register is not 0
+ case 0b10100000: case 0b10100001: case 0b10100010: case 0b10100011:
+ case 0b10100100: case 0b10100101: case 0b10100110: case 0b10100111:
+ {
+ const u8 operand = fetch();
+ const uint16_t fulladdr = ((inst & 0x07) << 8) | operand | (m_pc & 0x800);
+
+ if (getreg(0))
+ m_pc = fulladdr;
+
+ return;
+ }
+
+ // case 0b10101aaa: (with 8-bit immediate) : JNZ R1,address : Jump if register is not 0
+ case 0b10101000: case 0b10101001: case 0b10101010: case 0b10101011:
+ case 0b10101100: case 0b10101101: case 0b10101110: case 0b10101111:
+ {
+ const u8 operand = fetch();
+ const uint16_t fulladdr = ((inst & 0x07) << 8) | operand | (m_pc & 0x800);
+
+ if (getreg(1))
+ m_pc = fulladdr;
+
+ return;
+ }
+
+ // case 0b11011aaa: (with 8-bit immediate) : JNZ R4,address : Jump if register is not 0
+ case 0b11011000: case 0b11011001: case 0b11011010: case 0b11011011:
+ case 0b11011100: case 0b11011101: case 0b11011110: case 0b11011111:
+ {
+ const u8 operand = fetch();
+ const uint16_t fulladdr = ((inst & 0x07) << 8) | operand | (m_pc & 0x800);
+
+ if (getreg(4))
+ m_pc = fulladdr;
+
+ return;
+ }
+
+ // case 0b11010aaa: (with 8-bit immediate) : JTMR address : Jump if time-out
+ case 0b11010000: case 0b11010001: case 0b11010010: case 0b11010011:
+ case 0b11010100: case 0b11010101: case 0b11010110: case 0b11010111:
+ {
+ const u8 operand = fetch();
+ const uint16_t fulladdr = ((inst & 0x07) << 8) | operand | (m_pc & 0x800);
+
+ if (m_timerover)
+ {
+ m_pc = fulladdr;
+ m_timerover = 0;
+ }
+ return;
+ }
+
+ // case 0b10110aaa: (with 8-bit immediate) : JZ A,address : Jump if accumulator is 0
+ case 0b10110000: case 0b10110001: case 0b10110010: case 0b10110011:
+ case 0b10110100: case 0b10110101: case 0b10110110: case 0b10110111:
+ {
+ const u8 operand = fetch();
+ const uint16_t fulladdr = ((inst & 0x07) << 8) | operand | (m_pc & 0x800);
+
+ if (!getacc())
+ m_pc = fulladdr;
+
+ return;
+ }
+
+ //case 0b100nnaaa: // (with 8-bit immediate) : JAn address : Jump if accumulator bit n is set
+ case 0b10000000: case 0b10000001: case 0b10000010: case 0b10000011:
+ case 0b10000100: case 0b10000101: case 0b10000110: case 0b10000111:
+ case 0b10001000: case 0b10001001: case 0b10001010: case 0b10001011:
+ case 0b10001100: case 0b10001101: case 0b10001110: case 0b10001111:
+ case 0b10010000: case 0b10010001: case 0b10010010: case 0b10010011:
+ case 0b10010100: case 0b10010101: case 0b10010110: case 0b10010111:
+ case 0b10011000: case 0b10011001: case 0b10011010: case 0b10011011:
+ case 0b10011100: case 0b10011101: case 0b10011110: case 0b10011111:
+ {
+ const u8 operand = fetch();
+ const uint16_t fulladdr = ((inst & 0x07) << 8) | operand | (m_pc & 0x800);
+ const uint8_t bit = (inst & 0x18) >> 3;
+
+ if (BIT(getacc(),bit))
+ m_pc = fulladdr;
+
+ return;
+ }
+
+ ///////////////////////////////////////////////////////////////////////////////////////
+ // Return Ops
+ ///////////////////////////////////////////////////////////////////////////////////////
+
+ case 0b00101110: // RET : Return from subroutine or interrupt
+ {
+ m_pc = m_stackaddr;
+ return;
+ }
+
+ case 0b00101111: // RETI : Return from interrupt subroutine
+ {
+ m_pc = m_stackaddr;
+ if (m_stackcarry)
+ setcarry();
+ else
+ clearcarry();
+ return;
+ }
+
+ ///////////////////////////////////////////////////////////////////////////////////////
+ // Timer Ops
+ ///////////////////////////////////////////////////////////////////////////////////////
+
+ case 0b00111011: // MOV A,TMRH : Move timer high nibble to accumulator
+ {
+ const u8 data = gettimer_upper();
+ setacc(data);
+ return;
+ }
+
+ case 0b00111010: // MOV A,TMRL : Move timer low nibble to accumulator
+ {
+ const u8 data = gettimer_lower();
+ setacc(data);
+ return;
+ }
+
+ case 0b00111101: // MOV TMRH,A : Move accumulator to timer high nibble
+ {
+ const u8 acc = getacc();
+ settimer_upper(acc);
+ return;
+ }
+
+ case 0b00111100: // MOV TMRL,A : Move accumulator to timer low nibble
+ {
+ const u8 acc = getacc();
+ settimer_lower(acc);
+ return;
+ }
+
+ case 0b01000111: // (with 8-bit immediate) : TIMER XXH : Set immediate data to timer counter
+ {
+ const u8 operand = fetch();
+ settimer(operand);
+ return;
+ }
+
+ ///////////////////////////////////////////////////////////////////////////////////////
+ // SOUND Ops (unimplemented)
+ ///////////////////////////////////////////////////////////////////////////////////////
+
+ case 0b01001011: // SOUND A : Activate SOUND channel with accumulator
+ {
+ LOGMASKED(LOG_UNHANDLED_SOUND_OPS, "SOUND A");
+ return;
+ }
+
+ case 0b01001001: // SOUND LOOP : Turn on sound repeat cycle
+ {
+ LOGMASKED(LOG_UNHANDLED_SOUND_OPS, "SOUND LOOP");
+ return;
+ }
+
+ case 0b01001010: // SOUND OFF : Turn off sound
+ {
+ LOGMASKED(LOG_UNHANDLED_SOUND_OPS, "SOUND OFF");
+ return;
+ }
+
+ case 0b01001000: // SOUND ONE : Turn on sound 1 cycle
+ {
+ LOGMASKED(LOG_UNHANDLED_SOUND_OPS, "SOUND ONE");
+ return;
+ }
+
+ case 0b01000101: // (with 4 bit immediate) : SOUND n : Activate SOUND channel n
+ {
+ u8 operand = fetch() & 0x0f;
+ LOGMASKED(LOG_UNHANDLED_SOUND_OPS, "SOUND %d", operand);
+ return;
+ }
+
+ ///////////////////////////////////////////////////////////////////////////////////////
+ // other Ops
+ ///////////////////////////////////////////////////////////////////////////////////////
+
+ case 0b00110111: // (with 00111110) : HALT : Halt system clock
+ {
+ const u8 operand = fetch();
+ if (operand == 0b00111110) // this is a 'NOP' must HALT always be followed by NOP to work?
+ {
+ m_inhalt = 1;
+ }
+ else
+ {
+ LOGMASKED(LOG_UNHANDLED_OPS, "<ill HALT %02x %02x>", inst, operand);
+ }
+ return;
+ }
+
+ default:
+ {
+ LOGMASKED(LOG_UNHANDLED_OPS, "<ill %02x>", inst);
+ return;
+ }
+ }
+}
+
+void ht1130_device::execute_run()
+{
+ while (m_icount > 0)
+ {
+ debugger_instruction_hook(m_pc);
+
+ if (!m_inhalt)
+ {
+ do_op();
+ }
+ else
+ {
+ m_icount--;
+ }
+
+ if (m_timer & 0x100)
+ {
+ m_timer -= 0x100;
+ m_timerover = 1; // can also generate an interrupt
+ }
+ }
+}
+
+void ht1130_device::execute_set_input(int inputnum, int state)
+{
+}
diff --git a/src/devices/cpu/ht1130/ht1130.h b/src/devices/cpu/ht1130/ht1130.h
new file mode 100644
index 00000000000..a0b90ef39dc
--- /dev/null
+++ b/src/devices/cpu/ht1130/ht1130.h
@@ -0,0 +1,126 @@
+// license:BSD-3-Clause
+// copyright-holders:David Haywood
+
+#ifndef MAME_CPU_HT1130_HT1130_H
+#define MAME_CPU_HT1130_HT1130_H
+
+#pragma once
+
+
+class ht1130_device : public cpu_device
+{
+public:
+ ht1130_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock);
+
+ enum {
+ HT1130_PC,
+ HT1130_R0,
+ HT1130_R1,
+ HT1130_R2,
+ HT1130_R3,
+ HT1130_R4,
+ HT1130_ACC,
+ HT1130_TIMER_EN,
+ HT1130_TIMER,
+ };
+
+ auto pm_in_cb() { return m_port_in_pm.bind(); }
+ auto ps_in_cb() { return m_port_in_ps.bind(); }
+ auto pp_in_cb() { return m_port_in_pp.bind(); }
+
+ auto pa_out_cb() { return m_port_out_pa.bind(); }
+
+ auto display_data_out_cb() { return m_display_data_out.bind(); }
+
+protected:
+ ht1130_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, u32 clock, address_map_constructor data);
+
+
+ virtual void device_start() override;
+ virtual void device_reset() override;
+
+ virtual void execute_run() override;
+ virtual void execute_set_input(int inputnum, int state) override;
+ virtual uint32_t execute_max_cycles() const noexcept override { return 2; }
+
+ virtual std::unique_ptr<util::disasm_interface> create_disassembler() override;
+
+ virtual space_config_vector memory_space_config() const override;
+
+ void internal_map(address_map &map);
+ void internal_data_map(address_map &map);
+
+ void tempram_w(offs_t offset, u8 data);
+ void displayram_w(offs_t offset, u8 data);
+
+ required_shared_ptr<u8> m_tempram;
+ required_shared_ptr<u8> m_displayram;
+
+private:
+ address_space_config m_space_config;
+ address_space_config m_data_config;
+
+ memory_access<8, 0, 0, ENDIANNESS_LITTLE>::specific m_space;
+ memory_access<8, 0, 0, ENDIANNESS_LITTLE>::specific m_data;
+
+ void setreg(u8 which, u8 data);
+ u8 getreg(u8 which);
+ void setacc(u8 data);
+ u8 getacc();
+ void setcarry();
+ void clearcarry();
+ u8 getcarry();
+ u8 getr1r0();
+ u8 getr1r0_data();
+ void setr1r0_data(u8 data);
+ u8 getr3r2();
+ u8 getr3r2_data();
+ void setr3r2_data(u8 data);
+ void settimer(u8 data);
+ void settimer_upper(u8 data);
+ void settimer_lower(u8 data);
+ u8 gettimer_upper();
+ u8 gettimer_lower();
+
+ void cycle();
+ u8 fetch();
+ void do_op();
+
+ u32 m_pc;
+ s32 m_icount;
+
+ u8 m_regs[5];
+ u8 m_acc;
+ u8 m_carry;
+ u8 m_irqen;
+ u8 m_timer_en;
+ u8 m_inhalt;
+ u8 m_timerover;
+ u16 m_timer;
+
+ u16 m_stackaddr;
+ u8 m_stackcarry;
+
+ devcb_read8 m_port_in_pm;
+ devcb_read8 m_port_in_ps;
+ devcb_read8 m_port_in_pp;
+
+ devcb_write8 m_port_out_pa;
+
+ devcb_write8 m_display_data_out;
+};
+
+class ht1190_device : public ht1130_device
+{
+public:
+ ht1190_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock);
+
+private:
+ void internal_data_map_ht1190(address_map &map);
+};
+
+
+DECLARE_DEVICE_TYPE(HT1130, ht1130_device)
+DECLARE_DEVICE_TYPE(HT1190, ht1190_device)
+
+#endif // MAME_CPU_HT1130_HT1130_H
diff --git a/src/devices/cpu/ht1130/ht1130d.cpp b/src/devices/cpu/ht1130/ht1130d.cpp
new file mode 100644
index 00000000000..8467fdf1476
--- /dev/null
+++ b/src/devices/cpu/ht1130/ht1130d.cpp
@@ -0,0 +1,668 @@
+// license:BSD-3-Clause
+// copyright-holders:David Haywood
+
+#include "emu.h"
+#include "ht1130d.h"
+
+ht1130_disassembler::ht1130_disassembler()
+ : util::disasm_interface()
+{
+}
+
+u32 ht1130_disassembler::opcode_alignment() const
+{
+ return 1;
+}
+
+offs_t ht1130_disassembler::disassemble(std::ostream &stream, offs_t pc, const ht1130_disassembler::data_buffer &opcodes, const ht1130_disassembler::data_buffer &params)
+{
+ u8 inst = opcodes.r8(pc);
+
+ switch (inst)
+ {
+ case 0b00001000: // ADC A,[R1R0] : Add data memory contents and carry to the accumulator
+ {
+ stream << "ADC A,[R1R0]";
+ return 1;
+ }
+
+ case 0b00001001: // ADD A,[R1R0] : Add data memory contents to the accumulator
+ {
+ stream << "ADD A,[R1R0]";
+ return 1;
+ }
+
+ case 0b00011010: // AND A,[R1R0] : Logical AND accumulator with data memory
+ {
+ stream << "AND A,[R1R0]";
+ return 1;
+ }
+
+ case 0b00011101: // AND [R1R0],A : Logical AND data memory with accumulator
+ {
+ stream << "AND [R1R0],A";
+ return 1;
+ }
+
+ case 0b00101010: // CLC : Clear carry flag
+ {
+ stream << "CLC";
+ return 1;
+ }
+
+ case 0b00110110: // DAA : Decimal-Adjust accumulator
+ {
+ stream << "DAA";
+ return 1;
+ }
+
+ case 0b00111111: // DEC A : Decrement accumulator
+ {
+ stream << "DEC A";
+ return 1;
+ }
+
+ case 0b00001101: // DEC [R1R0] : Decrement data memory
+ {
+ stream << "DEC [R1R0]";
+ return 1;
+ }
+
+ case 0b00001111: // DEC [R3R2] : Decrement data memory
+ {
+ stream << "DEC [R3R2]";
+ return 1;
+ }
+
+ case 0b00101101: // DI : Disable interrupt
+ {
+ stream << "DI";
+ return 1;
+ }
+
+ case 0b00101100: // EI : Enable interrupt
+ {
+ stream << "EI";
+ return 1;
+ }
+
+ case 0b00110010: // IN A,PM : Input port to accumulator
+ {
+ stream << "IN A,PM";
+ return 1;
+ }
+
+ case 0b00110011: // IN A,PS : Input port to accumulator
+ {
+ stream << "IN A,PS";
+ return 1;
+ }
+
+ case 0b00110100: // IN A,PP : Input port to accumulator
+ {
+ stream << "IN A,PP";
+ return 1;
+ }
+
+ case 0b00110001: // INC A : Increment accumulator
+ {
+ stream << "INC A";
+ return 1;
+ }
+
+ case 0b00001100: // INC [R1R0] : Increment data memory
+ {
+ stream << "INC [R1R0]";
+ return 1;
+ }
+
+ case 0b00001110: // INC [R3R2] : Increment data memory
+ {
+ stream << "INC [R3R2]";
+ return 1;
+ }
+
+ case 0b00111011: // MOV A,TMRH : Move timer high nibble to accumulator
+ {
+ stream << "MOV A,TMRH";
+ return 1;
+ }
+
+ case 0b00111010: // MOV A,TMRL : Move timer low nibble to accumulator
+ {
+ stream << "MOV A,TMRL";
+ return 1;
+ }
+
+ case 0b00000100: // MOV A,[R1R0] : Move data memory to accumulator
+ {
+ stream << "MOV A,[R1R0]";
+ return 1;
+ }
+
+ case 0b00000110: // MOV A,[R3R2] : Move data memory to accumulator
+ {
+ stream << "MOV A,[R3R2]";
+ return 1;
+ }
+
+ case 0b00111101: // MOV TMRH,A : Move accumulator to timer high nibble
+ {
+ stream << "MOV TMRH,A";
+ return 1;
+ }
+
+ case 0b00111100: // MOV TMRL,A : Move accumulator to timer low nibble
+ {
+ stream << "MOV TMRL,A";
+ return 1;
+ }
+
+ case 0b00000101: // MOV [R1R0],A : Move accumulator to data memory
+ {
+ stream << "MOV [R1R0],A";
+ return 1;
+ }
+
+ case 0b00000111: // MOV [R3R2],A : Move accumulator to data memory
+ {
+ stream << "MOV [R3R2],A";
+ return 1;
+ }
+
+ case 0b00111110: // NOP : No operation
+ {
+ stream << "NOP";
+ return 1;
+ }
+
+ case 0b00011100: // OR A,[R1R0] : Logical OR accumulator with data memory
+ {
+ stream << "OR A,[R1R0]";
+ return 1;
+ }
+
+ case 0b00011111: // OR [R1R0],A : Logically OR data memory with accumulator
+ {
+ stream << "OR [R1R0],A";
+ return 1;
+ }
+
+ case 0b00110000: // OUT PA,A : Output accumulator data to port A
+ {
+ stream << "OUT PA,A";
+ return 1;
+ }
+
+ case 0b01001110: // READ MR0A : Read ROM code of current page to M(R1,R0) and ACC
+ {
+ stream << "READ MR0A";
+ return 1;
+ }
+
+ case 0b01001100: // READ R4A : Read ROM code of current page to R4 and accumulator
+ {
+ stream << "READ R4A";
+ return 1;
+ }
+
+ case 0b01001111: // READF MR0A : Read ROM Code of page F to M(R1,R0) and ACC
+ {
+ stream << "READF MR0A";
+ return 1;
+ }
+
+ case 0b01001101: // READF R4A : Read ROM code of page F to R4 and accumulator
+ {
+ stream << "READF R4A";
+ return 1;
+ }
+
+ case 0b00101110: // RET : Return from subroutine or interrupt
+ {
+ stream << "RET";
+ return 1;
+ }
+
+ case 0b00101111: // RETI : Return from interrupt subroutine
+ {
+ stream << "RETI";
+ return 1;
+ }
+
+ case 0b00000001: // RL A : Rotate accumulator left
+ {
+ stream << "RL A";
+ return 1;
+ }
+
+ case 0b00000011: // RLC A : Rotate accumulator left through carry
+ {
+ stream << "RLC A";
+ return 1;
+ }
+
+ case 0b00000000: // RR A : Rotate accumulator right
+ {
+ stream << "RR A";
+ return 1;
+ }
+
+ case 0b00000010: // RRC A : Rotate accumulator right through carry
+ {
+ stream << "RRC A";
+ return 1;
+ }
+
+ case 0b00001010: // SBC A,[R1R0] : Subtract data memory contents and carry from ACC
+ {
+ stream << "SBC A,[R1R0]";
+ return 1;
+ }
+
+ case 0b01001011: // SOUND A : Activate SOUND channel with accumulator
+ {
+ stream << "SOUND A";
+ return 1;
+ }
+
+ case 0b01001001: // SOUND LOOP : Turn on sound repeat cycle
+ {
+ stream << "SOUND LOOP";
+ return 1;
+ }
+
+ case 0b01001010: // SOUND OFF : Turn off sound
+ {
+ stream << "SOUND OFF";
+ return 1;
+ }
+
+ case 0b01001000: // SOUND ONE : Turn on sound 1 cycle
+ {
+ stream << "SOUND ONE";
+ return 1;
+ }
+
+ case 0b00101011: // STC : Set carry flag
+ {
+ stream << "STC";
+ return 1;
+ }
+
+ case 0b00001011: // SUB A,[R1R0] : Subtract data memory contents from accumulator
+ {
+ stream << "SUB A,[R1R0]";
+ return 1;
+ }
+
+ case 0b00111001: // TIMER OFF : Set timer to stop counting
+ {
+ stream << "TIMER OFF";
+ return 1;
+ }
+
+ case 0b00111000: // TIMER ON : Set timer to start counting
+ {
+ stream << "TIMER ON";
+ return 1;
+ }
+
+ case 0b00011011: // XOR A,[R1R0] : Logical XOR accumulator with data memory
+ {
+ stream << "XOR A,[R1R0]";
+ return 1;
+ }
+
+ case 0b00011110: // XOR [R1R0],A : Logical XOR data memory with accumulator
+ {
+ stream << "XOR [R1R0],A";
+ return 1;
+ }
+
+ //// Opcodes with XH Immediates
+
+ case 0b01000000: // (with 4-bit immediate) : ADD A,XH : Add immediate data to the accumulator
+ {
+ u8 operand = opcodes.r8(pc + 1);
+ if (!(operand & 0xf0))
+ {
+ util::stream_format(stream, "ADD A,0x%02x", operand);
+ }
+ else
+ {
+ util::stream_format(stream, "<ill %02x %02x>", inst, operand);
+ }
+ return 2;
+ }
+
+ case 0b01000010: // (with 4-bit immediate) : AND A,XH : Logical AND immediate data to accumulator
+ {
+ u8 operand = opcodes.r8(pc + 1);
+ if (!(operand & 0xf0))
+ {
+ util::stream_format(stream, "AND A,0x%02x", operand);
+ }
+ else
+ {
+ util::stream_format(stream, "<ill %02x %02x>", inst, operand);
+ }
+ return 2;
+ }
+
+ case 0b01000110: // (with 4-bit immediate) : MOV R4,XH : Move immediate data to R4
+ {
+ u8 operand = opcodes.r8(pc + 1);
+ if (!(operand & 0xf0))
+ {
+ util::stream_format(stream, "MOV R4,0x%02x", operand);
+ }
+ else
+ {
+ util::stream_format(stream, "<ill %02x %02x>", inst, operand);
+ }
+ return 2;
+ }
+
+ case 0b01000100: // (with 4-bit immediate) : OR A,XH : Logical OR immediate data to accumulator
+ {
+ u8 operand = opcodes.r8(pc + 1);
+ if (!(operand & 0xf0))
+ {
+ util::stream_format(stream, "OR A,0x%02x", operand);
+ }
+ else
+ {
+ util::stream_format(stream, "<ill %02x %02x>", inst, operand);
+ }
+ return 2;
+ }
+
+ case 0b01000001: // (with 4-bit immediate) : SUB A,XH : Subtract immediate data from accumulator0
+ {
+ u8 operand = opcodes.r8(pc + 1);
+ if (!(operand & 0xf0))
+ {
+ util::stream_format(stream, "SUB A,0x%02x", operand);
+ }
+ else
+ {
+ util::stream_format(stream, "<ill %02x %02x>", inst, operand);
+ }
+ return 2;
+ }
+
+ case 0b01000011: // (with 4-bit immediate) : XOR A,XH : Logical XOR immediate data to accumulator
+ {
+ u8 operand = opcodes.r8(pc + 1);
+ if (!(operand & 0xf0))
+ {
+ util::stream_format(stream, "XOR A,0x%02x", operand);
+ }
+ else
+ {
+ util::stream_format(stream, "<ill %02x %02x>", inst, operand);
+ }
+ return 2;
+ }
+
+ //case 0b0111dddd: // MOV A,XH : Move immediate data to accumulator
+ case 0b01110000: case 0b01110001: case 0b01110010: case 0b01110011:
+ case 0b01110100: case 0b01110101: case 0b01110110: case 0b01110111:
+ case 0b01111000: case 0b01111001: case 0b01111010: case 0b01111011:
+ case 0b01111100: case 0b01111101: case 0b01111110: case 0b01111111:
+ {
+ u8 operand = inst & 0x0f;
+ util::stream_format(stream, "MOV A,0x%02x", operand);
+ return 1;
+ }
+
+ // Ops using registers
+
+ // case 0b0001nnn1: DEC Rn : Decrement register (R0-R4)
+ case 0b00010001: case 0b00010011: case 0b00010101: case 0b00010111:
+ case 0b00011001:
+ {
+ u8 reg = (inst & 0x0e) >> 1;
+ util::stream_format(stream, "DEC R%d", reg);
+ return 1;
+ }
+
+ // case 0b0001nnn0: INC Rn : Increment register
+ case 0b00010000: case 0b00010010: case 0b00010100: case 0b00010110:
+ case 0b00011000:
+ {
+ u8 reg = (inst & 0x0e) >> 1;
+ util::stream_format(stream, "INC R%d", reg);
+ return 1;
+ }
+
+ // case 0b0010nnn1: MOV A,Rn : Move register to accumulator
+ case 0b00100001: case 0b00100011: case 0b00100101: case 0b00100111:
+ case 0b00101001:
+ {
+ u8 reg = (inst & 0x0e) >> 1;
+ util::stream_format(stream, "MOV A,R%d", reg);
+ return 1;
+ }
+
+ //case 0b0010nnn0: MOV Rn,A : Move accumulator to register
+ case 0b00100000: case 0b00100010: case 0b00100100: case 0b00100110:
+ case 0b00101000:
+ {
+ u8 reg = (inst & 0x0e) >> 1;
+ util::stream_format(stream, "MOV R%d, A", reg);
+ return 1;
+ }
+
+ // dual move ops
+
+ //case 0b0101dddd: // (with 4-bit immediate) : MOV R1R0,XXH : Move immediate data to R1 and R0
+ case 0b01010000: case 0b01010001: case 0b01010010: case 0b01010011:
+ case 0b01010100: case 0b01010101: case 0b01010110: case 0b01010111:
+ case 0b01011000: case 0b01011001: case 0b01011010: case 0b01011011:
+ case 0b01011100: case 0b01011101: case 0b01011110: case 0b01011111:
+ {
+ u8 operand = opcodes.r8(pc + 1);
+ if (!(operand & 0xf0))
+ {
+ uint8_t fulldata = (inst & 0x0f) | (operand << 4);
+ util::stream_format(stream, "MOV R1R0,0x%02x", fulldata);
+ }
+ else
+ {
+ util::stream_format(stream, "<ill %02x %02x>", inst, operand);
+ }
+ return 2;
+ }
+
+ //case 0b0110dddd: // (with 4-bit immediate) : MOV R3R2,XXH : Move immediate data to R3 and R2
+ case 0b01100000: case 0b01100001: case 0b01100010: case 0b01100011:
+ case 0b01100100: case 0b01100101: case 0b01100110: case 0b01100111:
+ case 0b01101000: case 0b01101001: case 0b01101010: case 0b01101011:
+ case 0b01101100: case 0b01101101: case 0b01101110: case 0b01101111:
+ {
+ u8 operand = opcodes.r8(pc + 1);
+ if (!(operand & 0xf0))
+ {
+ uint8_t fulldata = (inst & 0x0f) | (operand << 4);
+ util::stream_format(stream, "MOV R3R2,0x%02x", fulldata);
+ }
+ else
+ {
+ util::stream_format(stream, "<ill %02x %02x>", inst, operand);
+ }
+ return 2;
+ }
+
+ // Jump / Call opcodes with full addresses
+
+ // case 0b1111aaaa: // (with 8-bit immediate) : CALL address : Subroutine call
+ case 0b11110000: case 0b11110001: case 0b11110010: case 0b11110011:
+ case 0b11110100: case 0b11110101: case 0b11110110: case 0b11110111:
+ case 0b11111000: case 0b11111001: case 0b11111010: case 0b11111011:
+ case 0b11111100: case 0b11111101: case 0b11111110: case 0b11111111:
+ {
+ u8 operand = opcodes.r8(pc + 1);
+ uint16_t fulladdr = ((inst & 0x0f) << 8) | operand;
+ util::stream_format(stream, "CALL %03x", fulladdr);
+ return 2;
+ }
+
+ // case 0b1110aaaa: // (with 8-bit immediate) : JMP address : Direct jump
+ case 0b11100000: case 0b11100001: case 0b11100010: case 0b11100011:
+ case 0b11100100: case 0b11100101: case 0b11100110: case 0b11100111:
+ case 0b11101000: case 0b11101001: case 0b11101010: case 0b11101011:
+ case 0b11101100: case 0b11101101: case 0b11101110: case 0b11101111:
+ {
+ u8 operand = opcodes.r8(pc + 1);
+ uint16_t fulladdr = ((inst & 0x0f) << 8) | operand;
+ util::stream_format(stream, "JMP %03x", fulladdr);
+ return 2;
+ }
+
+ // Jump / Call opcodes with partial address
+
+ // case 0b11000aaa: (with 8-bit immediate) : JC address : Jump if carry is set
+ case 0b11000000: case 0b11000001: case 0b11000010: case 0b11000011:
+ case 0b11000100: case 0b11000101: case 0b11000110: case 0b11000111:
+ {
+ u8 operand = opcodes.r8(pc + 1);
+ uint16_t fulladdr = ((inst & 0x07) << 8) | operand | (pc & 0x800);
+ util::stream_format(stream, "JC %03x", fulladdr);
+ return 2;
+ }
+
+ // case 0b11001aaa: (with 8-bit immediate) : JNC address : Jump if carry is not set
+ case 0b11001000: case 0b11001001: case 0b11001010: case 0b11001011:
+ case 0b11001100: case 0b11001101: case 0b11001110: case 0b11001111:
+ {
+ u8 operand = opcodes.r8(pc + 1);
+ uint16_t fulladdr = ((inst & 0x07) << 8) | operand | (pc & 0x800);
+ util::stream_format(stream, "JNC %03x", fulladdr);
+ return 2;
+ }
+
+ // case 0b10111aaa: (with 8-bit immediate) : JNZ A,address : Jump if accumulator is not 0
+ case 0b10111000: case 0b10111001: case 0b10111010: case 0b10111011:
+ case 0b10111100: case 0b10111101: case 0b10111110: case 0b10111111:
+ {
+ u8 operand = opcodes.r8(pc + 1);
+ uint16_t fulladdr = ((inst & 0x07) << 8) | operand | (pc & 0x800);
+ util::stream_format(stream, "JNZ A,%03x", fulladdr);
+ return 2;
+ }
+
+ // case 0b10100aaa: (with 8-bit immediate) : JNZ R0,address : Jump if register is not 0
+ case 0b10100000: case 0b10100001: case 0b10100010: case 0b10100011:
+ case 0b10100100: case 0b10100101: case 0b10100110: case 0b10100111:
+ {
+ u8 operand = opcodes.r8(pc + 1);
+ uint16_t fulladdr = ((inst & 0x07) << 8) | operand | (pc & 0x800);
+ util::stream_format(stream, "JNZ R0,%03x", fulladdr);
+ return 2;
+ }
+
+ // case 0b10101aaa: (with 8-bit immediate) : JNZ R1,address : Jump if register is not 0
+ case 0b10101000: case 0b10101001: case 0b10101010: case 0b10101011:
+ case 0b10101100: case 0b10101101: case 0b10101110: case 0b10101111:
+ {
+ u8 operand = opcodes.r8(pc + 1);
+ uint16_t fulladdr = ((inst & 0x07) << 8) | operand | (pc & 0x800);
+ util::stream_format(stream, "JNZ R1,%03x", fulladdr);
+ return 2;
+ }
+
+ // case 0b11011aaa: (with 8-bit immediate) : JNZ R4,address : Jump if register is not 0
+ case 0b11011000: case 0b11011001: case 0b11011010: case 0b11011011:
+ case 0b11011100: case 0b11011101: case 0b11011110: case 0b11011111:
+ {
+ u8 operand = opcodes.r8(pc + 1);
+ uint16_t fulladdr = ((inst & 0x07) << 8) | operand | (pc & 0x800);
+ util::stream_format(stream, "JNZ R4,%03x", fulladdr);
+ return 2;
+ }
+
+ // case 0b11010aaa: (with 8-bit immediate) : JTMR address : Jump if time-out
+ case 0b11010000: case 0b11010001: case 0b11010010: case 0b11010011:
+ case 0b11010100: case 0b11010101: case 0b11010110: case 0b11010111:
+ {
+ u8 operand = opcodes.r8(pc + 1);
+ uint16_t fulladdr = ((inst & 0x07) << 8) | operand | (pc & 0x800);
+ util::stream_format(stream, "JTMR %03x", fulladdr);
+ return 2;
+ }
+
+ // case 0b10110aaa: (with 8-bit immediate) : JZ A,address : Jump if accumulator is 0
+ case 0b10110000: case 0b10110001: case 0b10110010: case 0b10110011:
+ case 0b10110100: case 0b10110101: case 0b10110110: case 0b10110111:
+ {
+ u8 operand = opcodes.r8(pc + 1);
+ uint16_t fulladdr = ((inst & 0x07) << 8) | operand | (pc & 0x800);
+ util::stream_format(stream, "JZ A,%03x", fulladdr);
+ return 2;
+ }
+
+ //case 0b100nnaaa: // (with 8-bit immediate) : JAn address : Jump if accumulator bit n is set
+ case 0b10000000: case 0b10000001: case 0b10000010: case 0b10000011:
+ case 0b10000100: case 0b10000101: case 0b10000110: case 0b10000111:
+ case 0b10001000: case 0b10001001: case 0b10001010: case 0b10001011:
+ case 0b10001100: case 0b10001101: case 0b10001110: case 0b10001111:
+ case 0b10010000: case 0b10010001: case 0b10010010: case 0b10010011:
+ case 0b10010100: case 0b10010101: case 0b10010110: case 0b10010111:
+ case 0b10011000: case 0b10011001: case 0b10011010: case 0b10011011:
+ case 0b10011100: case 0b10011101: case 0b10011110: case 0b10011111:
+ {
+ u8 operand = opcodes.r8(pc + 1);
+ uint16_t fulladdr = ((inst & 0x07) << 8) | operand | (pc & 0x800);
+ uint8_t bit = (inst & 0x18)>>3;
+ util::stream_format(stream, "JA%d %03x", bit, fulladdr);
+ return 2;
+ }
+
+ // other Ops
+ case 0b01000101: // (with 4 bit immediate) : SOUND n : Activate SOUND channel n
+ {
+ u8 operand = opcodes.r8(pc + 1);
+ if (!(operand & 0xf0))
+ {
+ util::stream_format(stream, "SOUND %d", operand);
+ }
+ else
+ {
+ util::stream_format(stream, "<ill %02x %02x>", inst, operand);
+ }
+ return 2;
+ }
+
+
+ case 0b00110111: // (with 00111110) : HALT Halt system clock
+ {
+ u8 operand = opcodes.r8(pc + 1);
+ if (operand == 0b00111110)
+ {
+ stream << "HALT";
+ }
+ else
+ {
+ util::stream_format(stream, "<ill %02x %02x>", inst, operand);
+ }
+ return 2;
+ }
+
+ case 0b01000111: // (with 8-bit immediate) : TIMER XXH : Set immediate data to timer counter
+ {
+ u8 operand = opcodes.r8(pc + 1);
+ util::stream_format(stream, "TIMER %02x", operand);
+ return 2;
+ }
+
+ default:
+ {
+ util::stream_format(stream, "<ill %02x>", inst);
+ return 1;
+ }
+ }
+
+ return 1;
+}
diff --git a/src/devices/cpu/ht1130/ht1130d.h b/src/devices/cpu/ht1130/ht1130d.h
new file mode 100644
index 00000000000..96d08d249a3
--- /dev/null
+++ b/src/devices/cpu/ht1130/ht1130d.h
@@ -0,0 +1,22 @@
+// license:BSD-3-Clause
+// copyright-holders:David Haywood
+
+#ifndef MAME_CPU_HT1130_HT1130D_H
+#define MAME_CPU_HT1130_HT1130D_H
+
+#pragma once
+
+
+class ht1130_disassembler : public util::disasm_interface
+{
+public:
+ // construction/destruction
+ ht1130_disassembler();
+
+protected:
+ // disassembler overrides
+ virtual u32 opcode_alignment() const override;
+ virtual offs_t disassemble(std::ostream &stream, offs_t pc, const data_buffer &opcodes, const data_buffer &params) override;
+};
+
+#endif // MAME_CPU_HT1130_HT1130D_H
diff --git a/src/mame/handheld/hh_ht11xx.cpp b/src/mame/handheld/hh_ht11xx.cpp
new file mode 100644
index 00000000000..5c3101552cd
--- /dev/null
+++ b/src/mame/handheld/hh_ht11xx.cpp
@@ -0,0 +1,108 @@
+// license:BSD-3-Clause
+// copyright-holders:David Haywood
+
+
+#include "emu.h"
+
+#include "cpu/ht1130/ht1130.h"
+
+#include "screen.h"
+#include "speaker.h"
+
+#define VERBOSE (0)
+#include "logmacro.h"
+
+namespace {
+
+class ht11xx_brickgame_state : public driver_device
+{
+public:
+ ht11xx_brickgame_state(const machine_config &mconfig, device_type type, const char *tag) :
+ driver_device(mconfig, type, tag),
+ m_maincpu(*this, "maincpu"),
+ m_out_x(*this, "seg%u_%u", 0U, 0U),
+ m_in1(*this, "IN1"),
+ m_in2(*this, "IN2")
+ { }
+
+ void ht11xx_brickgame(machine_config &config);
+
+protected:
+ virtual void machine_start() override;
+ virtual void machine_reset() override;
+
+private:
+ void mcfg_svg_screen(machine_config &config, u16 width, u16 height, const char *tag = "screen");
+
+ void display_data_w(offs_t offset, u8 data);
+
+ required_device<ht1130_device> m_maincpu;
+ output_finder<256, 4> m_out_x;
+ required_ioport m_in1;
+ required_ioport m_in2;
+};
+
+void ht11xx_brickgame_state::machine_start()
+{
+ m_out_x.resolve();
+}
+
+void ht11xx_brickgame_state::machine_reset()
+{
+}
+
+static INPUT_PORTS_START( ht11xx_brickgame )
+ PORT_START("IN1")
+ PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_START1 )
+ PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_BUTTON5 ) PORT_NAME("Mute")
+ PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_BUTTON6 ) PORT_NAME("Power")
+
+ PORT_START("IN2") // not a joystick, but buttons are used for directional inputs in the snake game etc.
+ PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_BUTTON1 ) PORT_NAME("Up / Rotate")
+ PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_BUTTON2 ) PORT_NAME("Down / Drop")
+ PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_BUTTON3 ) PORT_NAME("Right")
+ PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_BUTTON4 ) PORT_NAME("Left")
+INPUT_PORTS_END
+
+
+void ht11xx_brickgame_state::display_data_w(offs_t offset, u8 data)
+{
+ for (int i = 0; i < 4; i++)
+ {
+ m_out_x[offset+0xb0][i] = (data >> i) & 1;
+ }
+}
+
+void ht11xx_brickgame_state::mcfg_svg_screen(machine_config &config, u16 width, u16 height, const char *tag)
+{
+ screen_device &screen(SCREEN(config, tag, SCREEN_TYPE_SVG));
+ screen.set_refresh_hz(60);
+ screen.set_size(width, height);
+ screen.set_visarea_full();
+}
+
+void ht11xx_brickgame_state::ht11xx_brickgame(machine_config &config)
+{
+ HT1190(config, m_maincpu, 1000000/8); // frequency?
+ m_maincpu->display_data_out_cb().set(FUNC(ht11xx_brickgame_state::display_data_w));
+
+ m_maincpu->ps_in_cb().set_ioport(m_in1);
+ m_maincpu->pp_in_cb().set_ioport(m_in2);
+
+ SPEAKER(config, "speaker").front_center();
+
+ mcfg_svg_screen(config, 768, 1080);
+}
+
+ROM_START( brke23p2 )
+ ROM_REGION( 0x1000, "maincpu", 0 )
+ ROM_LOAD( "e23plusmarkii96in1.bin", 0x0000, 0x1000, CRC(8045fac4) SHA1(a36213309e6add31f31e4248f02f17de9914a5c1) ) // visual decap
+
+ ROM_REGION( 139648, "screen", 0)
+ ROM_LOAD( "brke23p2.svg", 0, 139648, CRC(f29ea936) SHA1(d80a37aa4e5647b31454a6d6de5a59c770ef0322) )
+ROM_END
+
+} // anonymous namespace
+
+// some other dieshots have 1996 on them, it is also possible the software is from Holtek
+CONS( 1993, brke23p2, 0, 0, ht11xx_brickgame, ht11xx_brickgame, ht11xx_brickgame_state, empty_init, "E-Star", "Brick Game 96 in 1 (E-23 Plus Mark II)", MACHINE_NOT_WORKING | MACHINE_NO_SOUND ) // needs SVG screen
diff --git a/src/mame/mame.lst b/src/mame/mame.lst
index 520d1956d6c..66f144dac6e 100644
--- a/src/mame/mame.lst
+++ b/src/mame/mame.lst
@@ -18807,6 +18807,9 @@ vinvader // VTech
wantgman // Actronics / Hanzawa
zackman // Bandai
+@source:handheld/hh_ht11xx.cpp
+brke23p2
+
@source:handheld/hh_melps4.cpp
cfrogger // Coleco
gjungler // Gakken
diff --git a/src/tools/unidasm.cpp b/src/tools/unidasm.cpp
index 5a2efb167a2..d84393be307 100644
--- a/src/tools/unidasm.cpp
+++ b/src/tools/unidasm.cpp
@@ -70,6 +70,7 @@ using util::BIT;
#include "cpu/hp2100/hp2100d.h"
#include "cpu/hpc/hpcdasm.h"
#include "cpu/hphybrid/hphybrid_dasm.h"
+#include "cpu/ht1130/ht1130d.h"
#include "cpu/i386/i386dasm.h"
#include "cpu/i8008/8008dasm.h"
#include "cpu/i8085/8085dasm.h"
@@ -475,6 +476,7 @@ static const dasm_table_entry dasm_table[] =
{ "hpc16083", le, 0, []() -> util::disasm_interface * { return new hpc16083_disassembler; } },
{ "hpc16164", le, 0, []() -> util::disasm_interface * { return new hpc16164_disassembler; } },
{ "hyperstone", be, 0, []() -> util::disasm_interface * { return new hyperstone_disassembler(&hyperstone_unidasm); } },
+ { "ht1130", le, 0, []() -> util::disasm_interface * { return new ht1130_disassembler; } },
{ "i4004", le, 0, []() -> util::disasm_interface * { return new i4004_disassembler; } },
{ "i4040", le, 0, []() -> util::disasm_interface * { return new i4040_disassembler; } },
{ "i8008", le, 0, []() -> util::disasm_interface * { return new i8008_disassembler; } },