summaryrefslogtreecommitdiffstatshomepage
diff options
context:
space:
mode:
-rw-r--r--hash/cassvisn_cart.xml215
-rw-r--r--scripts/src/cpu.lua17
-rw-r--r--src/devices/cpu/upd777/upd777.cpp1262
-rw-r--r--src/devices/cpu/upd777/upd777.h165
-rw-r--r--src/devices/cpu/upd777/upd777dasm.cpp504
-rw-r--r--src/devices/cpu/upd777/upd777dasm.h34
-rw-r--r--src/mame/epoch/cassvisn.cpp112
-rw-r--r--src/mame/mame.lst3
-rw-r--r--src/tools/unidasm.cpp2
9 files changed, 2314 insertions, 0 deletions
diff --git a/hash/cassvisn_cart.xml b/hash/cassvisn_cart.xml
new file mode 100644
index 00000000000..9fbb64e17bb
--- /dev/null
+++ b/hash/cassvisn_cart.xml
@@ -0,0 +1,215 @@
+<?xml version="1.0"?>
+<!DOCTYPE softwarelist SYSTEM "softwarelist.dtd">
+<!--
+license:CC0-1.0
+-->
+<!--
+The following games exist for this platform
+
+1 Yosaku
+2 Baseball
+3 Galaxian
+4 Big Sports 12
+5 Battle Vader
+6 PakPak Monster
+7 New Baseball
+8 Monster Mansion
+9 Astro Command
+10 Grand Champion*
+11 Monster Block
+12 Elevator Panic
+
+* = not dumped
+
+-->
+
+<!--
+Note on PRG size (from CPU documentation)
+
+Physically, 128 * 15 * 12 = 23,040 bits
+Logically, 127 * 15 * 12 = 22,860 bits (as final word of each page is inaccessible by the CPU)
+
+the ROM files below store 12 bit in a 16-bit word with 4 bits unused, so 128 * 15 * (12+4)
+-->
+
+<softwarelist name="cassvisn_cart" description="Epoch Cassette Vision cartridges">
+
+ <software name="yosaku" supported="no">
+ <description>Yosaku</description>
+ <year>1981</year>
+ <publisher>Epoch</publisher>
+ <info name="alt_title" value="きこりの与作"/> <!-- Kikori no Yosaku -->
+ <info name="release" value="19810730"/>
+ <part name="cart" interface="cassvisn_cart">
+ <dataarea name="prg" width="16" endianness="big" size="0xf00">
+ <rom name="d777c005prog.s01" size="0xf00" crc="f917d2f0" sha1="b26baf2fd9b961b5a0e0475dfac93c18bd54cc4d"/>
+ </dataarea>
+ <dataarea name="pat" size="0x4d0">
+ <rom name="d777c005_pat.s01" size="0x4d0" crc="bf085553" sha1="321262b6dd67789e03db8f03160cb1819529f43c"/>
+ </dataarea>
+ </part>
+ </software>
+
+ <software name="baseball" supported="no">
+ <description>Baseball</description>
+ <year>1981</year>
+ <publisher>Epoch</publisher>
+ <info name="alt_title" value="ベースボール"/>
+ <info name="release" value="19810730"/>
+ <part name="cart" interface="cassvisn_cart">
+ <dataarea name="prg" width="16" endianness="big" size="0xf00">
+ <rom name="d778c_prog.s01" size="0xf00" crc="34127969" sha1="56528b54720b844a6275d219fb78e51caf06f777"/>
+ </dataarea>
+ <dataarea name="pat" size="0x4d0">
+ <rom name="d778c_pat.s01" size="0x4d0" crc="603fa141" sha1="e30f5ce85d8e2419025d52a7c733be2c127cc24f"/>
+ </dataarea>
+ </part>
+ </software>
+
+ <!-- PCB is silkscreened 006, ROM here is 007 is there an undumped earlier version? -->
+ <software name="galaxian" supported="no">
+ <description>Galaxian</description>
+ <year>1981</year>
+ <publisher>Epoch</publisher>
+ <info name="alt_title" value="ギャラクシアン"/>
+ <info name="release" value="19810810"/>
+ <part name="cart" interface="cassvisn_cart">
+ <dataarea name="prg" width="16" endianness="big" size="0xf00">
+ <rom name="d777c007prog.s01" size="0xf00" crc="538c13d3" sha1="537287e63b311caa281d8c6456f9cbe2eaebb9e7"/>
+ </dataarea>
+ <dataarea name="pat" size="0x4d0">
+ <rom name="d777c007_pat.s01" size="0x4d0" crc="d343eca4" sha1="e8f0312de0d7f0934a66192c1aaa2c802532b021"/>
+ </dataarea>
+ </part>
+ </software>
+
+ <software name="bigspt12" supported="no">
+ <description>Big Sports 12</description>
+ <year>1982</year>
+ <publisher>Epoch</publisher>
+ <info name="alt_title" value="ビッグスポーツ12"/>
+ <info name="release" value="19820615"/>
+ <part name="cart" interface="cassvisn_cart">
+ <dataarea name="prg" width="16" endianness="big" size="0xf00">
+ <rom name="d777c004prog.s01" size="0xf00" crc="030fc0d2" sha1="b1efe8ff1bc51082502253e63721e656ee75edda"/>
+ </dataarea>
+ <dataarea name="pat" size="0x4d0">
+ <rom name="d777c004_pat.s01" size="0x4d0" crc="7df5eb39" sha1="c59f6febf5f51eebc0cc8d288df6b5319510ecbf"/>
+ </dataarea>
+ </part>
+ </software>
+
+ <!-- PCB is skilscreened TV Vader & Yosaku, is there also a version of Yosaku using a D774C? -->
+ <software name="btlvader" supported="no">
+ <description>Battle Vader</description>
+ <year>1982</year>
+ <publisher>Epoch</publisher>
+ <info name="alt_title" value="バトルヘーダー"/>
+ <info name="release" value="19820303"/>
+ <part name="cart" interface="cassvisn_cart">
+ <dataarea name="prg" width="16" endianness="big" size="0xf00">
+ <rom name="d774cprog.s01" size="0xf00" crc="2bbe734f" sha1="04f2b6003b41bad2d246bd2e912887fdc77520c4"/>
+ </dataarea>
+ <dataarea name="pat" size="0x4d0">
+ <rom name="d774c_pat.s01" size="0x4d0" crc="5efbc1f4" sha1="1c5774113c554e26e8d0b7002d2f7b9d79107c82"/>
+ </dataarea>
+ </part>
+ </software>
+
+ <software name="pakpak" supported="no">
+ <description>PakPak Monster</description>
+ <year>1982</year>
+ <publisher>Epoch</publisher>
+ <info name="alt_title" value="パクパクモンスター"/>
+ <info name="release" value="19820713"/>
+ <part name="cart" interface="cassvisn_cart">
+ <dataarea name="prg" width="16" endianness="big" size="0xf00">
+ <rom name="d777c008prog.s01" size="0xf00" crc="254127d5" sha1="1217542ba02d84eeb0fad7b271a188ed2737fa71"/>
+ </dataarea>
+ <dataarea name="pat" size="0x4d0">
+ <rom name="d777c008_pat.s01" size="0x4d0" crc="e17f5d04" sha1="4393b21d3ef4e98ebf8d25ffb9b3f287fbbb23cb"/>
+ </dataarea>
+ </part>
+ </software>
+
+ <software name="nbasebal" supported="no">
+ <description>New Baseball</description>
+ <year>1982</year>
+ <publisher>Epoch</publisher>
+ <info name="alt_title" value="ニューベースボール"/>
+ <info name="release" value="19820615"/>
+ <part name="cart" interface="cassvisn_cart">
+ <dataarea name="prg" width="16" endianness="big" size="0xf00">
+ <rom name="d777c002prog.s01" size="0xf00" crc="65dae0cb" sha1="468ec1869235e96da2374f7a165d1d9ca96f3044"/>
+ </dataarea>
+ <dataarea name="pat" size="0x4d0">
+ <rom name="d777c002_pat.s01" size="0x4d0" crc="434209f8" sha1="e783cb265618c598ad614bf29c833f3e47ee6487"/>
+ </dataarea>
+ </part>
+ </software>
+
+ <software name="monstrmn" supported="no">
+ <description>Monster Mansion</description>
+ <year>1982</year>
+ <publisher>Epoch</publisher>
+ <info name="alt_title" value="モンスターマンション"/>
+ <info name="release" value="19821007"/>
+ <part name="cart" interface="cassvisn_cart">
+ <dataarea name="prg" width="16" endianness="big" size="0xf00">
+ <rom name="d777c009prog.s01" size="0xf00" crc="e453a00c" sha1="7f3c20653c8f6168d17cad40eac5926e6e9bb7b0"/>
+ </dataarea>
+ <dataarea name="pat" size="0x4d0">
+ <rom name="d777c009_pat.s01" size="0x4d0" crc="75deffbd" sha1="4f4c25fa9f96a345f7128ff0b52de315117da549"/>
+ </dataarea>
+ </part>
+ </software>
+
+ <software name="astrocmd" supported="no">
+ <description>Astro Command</description>
+ <year>1982</year>
+ <publisher>Epoch</publisher>
+ <info name="alt_title" value="アストロコマンド"/>
+ <info name="release" value="19820803"/>
+ <part name="cart" interface="cassvisn_cart">
+ <dataarea name="prg" width="16" endianness="big" size="0xf00">
+ <rom name="d777c010prog.s01" size="0xf00" crc="2691f5ce" sha1="f5510e139c6bda266450d3feaffe073374608b08"/>
+ </dataarea>
+ <dataarea name="pat" size="0x4d0">
+ <rom name="d777c010_pat.s01" size="0x4d0" crc="8b0c00bd" sha1="6fc01434fd92172eeaa7600742525ed69ecb90c2"/>
+ </dataarea>
+ </part>
+ </software>
+
+ <software name="monstrbk" supported="no">
+ <description>Monster Block</description>
+ <year>1984</year>
+ <publisher>Epoch</publisher>
+ <info name="alt_title" value="モンスターブロック"/>
+ <info name="release" value="19840316"/>
+ <part name="cart" interface="cassvisn_cart">
+ <dataarea name="prg" width="16" endianness="big" size="0xf00">
+ <rom name="d777c013prog.s01" size="0xf00" crc="81903844" sha1="0fbccdb42c245ad62b14041256df0f33e83bc32a"/>
+ </dataarea>
+ <dataarea name="pat" size="0x4d0">
+ <rom name="d777c013_pat.s01" size="0x4d0" crc="abf87caa" sha1="dd629bd43bea70ece9f9cd25b70eeef1caf72828"/>
+ </dataarea>
+ </part>
+ </software>
+
+ <software name="elvpanic" supported="no">
+ <description>Elevator Panic</description>
+ <year>1984</year>
+ <publisher>Epoch</publisher>
+ <info name="alt_title" value="エレベーターパニック"/>
+ <info name="release" value="19840801"/>
+ <part name="cart" interface="cassvisn_cart">
+ <dataarea name="prg" width="16" endianness="big" size="0xf00">
+ <rom name="d777c015prog.s01" size="0xf00" crc="709002f2" sha1="d88213dfcac99e303a34a0ad0561bf9242279873"/>
+ </dataarea>
+ <dataarea name="pat" size="0x4d0">
+ <rom name="d777c015_pat.s01" size="0x4d0" crc="51654eb2" sha1="e5e602a7c5dd2725b67235d401203ddd398bee9b"/>
+ </dataarea>
+ </part>
+ </software>
+
+</softwarelist>
diff --git a/scripts/src/cpu.lua b/scripts/src/cpu.lua
index 96a274222e1..302d4ff83f5 100644
--- a/scripts/src/cpu.lua
+++ b/scripts/src/cpu.lua
@@ -4136,6 +4136,23 @@ if opt_tool(CPUS, "HT1130") then
end
--------------------------------------------------
+-- UPD777
+--@src/devices/cpu/upd777/upd777.h,CPUS["UPD777"] = true
+--------------------------------------------------
+
+if CPUS["UPD777"] then
+ files {
+ MAME_DIR .. "src/devices/cpu/upd777/upd777.cpp",
+ MAME_DIR .. "src/devices/cpu/upd777/upd777.h",
+ }
+end
+
+if opt_tool(CPUS, "UPD777") then
+ table.insert(disasm_files , MAME_DIR .. "src/devices/cpu/upd777/upd777dasm.cpp")
+ table.insert(disasm_files , MAME_DIR .. "src/devices/cpu/upd777/upd777dasm.h")
+end
+
+--------------------------------------------------
-- Epson C33 STD, C33 ADV, etc.
--@src/devices/cpu/c33/c33common.h,CPUS["C33"] = true
--------------------------------------------------
diff --git a/src/devices/cpu/upd777/upd777.cpp b/src/devices/cpu/upd777/upd777.cpp
new file mode 100644
index 00000000000..fda5e638bb4
--- /dev/null
+++ b/src/devices/cpu/upd777/upd777.cpp
@@ -0,0 +1,1262 @@
+// license:BSD-3-Clause
+// copyright-holders:Vas Crabb
+
+#include "emu.h"
+#include "upd777.h"
+
+#include "upd777dasm.h"
+
+
+#define LOG_UNHANDLED_OPS (1U << 1)
+
+#define VERBOSE (LOG_UNHANDLED_OPS)
+#include "logmacro.h"
+
+DEFINE_DEVICE_TYPE(UPD777, upd777_cpu_device, "upd777", "uPD777")
+
+upd777_cpu_device::upd777_cpu_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_datamem(*this, "datamem")
+ , m_space_config("program", ENDIANNESS_BIG, 16, 11, -1, address_map_constructor(FUNC(upd777_cpu_device::internal_map), this))
+ , m_data_config("data", ENDIANNESS_BIG, 8, 7, 0, data)
+ , m_gfxdecode(*this, "gfxdecode")
+ , m_palette(*this, "palette")
+ , m_screen(*this, "screen")
+ , m_prgregion(*this, "prg")
+ , m_patregion(*this, "patterns")
+ , m_port_in(*this, 0xff)
+{
+}
+
+upd777_cpu_device::upd777_cpu_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock)
+ : upd777_cpu_device(mconfig, UPD777, tag, owner, clock, address_map_constructor(FUNC(upd777_cpu_device::internal_data_map), this))
+{
+}
+
+std::unique_ptr<util::disasm_interface> upd777_cpu_device::create_disassembler()
+{
+ return std::make_unique<upd777_disassembler>();
+}
+
+device_memory_interface::space_config_vector upd777_cpu_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 upd777_cpu_device::internal_map(address_map &map)
+{
+ map(0x000, 0x7ff).rom().region("prg", 0);
+}
+
+void upd777_cpu_device::internal_data_map(address_map &map)
+{
+ // 0x20 groups of 4 7-bit values
+ // groups 0x00-0x18 are used for sprite/pattern entries with the format
+
+ // 6543210
+ // 00 yyyyyyp (y = ypos, p = PRIO)
+ // 01 xxxxxxx (x = xpos)
+ // 02 ttttttt (t = pattern)
+ // 03 YYYRGBS (Y = , RGB = color, S=ySUB)
+
+ map(0x00, 0x7f).ram().share("datamem");
+}
+
+void upd777_cpu_device::increment_pc()
+{
+ u16 lowpc = m_pc & 0x07f;
+ u16 highpc = m_pc & 0x780;
+
+ const u16 xnor = BIT(lowpc, 6) ^ BIT(lowpc, 5) ^ 1;
+ lowpc = ((lowpc << 1) | xnor) & 0x7f;
+
+ // is returning to the start of the page the correct behavior?
+ if (lowpc == 0x00)
+ {
+ logerror("overflowing PC, returning to start of current page %03x\n", highpc);
+ // pakpak runs better if you do this, but that is clearly by chance as
+ // most other cases show this isn't meant to happen
+ //highpc += 0x80;
+ //highpc &= 0x7ff;
+ }
+
+ m_pc = highpc | lowpc;
+}
+
+void upd777_cpu_device::device_start()
+{
+ space(AS_PROGRAM).specific(m_space);
+ space(AS_DATA).specific(m_data);
+
+ set_icountptr(m_icount);
+
+ state_add(UPD777_PC, "PC", m_pc);
+ state_add(STATE_GENPC, "GENPC", m_pc).noshow();
+ state_add(STATE_GENPCBASE, "CURPC", m_pc).noshow();
+
+ state_add(UPD777_A1, "A1", m_a[0]);
+ state_add(UPD777_A2, "A2", m_a[1]);
+ state_add(UPD777_A3, "A3", m_a[2]);
+ state_add(UPD777_A4, "A4", m_a[3]);
+
+ state_add(UPD777_L, "L", m_l);
+ state_add(UPD777_H, "H", m_h);
+
+ state_add(UPD777_ADDR_STACK0, "ADDR_STACK0", m_stack[0]);
+ state_add(UPD777_ADDR_STACK1, "ADDR_STACK1", m_stack[1]);
+ state_add(UPD777_ADDR_STACK2, "ADDR_STACK2", m_stack[2]);
+ state_add(UPD777_ADDR_STACK_POS, "ADDR_STACK_POS", m_stackpos);
+
+ //state_add(UPD777_SKIP, "SKIP", m_skip); // will always be showing 0 as debugger doesn't hook on skipped opcodes
+
+ save_item(NAME(m_ppc));
+ save_item(NAME(m_pc));
+ save_item(NAME(m_skip));
+ save_item(NAME(m_a));
+ save_item(NAME(m_l));
+ save_item(NAME(m_ldash));
+ save_item(NAME(m_x4));
+ save_item(NAME(m_h));
+
+ save_item(NAME(m_frs));
+ save_item(NAME(m_fls));
+
+ save_item(NAME(m_mode));
+ save_item(NAME(m_stb));
+
+ save_item(NAME(m_stack));
+ save_item(NAME(m_stackpos));
+
+ save_item(NAME(m_disp));
+ save_item(NAME(m_gpe));
+ save_item(NAME(m_kie));
+ save_item(NAME(m_sme));
+}
+
+void upd777_cpu_device::device_reset()
+{
+ m_ppc = 0;
+ m_pc = 0;
+
+ m_a[0] = m_a[1] = m_a[2] = m_a[3] = 0;
+ m_l = 0;
+ m_ldash = 0;
+ m_x4 = 0;
+ m_h = 0;
+
+ m_frs = 0;
+ m_fls = 0;
+
+ m_skip = 1; // the first opcode is always 'NOP' so maybe skip is 1 on startup?
+
+ m_stack[0] = m_stack[1] = m_stack[2] = 0;
+ m_stackpos = 0;
+
+ m_disp = 0;
+ m_gpe = 0;
+ m_kie = 0;
+ m_sme = 0;
+}
+
+inline u16 upd777_cpu_device::fetch()
+{
+ u16 opcode = m_space.read_word(m_pc);
+ m_ppc = m_pc;
+ increment_pc();
+ return opcode;
+}
+
+
+inline void upd777_cpu_device::set_a11(int a11)
+{
+ m_pc = (m_pc & 0x3ff) | (a11 & 1) << 10;
+}
+
+inline void upd777_cpu_device::set_new_pc(int newpc)
+{
+ m_pc = newpc;
+}
+
+// L reg (lower memory pointer) is 2 bit
+inline void upd777_cpu_device::set_l(int l)
+{
+ m_l = l & 0x3;
+}
+
+inline u8 upd777_cpu_device::get_l() const
+{
+ return m_l & 0x3;
+}
+
+// H reg (upper memory pointer) is 5-bit
+inline void upd777_cpu_device::set_h(int h)
+{
+ m_h = h & 0x1f;
+}
+
+// H reg is used as upper bits of memory address
+inline u8 upd777_cpu_device::get_h_shifted() const
+{
+ return (m_h & 0x1f) << 2;
+}
+
+inline u8 upd777_cpu_device::get_h() const
+{
+ return m_h & 0x1f;
+}
+
+// M is the content of memory address pointed to by H and L
+inline u8 upd777_cpu_device::get_m_data()
+{
+ u8 addr = get_h_shifted() | get_l();
+ return read_data_mem(addr & 0x7f) & 0x7f;
+}
+
+inline void upd777_cpu_device::set_m_data(u8 data)
+{
+ u8 addr = get_h_shifted() | get_l();
+ write_data_mem(addr & 0x7f, data & 0x7f);
+}
+
+// 'A' regs are 7-bit
+inline void upd777_cpu_device::set_a1(u8 data) { m_a[0] = data & 0x7f; }
+inline void upd777_cpu_device::set_a2(u8 data) { m_a[1] = data & 0x7f; }
+inline void upd777_cpu_device::set_a3(u8 data) { m_a[2] = data & 0x7f; }
+inline void upd777_cpu_device::set_a4(u8 data) { m_a[3] = data & 0x7f; }
+
+inline void upd777_cpu_device::set_a1_or_a2(int reg, u8 value)
+{
+ if (reg == 0)
+ set_a1(value);
+ else
+ set_a2(value);
+}
+
+// 'A' regs are 7-bit
+inline u8 upd777_cpu_device::get_a1() const { return m_a[0] & 0x7f; }
+inline u8 upd777_cpu_device::get_a2() const { return m_a[1] & 0x7f; }
+inline u8 upd777_cpu_device::get_a3() const { return m_a[2] & 0x7f; }
+inline u8 upd777_cpu_device::get_a4() const { return m_a[3] & 0x7f; }
+
+inline u8 upd777_cpu_device::get_a1_or_a2(int reg) const
+{
+ if (reg == 0)
+ return get_a1();
+ else
+ return get_a2();
+}
+
+// FRS/FLS are the 2 7-bit sound registers
+inline void upd777_cpu_device::set_frs(u8 frs) { m_frs = frs & 0x7f; }
+inline void upd777_cpu_device::set_fls(u8 fls) { m_fls = fls & 0x7f; }
+
+// MODE is a 7-bit register with the following format
+// 6543210
+// rbhpRGB (r = reverberate sound effect, b = brightness, h = hue, p = black/prio, RGB = color)
+inline void upd777_cpu_device::set_mode(u8 mode) { m_mode = mode & 0x7f; }
+
+// single bit enable registers, although they have an important effect on the K->M opcode
+inline void upd777_cpu_device::set_disp(u8 data) { m_disp = data & 1; }
+inline void upd777_cpu_device::set_gpe(u8 data) { m_gpe = data & 1; }
+inline void upd777_cpu_device::set_kie(u8 data) { m_kie = data & 1; }
+inline void upd777_cpu_device::set_sme(u8 data) { m_sme = data & 1; }
+
+inline u8 upd777_cpu_device::get_kie() const { return m_kie & 1; }
+inline u8 upd777_cpu_device::get_sme() const { return m_sme & 1; }
+
+
+inline u8 upd777_cpu_device::read_data_mem(u8 addr)
+{
+ // data memory is 7-bit
+ return m_data.read_byte(addr) & 0x7f;
+}
+
+inline void upd777_cpu_device::write_data_mem(u8 addr, u8 data)
+{
+ // data memory is 7-bit
+ m_data.write_byte(addr, data & 0x7f);
+}
+
+inline void upd777_cpu_device::push_to_stack(u16 addr)
+{
+ if (m_stackpos < 3)
+ {
+ m_stack[m_stackpos] = addr;
+ m_stackpos++;
+ }
+ else
+ {
+ logerror("attempting to push to full address stack\n");
+ }
+}
+
+inline u16 upd777_cpu_device::pull_from_stack()
+{
+ if (m_stackpos > 0)
+ {
+ m_stackpos--;
+ return m_stack[m_stackpos];
+ }
+ else
+ {
+ logerror("attempting to pull from empty address stack\n");
+ return 0;
+ }
+}
+
+void upd777_cpu_device::do_op()
+{
+ const u16 inst = fetch();
+
+ if (inst >= 0b0000'1000'0000 && inst <= 0b0000'1111'1111)
+ {
+ // 080 - 0ff Skip if (M[H[5:1],L[2:1]][7:1]-K[7:1]) makes borrow
+ const int k = inst & 0x7f;
+ u8 m = get_m_data();
+ m = m - k;
+ if (m & 0x80)
+ m_skip = 1;
+ }
+ else if (inst >= 0b0001'0000'0000 && inst <= 0b0001'0111'1111)
+ {
+ // 100-17f M[H[5:1],L[2:1]][7:1]+K[7:1]->M[H[5:1],L[2:1]][7:1], Skip if carry, N->L[2:1]
+ const int k = inst & 0x1f;
+ const int n = (inst >> 5) & 0x3;
+ u8 m = get_m_data();
+ m = m + k;
+ set_m_data(m & 0x7f);
+
+ if (m & 0x80)
+ m_skip = 1;
+
+ // TODO: prevents infinitely scrolling screen, and allows pakpak to boot, but this does't make logical
+ // sense, unless the 'Skip if carry' in the text above is specifically meaning this only applies
+ // when skipping, however if you apply the same to the next opcode it breaks text rendering
+ // eg 'score' text in the same game.
+ if (m_skip)
+ set_l(n);
+ }
+ else if (inst >= 0b0001'1000'0000 && inst <= 0b0001'1111'1111)
+ {
+ // 180-1ff M[H[5:1],L[2:1]][7:1]-K[7:1]->M[H[5:1],L[2:1]][7:1], Skip if borrow, N->L[2:1]
+ const int k = inst & 0x1f;
+ const int n = (inst >> 5) & 0x3;
+ u8 m = get_m_data();
+ m = m - k;
+ set_m_data(m & 0x7f);
+
+ if (m & 0x80)
+ m_skip = 1;
+
+ set_l(n);
+ }
+ else if (inst >= 0b0100'1000'0000 && inst <= 0b0100'1011'1111)
+ {
+ // 480-4bf H[5:1]-K[5:1]->H[5:1], Skip if borrow
+ const int k = inst & 0x1f;
+ u8 h = get_h();
+ h = h - k;
+ if (h & 0x20)
+ m_skip = 1;
+ set_h(h & 0x1f);
+ }
+ else if (inst >= 0b0100'1100'0000 && inst <= 0b0100'1111'1111)
+ {
+ // 4c0 - 4ff H[5:1]+K[5:1]->H[5:1], Skip if carry
+ const int k = inst & 0x1f;
+ u8 h = get_h();
+ h = h + k;
+ if (h & 0x20)
+ m_skip = 1;
+ set_h(h & 0x1f);
+ }
+ else if (inst >= 0b0101'0000'0000 && inst <= 0b0101'0111'1111)
+ {
+ // 500 - 57f
+ // When (KIE=0)&(SME=0), Store K[7:1] to M[H[5:1],L[2:1]][7:1]
+ // When (KIE=1), Store KIN[7:1] to M[H[5:1],L[2:1]][7:1]
+ // When (SME=1), Store HCL[7:1] to M[H[5:1],L[2:1]][7:1]
+ const int k = inst & 0x7f;
+
+ if (get_kie()) // documentation does not state if KIE or SME has priority
+ {
+ LOGMASKED(LOG_UNHANDLED_OPS, "KIE->M\n", k);
+ // Inputs for Cassette Vision appear to be read by this
+ // (selected based on the STB output value?)
+ set_m_data(m_port_in(m_stb));
+ }
+ else if (get_sme())
+ {
+ LOGMASKED(LOG_UNHANDLED_OPS, "SME->M\n", k);
+ set_m_data(0);
+ }
+ else
+ {
+ set_m_data(k & 0x7f);
+ }
+ }
+ else if (inst >= 0b0101'1000'0000 && inst <= 0b0101'1111'1111)
+ {
+ // 580 - 5ff Store K[7:6] to L[2:1] and K[5:1] to H[5:1]
+ const int k = inst & 0x7f;
+ set_l(k >> 5);
+ set_h(k & 0x1f);
+ }
+ else if (inst >= 0b0110'0000'0000 && inst <= 0b0111'1111'1111)
+ {
+ // 600-67f Store K[7:1] to A1[7:1]
+ // 680-6ff Store K[7:1] to A2[7:1]
+ // 700-77f Store K[7:1] to A3[7:1]
+ // 780-7ff Store K[7:1] to A4[7:1]
+ const int reg = (inst & 0x180) >> 7;
+ const int k = inst & 0x7f;
+ switch (reg)
+ {
+ case 0: set_a1(k); break;
+ case 1: set_a2(k); break;
+ case 2: set_a3(k); break;
+ case 3: set_a4(k); break;
+ }
+ }
+ else if (inst >= 0b1000'0000'0000 && inst <= 0b1011'1111'1111)
+ {
+ // 800 - bff Move K[10:1] to A[10:1], Jump to A[11:1]
+ u16 fulladdress = (m_ppc & 0x400) | (inst & 0x3ff);
+ set_new_pc(fulladdress);
+ }
+ else if (inst >= 0b1100'0000'0000 && inst <= 0b1111'1111'1111)
+ {
+ // c00 - fff Move K[10:1] to A[10:1], 0 to A11, Jump to A[11:1], Push next A[11:1] up to ROM address stack
+ const int k = inst & 0x3ff;
+ push_to_stack(m_pc);
+ set_new_pc(k);
+ }
+ else if (((inst & 0b1111'0000'0000) == 0b0010'0000'0000) && ((inst & 0b0000'0000'1100) != 0b0000'0000'0100))
+ {
+ // 0b0010'rrnR'oonn where rr = reg1 (A1, A2, M or H), n = invert condition, R = reg2 (A1 or A2) and oo = optype (only 0,2,3 are valid, no cases here for 1) nn = next l value
+
+ // optype · (AND)
+ // 200 Skip if (A1[7:1]·A1[7:1]) makes zero, N->L[2:1]
+ // 220 Skip if (A1[7:1]·A1[7:1]) makes non zero, N->L[2:1]
+ // 210 Skip if (A1[7:1]·A2[7:1]) makes zero, N->L[2:1]
+ // 230 Skip if (A1[7:1]·A2[7:1]) makes non zero, N->L[2:1]
+ // 240 Skip if (A2[7:1]·A1[7:1]) makes zero, N->L[2:1]
+ // 260 Skip if (A2[7:1]·A1[7:1]) makes non zero, N->L[2:1]
+ // 250 Skip if (A2[7:1]·A2[7:1]) makes zero, N->L[2:1]
+ // 270 Skip if (A2[7:1]·A2[7:1]) makes non zero, N->L[2:1]
+ // 280 Skip if (M[H[5:1],L[2:1]][7:1]·A1[7:1]) makes zero, N->L[2:1]
+ // 2a0 Skip if (M[H[5:1],L[2:1]][7:1]·A1[7:1]) makes non zero, N->L[2:1]
+ // 290 Skip if (M[H[5:1],L[2:1]][7:1]·A2[7:1]) makes zero, N->L[2:1]
+ // 2b0 Skip if (M[H[5:1],L[2:1]][7:1]·A2[7:1]) makes non zero, N->L[2:1]
+ // 2c0 Skip if (H[5:1]·A1[5:1]) makes zero, N->L[2:1]
+ // 2e0 Skip if (H[5:1]·A1[5:1]) makes non zero, N->L[2:1]
+ // 2d0 Skip if (H[5:1]·A2[5:1]) makes zero, N->L[2:1]
+ // 2f0 Skip if (H[5:1]·A2[5:1]) makes non zero, N->L[2:1]
+
+ // optype = (these are expressed as x=y in the opcopde syntax, but x-y in the description, in reality it seems to act as 'CMP' so x-y = 0)
+ // 208 Skip if (A1[7:1]-A1[7:1]) makes zero, N->L[2:1]
+ // 228 Skip if (A1[7:1]-A1[7:1]) makes non zero, N->L[2:1]
+ // 218 Skip if (A1[7:1]-A2[7:1]) makes zero, N->L[2:1]
+ // 238 Skip if (A1[7:1]-A2[7:1]) makes non zero, N->L[2:1]
+ // 248 Skip if (A2[7:1]-A1[7:1]) makes zero, N->L[2:1]
+ // 268 Skip if (A2[7:1]-A1[7:1]) makes non zero, N->L[2:1]
+ // 258 Skip if (A2[7:1]-A2[7:1]) makes zero, N->L[2:1]
+ // 278 Skip if (A2[7:1]-A2[7:1]) makes non zero, N->L[2:1]
+ // 288 Skip if (M[H[5:1],L[2:1]][7:1]-A1[7:1]) makes zero, N->L[2:1]
+ // 2a8 Skip if (M[H[5:1],L[2:1]][7:1]-A1[7:1]) makes non zero, N->L[2:1]
+ // 298 Skip if (M[H[5:1],L[2:1]][7:1]-A2[7:1]) makes zero, N->L[2:1]
+ // 2b8 Skip if (M[H[5:1],L[2:1]][7:1]-A2[7:1]) makes non zero, N->L[2:1]
+ // 2c8 Skip if (H[5:1]-A1[5:1]) makes zero, N->L[2:1]
+ // 2e8 Skip if (H[5:1]-A1[5:1]) makes non zero, N->L[2:1]
+ // 2d8 Skip if (H[5:1]-A2[5:1]) makes zero, N->L[2:1]
+ // 2f8 Skip if (H[5:1]-A2[5:1]) makes non zero, N->L[2:1]
+
+ // optype -
+ // 20c Skip if (A1[7:1]-A1[7:1]) makes borrow, N->L[2:1]
+ // 22c Skip if (A1[7:1]-A1[7:1]) makes non borrow, N->L[2:1]
+ // 21c Skip if (A1[7:1]-A2[7:1]) makes borrow, N->L[2:1]
+ // 23c Skip if (A1[7:1]-A2[7:1]) makes non borrow, N->L[2:1]
+ // 24c Skip if (A2[7:1]-A1[7:1]) makes borrow, N->L[2:1]
+ // 26c Skip if (A2[7:1]-A1[7:1]) makes non borrow, N->L[2:1]
+ // 25c Skip if (A2[7:1]-A2[7:1]) makes borrow, N->L[2:1]
+ // 27c Skip if (A2[7:1]-A2[7:1]) makes non borrow, N->L[2:1]
+ // 28c Skip if (M[H[5:1],L[2:1]][7:1]-A1[7:1]) makes borrow, N->L[2:1]
+ // 2ac Skip if (M[H[5:1],L[2:1]][7:1]-A1[7:1]) makes non borrow, N->L[2:1]
+ // 29c Skip if (M[H[5:1],L[2:1]][7:1]-A2[7:1]) makes borrow, N->L[2:1]
+ // 2bc Skip if (M[H[5:1],L[2:1]][7:1]-A2[7:1]) makes non borrow, N->L[2:1]
+ // 2cc Skip if (H[5:1]-A1[5:1]) makes borrow, N->L[2:1]
+ // 2ec Skip if (H[5:1]-A1[5:1]) makes non borrow, N->L[2:1]
+ // 2dc Skip if (H[5:1]-A2[5:1]) makes borrow, N->L[2:1]
+ // 2fc Skip if (H[5:1]-A2[5:1]) makes non borrow, N->L[2:1]
+ const int non = inst & 0x20;
+ const int optype = (inst & 0x0c) >> 2;
+ const int reg1 = (inst & 0xc0) >> 6;
+ const int reg2 = (inst & 0x10) >> 4;
+ const int n = inst & 0x3;
+ u8 srcreg2 = get_a1_or_a2(reg2);
+ u8 srcreg1 = 0;
+ switch (reg1)
+ {
+ case 0: srcreg1 = get_a1(); break;
+ case 1: srcreg1 = get_a2(); break;
+ case 2: srcreg1 = get_m_data(); break;
+ case 3:
+ {
+ srcreg1 = get_h();
+ srcreg2 &= 0x1f;
+ break;
+ }
+ }
+
+ switch (optype)
+ {
+ case 0: // AND
+ {
+ if (!non)
+ {
+ if ((srcreg1 & srcreg2) == 0) // skip if (x·y) makes zero, N->L[2:1]
+ m_skip = 1;
+ }
+ else
+ {
+ if ((srcreg1 & srcreg2) != 0) // skip if (x·y) makes non zero, N->L[2:1]
+ m_skip = 1;
+ }
+ break;
+ }
+ case 1: // invalid
+ {
+ // can't happen, no switch case leads here
+ break;
+ }
+ case 2: // =
+ {
+ if (!non)
+ {
+ if (srcreg1 == srcreg2) // skip if (x-y) makes zero, N->L[2:1]
+ m_skip = 1;
+ }
+ else
+ {
+ if (srcreg1 != srcreg2) // skip if (x-y) makes non zero, N->L[2:1]
+ m_skip = 1;
+ }
+ break;
+ }
+ case 3: // -
+ {
+ u8 result = srcreg1 - srcreg2;
+
+ if (!non)
+ {
+ if (result & 0x80) // skip if (x-y) makes borrow, N->L[2:1]
+ m_skip = 1;
+ }
+ else
+ {
+ if ((result & 0x80) == 0) // skip if (x-y) makes non borrow, N->L[2:1]
+ m_skip = 1;
+ }
+
+ break;
+ }
+ }
+
+ set_l(n);
+ }
+ else if ((inst & 0b1111'1010'0000) == 0b0011'0010'0000)
+ {
+ // 0b0011'0r1R'oonn (where r = reg1, R = reg2, o = optype, and n = next l value)
+ // 320 AND A1[7:1] and A1[7:1], store to A1[7:1], N->L[2:1]
+ // 324 Add A1[7:1] and A1[7:1], store to A1[7:1], N->L[2:1]
+ // 328 OR A1[7:1] and A1[7:1], store to A1[7:1], N->L[2:1]
+ // 32c Subtract A1[7:1] and A1[7:1], store to A1[7:1], Skip if borrow, N->L[2:1]
+ // 330 AND A1[7:1] and A2[7:1], store to A1[7:1], N->L[2:1]
+ // 334 Add A1[7:1] and A2[7:1], store to A1[7:1], N->L[2:1]
+ // 338 OR A1[7:1] and A2[7:1], store to A1[7:1], N->L[2:1]
+ // 33c Subtract A1[7:1] and A2[7:1], store to A1[7:1], Skip if borrow, N->L[2:1]
+ // 360 AND A2[7:1] and A1[7:1], store to A2[7:1], N->L[2:1]
+ // 364 Add A2[7:1] and A1[7:1], store to A2[7:1], N->L[2:1]
+ // 368 OR A2[7:1] and A1[7:1], store to A2[7:1], N->L[2:1]
+ // 36c Subtract A2[7:1] and A1[7:1], store to A2[7:1], Skip if borrow, N->L[2:1]
+ // 370 AND A2[7:1] and A2[7:1], store to A2[7:1], N->L[2:1]
+ // 374 Add A2[7:1] and A2[7:1], store to A2[7:1], N->L[2:1]
+ // 378 OR A2[7:1] and A2[7:1], store to A2[7:1], N->L[2:1]
+ // 37c Subtract A2[7:1] and A2[7:1], store to A2[7:1], Skip if borrow, N->L[2:1]
+ const int optype = (inst & 0x0c) >> 2;
+ const int reg2 = (inst & 0x10) >> 4;
+ const int reg1 = (inst & 0x40) >> 6;
+ const int n = inst & 0x3;
+
+ u8 src1 = get_a1_or_a2(reg1);
+ u8 src2 = get_a1_or_a2(reg2);
+
+ switch (optype)
+ {
+ case 0: // AND
+ {
+ src1 = src1 & src2;
+ break;
+ }
+ case 1: // ADD
+ {
+ src1 = src1 + src2;
+ // not in this case?
+ //if (src1 & 0x80)
+ // m_skip = 1;
+ break;
+ }
+ case 2: // OR
+ {
+ src1 = src1 | src2;
+ break;
+ }
+ case 3: // MINUS
+ {
+ src1 = src1 - src2;
+ if (src1 & 0x80)
+ m_skip = 1;
+ break;
+ }
+ }
+ set_a1_or_a2(reg1, src1);
+ set_l(n);
+ }
+ else if ((inst & 0b1111'1110'0000) == 0b0011'1010'0000)
+ {
+ // 0b0011'101r'oonn (where r = reg, o = optype, n = next l value)
+ // 3a0 AND M[H[5:1],L[2:1]][7:1] and A1[7:1], store to M[H[5:1],L[2:1]][7:1], N->L[2:1]
+ // 3a4 Add M[H[5:1],L[2:1]][7:1] and A1[7:1], store to M[H[5:1],L[2:1]][7:1], N->L[2:1] Skip if carry
+ // 3a8 OR M[H[5:1],L[2:1]][7:1] and A1[7:1], store to M[H[5:1],L[2:1]][7:1], N->L[2:1]
+ // 3ac Subtract M[H[5:1],L[2:1]][7:1] and A1[7:1], store to M[H[5:1],L[2:1]][7:1], N->L[2:1] Skip if borrow
+ // 3b0 AND M[H[5:1],L[2:1]][7:1] and A2[7:1], store to M[H[5:1],L[2:1]][7:1], N->L[2:1]
+ // 3b4 Add M[H[5:1],L[2:1]][7:1] and A2[7:1], store to M[H[5:1],L[2:1]][7:1], N->L[2:1] Skip if carry
+ // 3b8 OR M[H[5:1],L[2:1]][7:1] and A2[7:1], store to M[H[5:1],L[2:1]][7:1], N->L[2:1]
+ // 3bc Subtract M[H[5:1],L[2:1]][7:1] and A2[7:1], store to M[H[5:1],L[2:1]][7:1], N->L[2:1] Skip if borrow
+ const int optype = (inst & 0x0c) >> 2;
+ const int reg2 = (inst & 0x10) >> 4;
+ const int n = inst & 0x3;
+ u8 src2 = get_a1_or_a2(reg2);
+ u8 m = get_m_data();
+
+ switch (optype)
+ {
+ case 0: // AND
+ {
+ m = m & src2;
+ break;
+ }
+ case 1: // ADD
+ {
+ m = m + src2;
+ if (m & 0x80)
+ m_skip = 1;
+ break;
+ }
+ case 2: // OR
+ {
+ m = m | src2;
+ break;
+ }
+ case 3: // MINUS
+ {
+ m = m - src2;
+ if (m & 0x80)
+ m_skip = 1;
+ break;
+ }
+ }
+ set_m_data(m);
+ set_l(n);
+ }
+ else if ((inst & 0b1111'1110'0000) == 0b0011'1110'0000)
+ {
+ // 0b0011'111r'oonn (where r = reg, o = optype, n = next l value)
+ // 3e0 AND H[5:1] and A1[5:1], store to H[5:1], N->L[2:1]
+ // 3e4 Add H[5:1] and A1[5:1], store to H[5:1], N->L[2:1]
+ // 3e8 OR H[5:1] and A1[5:1], store to H[5:1], N->L[2:1]
+ // 3ec Subtract H[5:1] and A1[5:1], store to H[5:1], Skip if borrow, N->L[2:1]
+ // 3f0 AND H[5:1] and A2[5:1], store to H[5:1], N->L[2:1]
+ // 3f4 Add H[5:1] and A2[5:1], store to H[5:1], N->L[2:1]
+ // 3f8 OR H[5:1] and A2[5:1], store to H[5:1], N->L[2:1]
+ // 3fc Subtract H[5:1] and A2[5:1], store to H[5:1], Skip if borrow, N->L[2:1]
+ const int optype = (inst & 0x0c) >> 2;
+ const int reg2 = (inst & 0x10) >> 4;
+ const int n = inst & 0x3;
+ u8 src2 = get_a1_or_a2(reg2) & 0x1f;
+ u8 h = get_h();
+
+ switch (optype)
+ {
+ case 0: // AND
+ {
+ h = h & src2;
+ break;
+ }
+ case 1: // ADD
+ {
+ h = h + src2;
+ break;
+ }
+ case 2: // OR
+ {
+ h = h | src2;
+ break;
+ }
+ case 3: // MINUS
+ {
+ h = h - src2;
+ if (h & 0x20)
+ m_skip = 1;
+ break;
+ }
+ }
+ set_h(h & 0x1f);
+ set_l(n);
+ }
+ else if ((inst & 0b1111'1100'0010) == 0b0100'0100'0000)
+ {
+ // 0b0100'01dg'ks0n (where d = DISP, G = GPE, K = KIE, S = SME, n = A11)
+ // 440 Set D to DISP, G to GPE, K to KIE, S to SME, N->A[11]
+ const int d = (inst >> 5) & 0x1;
+ const int g = (inst >> 4) & 0x1;
+ const int k = (inst >> 3) & 0x1;
+ const int s = (inst >> 2) & 0x1;
+ const int n = inst & 0x1;
+ set_disp(d);
+ set_gpe(g);
+ set_kie(k);
+ set_sme(s);
+ set_a11(n);
+ }
+ else if (inst == 0b0000'0000'0000)
+ {
+ // 000 No Operation
+ // nothing
+ }
+ else if (inst == 0b0000'0000'0100)
+ {
+ // 004 Skip if (Gun Port Latch) = 1
+ LOGMASKED(LOG_UNHANDLED_OPS, "GPL\n");
+ }
+ else if (inst == 0b0000'0000'1000)
+ {
+ // 008 Move H[5:1] to Line Buffer Register[5:1]
+ u8 h = get_h();
+ //LOGMASKED(LOG_UNHANDLED_OPS, "H(%02x)->NRM\n", h);
+
+ // this seems to push a value from RAM into the line buffer for the current 4(?) scanlines
+ u8 m1 = read_data_mem(get_h_shifted() | 0);
+ u8 m2 = read_data_mem(get_h_shifted() | 1);
+ u8 m3 = read_data_mem(get_h_shifted() | 2);
+ u8 m4 = read_data_mem(get_h_shifted() | 3);
+ push_to_line_buffer(h, m1,m2,m3,m4);
+ }
+ else if (inst == 0b0000'0001'1000)
+ {
+ // 018 H[5:1]<->X4[5:1], 0->X4[7:6], 0->X3[7:1], 0->X1'[1], 0->A1'[1], L[2:1]<->L'[2:1]
+ LOGMASKED(LOG_UNHANDLED_OPS, "H<->X\n");
+#if 1
+ // this opcode is not well explained! but X4 etc. are referenced on Data RAM & Register Files which makes
+ // it even more confusing (while L' isn't referenced anywhere else at all!)
+ u8 temp;
+
+ temp = m_x4;
+ m_x4 = get_h();
+ m_h = temp;
+
+ temp = m_ldash;
+ m_ldash = get_l();
+ set_l(temp);
+#endif
+ }
+ else if (inst == 0b0000'0010'0000)
+ {
+ // 020 Subroutine End, Pop down address stack
+ u16 addr = pull_from_stack();
+ set_new_pc(addr);
+ }
+ else if (inst == 0b0000'0100'1001)
+ {
+ // 049 Skip if (4H Horizontal Blank) = 1
+ //LOGMASKED(LOG_UNHANDLED_OPS, "4H BLK\n");
+ if (get_hbl_4_state())
+ m_skip = 1;
+ }
+ else if (inst == 0b0000'0100'1010)
+ {
+ // 04a Skip if (Vertical Blank) = 1, 0->M[[18:00],[3]][1]
+ LOGMASKED(LOG_UNHANDLED_OPS, "VBLK\n");
+ if (get_vbl_state())
+ m_skip = 1;
+
+ // TODO:
+ // need to do the 0->M[[18:00],[3]][1] bit
+ }
+ else if (inst == 0b0000'0100'1100)
+ {
+ // 04c Skip if (GP&SW/ input) = 1
+ LOGMASKED(LOG_UNHANDLED_OPS, "GPSW/\n");
+ }
+ else if (inst == 0b0000'0101'0100)
+ {
+ // 054 Move (A4[7:1],A3[7:1],A2[7:1],A1[7:1]) to M[H[5:1]][28:1]
+ write_data_mem(get_h_shifted() | 0, get_a1());
+ write_data_mem(get_h_shifted() | 1, get_a2());
+ write_data_mem(get_h_shifted() | 2, get_a3());
+ write_data_mem(get_h_shifted() | 3, get_a4());
+ }
+ else if (inst == 0b0000'0101'1000)
+ {
+ // 058 Move M[H[5:1]][28:1] to (A4[7:1],A3[7:1],A2[7:1],A1[7:1])
+ u8 m1 = read_data_mem(get_h_shifted() | 0);
+ u8 m2 = read_data_mem(get_h_shifted() | 1);
+ u8 m3 = read_data_mem(get_h_shifted() | 2);
+ u8 m4 = read_data_mem(get_h_shifted() | 3);
+ set_a1(m1);
+ set_a2(m2);
+ set_a3(m3);
+ set_a4(m4);
+ }
+ else if (inst == 0b0000'0101'1100)
+ {
+ // 05c Exchange (A4[7:1],A3[7:1],A2[7:1],A1[7:1]) and M[H[5:1]][28:1]
+ u8 m1 = read_data_mem(get_h_shifted() | 0);
+ u8 m2 = read_data_mem(get_h_shifted() | 1);
+ u8 m3 = read_data_mem(get_h_shifted() | 2);
+ u8 m4 = read_data_mem(get_h_shifted() | 3);
+ write_data_mem(get_h_shifted() | 0, get_a1());
+ write_data_mem(get_h_shifted() | 1, get_a2());
+ write_data_mem(get_h_shifted() | 2, get_a3());
+ write_data_mem(get_h_shifted() | 3, get_a4());
+ set_a1(m1);
+ set_a2(m2);
+ set_a3(m3);
+ set_a4(m4);
+ }
+ else if (inst == 0b0000'0110'0000)
+ {
+ // 060 Subroutine End, Pop down address stack, Skip
+ u16 addr = pull_from_stack();
+ set_new_pc(addr);
+ m_skip = 1;
+ }
+ else if (inst == 0b0011'0000'1000)
+ {
+ // 308 Move A1[7:1] to FLS[7:1], 0->L[2:1]
+ u8 a1 = get_a1();
+ set_fls(a1);
+ set_l(0);
+ }
+ else if (inst == 0b0011'0100'1000)
+ {
+ // 348 Move A2[7:1] to FLS[7:1], 0->L[2:1]
+ u8 a2 = get_a2();
+ set_fls(a2);
+ set_l(0);
+ }
+ else if (inst == 0b0011'1000'1000)
+ {
+ // 388 Move M[H[5:1],L[2:1]][7:1] to FLS[7:1], 0->L[2:1]
+ u8 m = get_m_data();
+ set_fls(m);
+ set_l(0);
+ }
+ else if (inst == 0b0011'0000'1001)
+ {
+ // 309 Move A1[7:1] to FRS[7:1], 1->L[2:1]
+ u8 a1 = get_a1();
+ set_frs(a1);
+ set_l(1);
+ }
+ else if (inst == 0b0011'0100'1001)
+ {
+ // 349 Move A2[7:1] to FRS[7:1], 1->L[2:1]
+ u8 a2 = get_a2();
+ set_frs(a2);
+ set_l(1);
+ }
+ else if (inst == 0b0011'1000'1001)
+ {
+ // 389 Move M[H[5:1],L[2:1]][7:1] to FRS[7:1], 1->L[2:1]
+ u8 m = get_m_data();
+ set_frs(m);
+ set_l(1);
+ }
+ else if ((inst == 0b0000'0010'1000) || (inst == 0b0000'0010'1001))
+ {
+ // 028 Shift STB[4:1], N->STB[1]
+ // STB is an input strobe / shifter
+ const int n = inst & 1;
+ LOGMASKED(LOG_UNHANDLED_OPS, "0x%d->STB\n", n);
+
+ m_stb = (m_stb << 1) | n;
+ m_stb &= 0xf;
+ }
+
+ else if ((inst == 0b0011'0000'1010) || (inst == 0b0011'0000'1011))
+ {
+ // 30a Move A1[7:1] to MODE[7:1], 1N->L[2:1]
+ const int n = (inst & 0x1) + 2;
+ u8 a1 = get_a1();
+ set_mode(a1);
+ set_l(n);
+ }
+ else if ((inst == 0b0011'01001010) || (inst == 0b0011'0100'1011))
+ {
+ // 34a Move A2[7:1] to MODE[7:1], 1N->L[2:1]
+ const int n = (inst & 0x1) + 2;
+ u8 a2 = get_a2();
+ set_mode(a2);
+ set_l(n);
+ }
+ else if ((inst == 0b0011'1000'1010) || (inst == 0b00111000'1011))
+ {
+ // 38a Move M[H[5:1],L[2:1]][7:1] to MODE[7:1], 1N->L[2:1]
+ const int n = (inst & 0x1) + 2;
+ u8 m = get_m_data();
+ set_mode(m);
+ set_l(n);
+ }
+ else if ((inst == 0b0100'0000'0000) || (inst == 0b0100'0000'0001))
+ {
+ // 400 N->A[11]
+ const int n = inst & 0x1;
+ set_a11(n);
+ }
+ else if ((inst == 0b0100'0000'0010) || (inst == 0b0100'0000'0011))
+ {
+ // 402 Jump to (000,M[H[5:1],L[2:1]][5:1],1N), 0->L[2:1], N->A[11]
+ const int n = inst & 0x1;
+ LOGMASKED(LOG_UNHANDLED_OPS, "JPM, 0->L, %d->A11\n", n);
+ set_a11(n);
+ }
+ else if ((inst & 0b1111'1111'1100) == 0b0011'0000'0000)
+ {
+ // 300 N->L[2:1]
+ const int n = inst & 0x3;
+ set_l(n);
+ }
+ else if ((inst & 0b1111'1111'1100) == 0b0011'0001'0000)
+ {
+ // 310 Move A2[7:1] to A1[7:1], N->L[2:1]
+ const int n = inst & 0x3;
+ u8 a2 = get_a2();
+ set_a1(a2);
+ set_l(n);
+ }
+ else if ((inst & 0b1111'1111'1100) == 0b0011'0100'0000)
+ {
+ // 340 Move A1[7:1] to A2[7:1], N->L[2:1]
+ const int n = inst & 0x3;
+ u8 a1 = get_a1();
+ set_a2(a1);
+ set_l(n);
+ }
+ else if ((inst & 0b1111'1111'1100) == 0b0011'0001'1000)
+ {
+ // 318 Right shift A1[7:1], 0->A1[7], N->L[2:1]
+ const int n = inst & 0x3;
+ u8 a1 = get_a1();
+ a1 = a1 >> 1;
+ set_a1(a1);
+ set_l(n);
+ }
+ else if ((inst & 0b1111'1111'1100) == 0b0011'0101'1000)
+ {
+ // 358 Right shift A2[7:1], 0->A2[7], N->L[2:1]
+ const int n = inst & 0x3;
+ u8 a2 = get_a2();
+ a2 = a2 >> 1;
+ set_a2(a2);
+ set_l(n);
+ }
+ else if ((inst & 0b1111'1111'1100) == 0b0011'1001'1000)
+ {
+ // 398 Right shift M[H[5:1],L[2:1]][7:1], 0->M[H[5:1],L[2:1]][7], N->L[2:1]
+ const int n = inst & 0x3;
+ u8 m = get_m_data();
+ m = m >> 1;
+ set_m_data(m);
+ set_l(n);
+ }
+ else if ((inst & 0b1111'1111'1100) == 0b0011'0001'1100)
+ {
+ // 31c Subtract A1[7:1] and A2[7:1], store to A2[7:1], Skip if borrow, N->L[2:1]
+ const int n = inst & 0x3;
+ u8 a1 = get_a1();
+ u8 a2 = get_a2();
+ a2 = a1 - a2;
+ if (a2 & 0x80)
+ m_skip = 1;
+ set_a2(a2);
+ set_l(n);
+ }
+ else if ((inst & 0b1111'1111'1100) == 0b0011'0100'1100)
+ {
+ // 34c Subtract A2[7:1] and A1[7:1], store to A1[7:1], Skip if borrow, N->L[2:1]
+ const int n = inst & 0x3;
+ u8 a1 = get_a1();
+ u8 a2 = get_a2();
+ a1 = a2 - a1;
+ if (a1 & 0x80)
+ m_skip = 1;
+ set_a1(a1);
+ set_l(n);
+ }
+ else if ((inst & 0b1111'1110'1100) == 0b0011'1000'0000)
+ {
+ // 380 Move A1[7:1] to M[H[5:1],L[2:1]][7:1], N->L[2:1]
+ // 390 Move A2[7:1] to M[H[5:1],L[2:1]][7:1], N->L[2:1]
+ const int reg = (inst & 0x10) >> 4;
+ const int n = inst & 0x3;
+ u8 src = get_a1_or_a2(reg);
+ set_m_data(src);
+ set_l(n);
+ }
+ else if ((inst & 0b1111'1110'1100) == 0b0011'1000'0100)
+ {
+ // 384 Exchange M[H[5:1],L[2:1]][7:1] and A1[7:1], N->L[2:1]
+ // 394 Exchange M[H[5:1],L[2:1]][7:1] and A2[7:1], N->L[2:1]
+ const int reg = (inst & 0x10) >> 4;
+ const int n = inst & 0x3;
+ u8 src = get_a1_or_a2(reg);
+ u8 m = get_m_data();
+ set_m_data(src);
+ set_a1_or_a2(reg,m);
+ set_l(n);
+ }
+ else if ((inst & 0b1111'1110'1100) == 0b0011'1000'1100)
+ {
+ // 38c Move M[H[5:1],L[2:1]][7:1] to A1[7:1], N->L[2:1]
+ // 39c Move M[H[5:1],L[2:1]][7:1] to A2[7:1], N->L[2:1]
+ const int reg = (inst & 0x10) >> 4;
+ const int n = inst & 0x3;
+ u8 m = get_m_data();
+ set_a1_or_a2(reg,m);
+ set_l(n);
+ }
+ else if ((inst & 0b1111'1110'1100) == 0b0011'1100'0000)
+ {
+ // 3c0 Move A1[5:1] to H[5:1], N->L[2:1]
+ // 3d0 Move A2[5:1] to H[5:1], N->L[2:1]
+ const int reg = (inst & 0x10) >> 4;
+ const int n = inst & 0x3;
+ u8 src = get_a1_or_a2(reg);
+ set_h(src);
+ set_l(n);
+ }
+ else if ((inst & 0b1111'1110'1100) == 0b0011'1100'1100)
+ {
+ // 3cc Move H[5:1] to A1[5:1], 0->A1[7:6], N->L[2:1]
+ // 3dc Move H[5:1] to A2[5:1], 0->A2[7:6], N->L[2:1]
+ const int reg = (inst & 0x10) >> 4;
+ const int n = inst & 0x3;
+ u8 h = get_h() & 0x1f;
+ set_a1_or_a2(reg,h);
+ set_l(n);
+ }
+ else if ((inst & 0b1111'1011'0011) == 0b0000'0011'0000)
+ {
+ // 30 Skip if (PD1 input) = 1
+ // 34 Skip if (PD2 input) = 1
+ // 38 Skip if (PD3 input) = 1
+ // 3c Skip if (PD4 input) = 1
+ // 70 Skip if (PD1 input) = 0
+ // 74 Skip if (PD2 input) = 0
+ // 78 Skip if (PD3 input) = 0
+ // 7c Skip if (PD4 input) = 0
+ const int which = (inst & 0x00c) >> 2;
+ const int inv = inst & 0x40;
+ LOGMASKED(LOG_UNHANDLED_OPS, "PD%d %sJ\n", which + 1, inv ? "/" : "");
+ }
+ else
+ {
+ LOGMASKED(LOG_UNHANDLED_OPS, "%04x <ILLEGAL>\n", inst);
+ }
+}
+
+void upd777_cpu_device::execute_run()
+{
+ while (m_icount > 0)
+ {
+ if (m_skip)
+ {
+ fetch();
+ m_skip = 0;
+ }
+
+ debugger_instruction_hook(m_pc);
+ do_op();
+ m_icount--;
+ }
+}
+
+void upd777_cpu_device::execute_set_input(int inputnum, int state)
+{
+}
+
+
+//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+
+bool upd777_cpu_device::get_vbl_state()
+{
+ int vpos = m_screen->vpos();
+ if (vpos > 240)
+ return true;
+
+ return false;
+}
+
+bool upd777_cpu_device::get_hbl_4_state()
+{
+ // I *think* this is Hblank for every 4th line (so a new display list can be written?)
+ int vpos = m_screen->vpos();
+ if ((vpos % 4) == 0)
+ {
+ int hpos = m_screen->hpos();
+ if (hpos > 200)
+ return true;
+ }
+
+ return false;
+}
+
+uint32_t upd777_cpu_device::screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect)
+{
+ // this needs to be scanline based, drawing whatever has been pushed to the linebuffer for the current
+ // group of scanlines!
+
+ bitmap.fill(0, cliprect);
+ gfx_element *gfx = m_gfxdecode->gfx(0);
+ gfx_element *gfx2 = m_gfxdecode->gfx(1);
+
+ for (int i = 0; i <= 0x18; i++)
+ {
+ u8 s0 = m_datamem[(i * 4) + 0];
+ u8 s1 = m_datamem[(i * 4) + 1];
+ u8 s2 = m_datamem[(i * 4) + 2];
+ u8 s3 = m_datamem[(i * 4) + 3];
+
+ int ypos = (s0 & 0x7e) >> 1;
+ //int prio = (s0 & 0x01);
+ int xpos = (s1 & 0x7f);
+ int patn = (s2 & 0x7f);
+ //int ylow = (s3 & 0x70);
+ int pal = (s3 & 0x0e) >> 1;
+ //int ysub = (s3 & 0x01);
+
+ if (patn<0x70)
+ gfx->zoom_transpen(bitmap, cliprect, patn, pal, 0, 0, xpos * 4, ypos * 4, 0x40000, 0x40000, 0);
+ else
+ gfx2->zoom_transpen(bitmap, cliprect, patn-0x70, pal, 0, 0, xpos * 4, ypos * 4, 0x40000, 0x40000, 0);
+ }
+
+ return 0;
+}
+
+// documentation says patterns 0x00 - 0x6e are 7x7
+// and patterns 0x70 - 0x7e are 8x7
+// but they all seem to be stored at 11x7, just with some columns blank?
+// this is probably because of how they were read out, over 11 data lines
+
+// 0x00-0x2f are 'Normal (7x7)
+// 0x30-0x67 are 'Bent' (7x7)
+// 0x68-0x6f are 'Y Repeat' (7x7)
+// 0x70-0x77 are 'XY Repeat' (8x7)
+// 0x78-0x7f are 'X Repeat' (8x7)
+//
+// NOTE, sprite patterns *7 and *f are unused so documentation expresses these ranges as to 66, 6e etc. rather than 67 6f
+//
+// it isn't clear how the 'Bent' effect etc. is enabled, as clearly not all patterns in this range should use it?
+
+static const gfx_layout test_layout =
+{
+ 7,7,
+ 0x70,
+ 1,
+ { 0 },
+ { 4,5,6,7,8,9,10 },
+ { 0*11,1*11,2*11,3*11,4*11,5*11,6*11 },
+ 7*11
+};
+
+static const gfx_layout test2_layout =
+{
+ 8,7,
+ 0x10,
+ 1,
+ { 0 },
+ { 3,4,5,6,7,8,9,10 },
+ { 0*11,1*11,2*11,3*11,4*11,5*11,6*11 },
+ 7*11
+};
+
+static GFXDECODE_START( gfx_ud777 )
+ GFXDECODE_ENTRY( "patterns", 0x000, test_layout, 0, 8 )
+ GFXDECODE_ENTRY( "patterns", 0x436, test2_layout, 0, 8 )
+GFXDECODE_END
+
+
+void upd777_cpu_device::palette_init(palette_device &palette) const
+{
+ // just a fake palette for now
+ for (int i = 0; i < palette.entries(); i++)
+ {
+ if (i & 1)
+ {
+ palette.set_pen_color(i, rgb_t(((i >> 1) & 1) ? 0xff : 0x7f, ((i >> 2) & 1) ? 0xff : 0x7f, ((i >> 3) & 1) ? 0xff : 0x7f));
+ }
+ else
+ {
+ palette.set_pen_color(i, rgb_t(0, 0, 0));
+ }
+ }
+}
+
+void upd777_cpu_device::push_to_line_buffer(u8 h, u8 m1, u8 m2, u8 m3, u8 m4)
+{
+ logerror("sprite %02x pushed to line buffer at scanline %d hpos %d: details %02x %02x %02x %02x\n", h, m_screen->vpos(), m_screen->hpos(), m1, m2, m3, m4);
+}
+
+TIMER_DEVICE_CALLBACK_MEMBER(upd777_cpu_device::scanline_timer)
+{
+ int scanline = param;
+
+ logerror("scanline %d\n", scanline);
+}
+
+void upd777_cpu_device::device_add_mconfig(machine_config &config)
+{
+ // or pass the screen from the driver?
+ SCREEN(config, m_screen, SCREEN_TYPE_RASTER);
+ m_screen->set_refresh_hz(60);
+ m_screen->set_vblank_time(ATTOSECONDS_IN_USEC(2000));
+ m_screen->set_size(512, 256);
+ m_screen->set_visarea(0, 512-1, 0, 256-0-1);
+ m_screen->set_screen_update(FUNC(upd777_cpu_device::screen_update));
+ m_screen->set_palette(m_palette);
+
+ TIMER(config, "scantimer").configure_scanline(FUNC(upd777_cpu_device::scanline_timer), "screen", 0, 1);
+
+ GFXDECODE(config, m_gfxdecode, m_palette, gfx_ud777);
+ PALETTE(config, m_palette, FUNC(upd777_cpu_device::palette_init), 32 * 3).set_entries(0x10);
+}
+
+ROM_START( upd777 )
+ ROM_REGION16_BE( 0x1000, "prg", ROMREGION_ERASEFF )
+ ROM_REGION( 0x4d0, "patterns", ROMREGION_ERASEFF )
+ROM_END
+
+const tiny_rom_entry *upd777_cpu_device::device_rom_region() const
+{
+ return ROM_NAME(upd777);
+}
+
diff --git a/src/devices/cpu/upd777/upd777.h b/src/devices/cpu/upd777/upd777.h
new file mode 100644
index 00000000000..cdb787fd104
--- /dev/null
+++ b/src/devices/cpu/upd777/upd777.h
@@ -0,0 +1,165 @@
+// license:BSD-3-Clause
+// copyright-holders:Vas Crabb
+
+#ifndef MAME_CPU_UPD777_UPD777_H
+#define MAME_CPU_UPD777_UPD777_H
+
+#pragma once
+
+#include "machine/timer.h"
+
+#include "emupal.h"
+#include "screen.h"
+
+
+class upd777_cpu_device : public cpu_device
+{
+public:
+ upd777_cpu_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock);
+
+ enum {
+ UPD777_PC = 0,
+ UPD777_A1,
+ UPD777_A2,
+ UPD777_A3,
+ UPD777_A4,
+ UPD777_SKIP,
+ UPD777_H,
+ UPD777_L,
+ UPD777_ADDR_STACK0,
+ UPD777_ADDR_STACK1,
+ UPD777_ADDR_STACK2,
+ UPD777_ADDR_STACK_POS,
+ };
+
+ u16* get_prgregion() { return &m_prgregion[0]; }
+ u8* get_patregion() { return &m_patregion[0]; }
+
+ auto in_cb() { return m_port_in.bind(); }
+
+protected:
+ upd777_cpu_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 void device_add_mconfig(machine_config &config) override;
+ virtual const tiny_rom_entry *device_rom_region() const override;
+
+ virtual std::unique_ptr<util::disasm_interface> create_disassembler() override;
+
+ virtual space_config_vector memory_space_config() const override;
+
+ bool get_vbl_state();
+ bool get_hbl_4_state();
+
+
+ void internal_map(address_map &map);
+ void internal_data_map(address_map &map);
+
+ required_shared_ptr<u8> m_datamem;
+
+private:
+ void increment_pc();
+ u16 fetch();
+ void do_op();
+
+
+ u8 get_l() const;
+ void set_l(int l);
+ u8 get_h_shifted() const;
+ u8 get_h() const;
+ void set_h(int h);
+ void set_a11(int a11);
+ void set_new_pc(int newpc);
+
+ void set_frs(u8 frs);
+ void set_fls(u8 fls);
+ void set_mode(u8 mode);
+
+ u8 get_m_data();
+ void set_m_data(u8 data);
+
+ u8 get_a1() const;
+ u8 get_a2() const;
+ u8 get_a3() const;
+ u8 get_a4() const;
+ u8 get_a1_or_a2(int reg) const;
+
+ void set_a1(u8 data);
+ void set_a2(u8 data);
+ void set_a3(u8 data);
+ void set_a4(u8 data);
+ void set_a1_or_a2(int reg, u8 value);
+
+ void set_disp(u8 data);
+ void set_gpe(u8 data);
+ void set_kie(u8 data);
+ void set_sme(u8 data);
+
+ u8 get_kie() const;
+ u8 get_sme() const;
+
+ u8 read_data_mem(u8 addr);
+ void write_data_mem(u8 addr, u8 data);
+ void push_to_stack(u16 addr);
+ u16 pull_from_stack();
+
+ address_space_config m_space_config;
+ address_space_config m_data_config;
+
+ memory_access<11, 1, -1, ENDIANNESS_BIG>::specific m_space;
+ memory_access<8, 0, 0, ENDIANNESS_BIG>::specific m_data;
+
+ u32 m_ppc;
+ u32 m_pc;
+ s32 m_icount;
+ u8 m_skip;
+ u8 m_a[4]; // A1 to A4
+ u8 m_h;
+ u8 m_l;
+ u16 m_stack[3]; // 3 11-bit registers
+ u8 m_stackpos;
+
+ u8 m_frs;
+ u8 m_fls;
+
+ u8 m_mode;
+
+ u8 m_stb;
+
+ // what are these, used by H<->X opcode
+ u8 m_ldash;
+ u8 m_x4;
+
+ // single bit enable registers?
+ u8 m_disp;
+ u8 m_gpe;
+ u8 m_kie;
+ u8 m_sme;
+
+ ///////////////////////////////////// VIDEO RELATED
+
+ uint32_t screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect);
+
+ void push_to_line_buffer(u8 h, u8 m1, u8 m2, u8 m3, u8 m4);
+
+ void palette_init(palette_device &palette) const;
+
+ TIMER_DEVICE_CALLBACK_MEMBER(scanline_timer);
+
+ required_device<gfxdecode_device> m_gfxdecode;
+ required_device<palette_device> m_palette;
+ required_device<screen_device> m_screen;
+ required_region_ptr<uint16_t> m_prgregion;
+ required_region_ptr<uint8_t> m_patregion;
+
+ devcb_read8 m_port_in;
+};
+
+DECLARE_DEVICE_TYPE(UPD777, upd777_cpu_device)
+
+#endif // MAME_CPU_UPD777_UPD777_H
diff --git a/src/devices/cpu/upd777/upd777dasm.cpp b/src/devices/cpu/upd777/upd777dasm.cpp
new file mode 100644
index 00000000000..fd7597b8bab
--- /dev/null
+++ b/src/devices/cpu/upd777/upd777dasm.cpp
@@ -0,0 +1,504 @@
+// license:BSD-3-Clause
+// copyright-holders:Vas Crabb
+
+#include "emu.h"
+#include "upd777dasm.h"
+
+upd777_disassembler::upd777_disassembler()
+ : util::disasm_interface()
+{
+ // init lfsr pc lut
+ for (u32 i = 0, pc = 0; i < 0x7f; i++)
+ {
+ m_l2r[i] = pc;
+ m_r2l[pc] = i;
+ const int top1 = (pc & 0x40) >> 6;
+ const int top2 = (pc & 0x20) >> 5;
+ const int nor = (top1 ^ top2) ^ 1;
+ pc = (pc << 1) | nor;
+ pc &= 0x7f;
+ }
+ m_l2r[0x7f] = 0x7f;
+ m_r2l[0x7f] = 0x7f;
+}
+
+
+std::string upd777_disassembler::get_300optype_name(int optype)
+{
+ switch (optype)
+ {
+ case 0x00: return "."; // 'AND' expressed as '·' in documentation, but disassembler isn't keen on that
+ case 0x01: return "+";
+ case 0x02: return "v"; // 'OR'
+ case 0x03: return "-";
+ }
+ return "<invalid optype>";
+}
+
+std::string upd777_disassembler::get_200optype_name(int optype)
+{
+ switch (optype)
+ {
+ case 0x00: return "."; // 'AND' expressed as '·' in documentation, but disassembler isn't keen on that
+ case 0x01: return "<invalid optype>";
+ case 0x02: return "=";
+ case 0x03: return "-";
+ }
+ return "<invalid optype>";
+}
+
+std::string upd777_disassembler::get_reg_name(int reg)
+{
+ switch (reg)
+ {
+ case 0x00: return "A1"; // general reg A1
+ case 0x01: return "A2"; // general reg A2
+ case 0x02: return "M"; // content of memory
+ case 0x03: return "H"; // high address
+ }
+ return "<invalid reg>";
+}
+
+offs_t upd777_disassembler::disassemble(std::ostream &stream, offs_t pc, const upd777_disassembler::data_buffer &opcodes, const upd777_disassembler::data_buffer &params)
+{
+ u16 inst = opcodes.r16(pc);
+
+ if (inst >= 0b0000'1000'0000 && inst <= 0b0000'1111'1111)
+ {
+ // 080 - 0ff Skip if (M[H[5:1],L[2:1]][7:1]-K[7:1]) makes borrow
+ const int k = inst & 0x7f;
+ util::stream_format(stream, "M-0x%02x", k);
+ }
+ else if (inst >= 0b0001'0000'0000 && inst <= 0b0001'0111'1111)
+ {
+ // 100-17f M[H[5:1],L[2:1]][7:1]+K[7:1]->M[H[5:1],L[2:1]][7:1], Skip if carry, N->L[2:1]
+ const int k = inst & 0x1f;
+ const int n = (inst >> 5) & 0x3;
+ util::stream_format(stream, "M+0x%02x->M, 0x%d->L", k, n);
+ }
+ else if (inst >= 0b0001'1000'0000 && inst <= 0b0001'1111'1111)
+ {
+ // 180-1ff M[H[5:1],L[2:1]][7:1]-K[7:1]->M[H[5:1],L[2:1]][7:1], Skip if borrow, N->L[2:1]
+ const int k = inst & 0x1f;
+ const int n = (inst >> 5) & 0x3;
+ util::stream_format(stream, "M-0x%02x->M, 0x%d->L", k, n);
+ }
+ else if (inst >= 0b0100'1000'0000 && inst <= 0b0100'1011'1111)
+ {
+ // 480-4bf H[5:1]-K[5:1]->H[5:1], Skip if borrow
+ const int k = inst & 0x1f;
+ util::stream_format(stream, "H-0x%02x->H BOJ", k);
+ }
+ else if (inst >= 0b0100'1100'0000 && inst <= 0b0100'1111'1111)
+ {
+ // 4c0 - 4ff H[5:1]+K[5:1]->H[5:1], Skip if carry
+ const int k = inst & 0x1f;
+ util::stream_format(stream, "H+0x%02x->H CAJ", k);
+ }
+ else if (inst >= 0b0101'0000'0000 && inst <= 0b0101'0111'1111)
+ {
+ // 500 - 57f
+ // When (KIE=0)&(SME=0), Store K[7:1] to M[H[5:1],L[2:1]][7:1]
+ // When (KIE=1), Store KIN[7:1] to M[H[5:1],L[2:1]][7:1]
+ // When (SME=1), Store HCL[7:1] to M[H[5:1],L[2:1]][7:1]
+ const int k = inst & 0x7f;
+ util::stream_format(stream, "0x%02x->M", k);
+ }
+ else if (inst >= 0b0101'1000'0000 && inst <= 0b0101'1111'1111)
+ {
+ // 580 - 5ff Store K[7:6] to L[2:1] and K[5:1] to H[5:1]
+ const int k = inst & 0x7f;
+ util::stream_format(stream, "0x%02x->L,H", k);
+ }
+ else if (inst >= 0b0110'0000'0000 && inst <= 0b0111'1111'1111)
+ {
+ // 600-67f Store K[7:1] to A1[7:1]
+ // 680-6ff Store K[7:1] to A2[7:1]
+ // 700-77f Store K[7:1] to A3[7:1]
+ // 780-7ff Store K[7:1] to A4[7:1]
+ const int reg = (inst & 0x180) >> 7;
+ const int k = inst & 0x7f;
+ util::stream_format(stream, "0x%02x->A%d", k, reg+1);
+ }
+ else if (inst >= 0b1000'0000'0000 && inst <= 0b1011'1111'1111)
+ {
+ // 800 - bff Move K[10:1] to A[10:1], Jump to A[11:1]
+ u16 fulladdress = (pc & 0x400) | (inst & 0x3ff);
+ util::stream_format(stream, "JP 0x%03x (%01x:%02x)", fulladdress, (fulladdress & 0x780)>>7, inst & 0x07f);
+ }
+ else if (inst >= 0b1100'0000'0000 && inst <= 0b1111'1111'1111)
+ {
+ // c00 - fff Move K[10:1] to A[10:1], 0 to A11, Jump to A[11:1], Push next A[11:1] up to ROM address stack
+ const int k = inst & 0x3ff;
+ util::stream_format(stream, "JS 0x%03x (%01x:%02x)", k & 0x3ff, (k & 0x380)>>7, k & 0x07f);
+ }
+ else if (((inst & 0b1111'0000'0000) == 0b0010'0000'0000) && ((inst & 0b0000'0000'1100) != 0b0000'0000'0100))
+ {
+ // 0b0010'rrnR'oonn where rr = reg1 (A1, A2, M or H), n = invert condition, R = reg2 (A1 or A2) and oo = optype (only 0,2,3 are valid, no cases here for 1) nn = next l value
+ //
+ // optype · (AND)
+ // 200 Skip if (A1[7:1]·A1[7:1]) makes zero, N->L[2:1]
+ // 220 Skip if (A1[7:1]·A1[7:1]) makes non zero, N->L[2:1]
+ // 210 Skip if (A1[7:1]·A2[7:1]) makes zero, N->L[2:1]
+ // 230 Skip if (A1[7:1]·A2[7:1]) makes non zero, N->L[2:1]
+ // 240 Skip if (A2[7:1]·A1[7:1]) makes zero, N->L[2:1]
+ // 260 Skip if (A2[7:1]·A1[7:1]) makes non zero, N->L[2:1]
+ // 250 Skip if (A2[7:1]·A2[7:1]) makes zero, N->L[2:1]
+ // 270 Skip if (A2[7:1]·A2[7:1]) makes non zero, N->L[2:1]
+ // 280 Skip if (M[H[5:1],L[2:1]][7:1]·A1[7:1]) makes zero, N->L[2:1]
+ // 2a0 Skip if (M[H[5:1],L[2:1]][7:1]·A1[7:1]) makes non zero, N->L[2:1]
+ // 290 Skip if (M[H[5:1],L[2:1]][7:1]·A2[7:1]) makes zero, N->L[2:1]
+ // 2b0 Skip if (M[H[5:1],L[2:1]][7:1]·A2[7:1]) makes non zero, N->L[2:1]
+ // 2c0 Skip if (H[5:1]·A1[5:1]) makes zero, N->L[2:1]
+ // 2e0 Skip if (H[5:1]·A1[5:1]) makes non zero, N->L[2:1]
+ // 2d0 Skip if (H[5:1]·A2[5:1]) makes zero, N->L[2:1]
+ // 2f0 Skip if (H[5:1]·A2[5:1]) makes non zero, N->L[2:1]
+
+ // optype = (these are expressed as x=y in the opcopde syntax, but x-y in the description, in reality it seems to act as 'CMP' so x-y = 0)
+ // 208 Skip if (A1[7:1]-A1[7:1]) makes zero, N->L[2:1]
+ // 228 Skip if (A1[7:1]-A1[7:1]) makes non zero, N->L[2:1]
+ // 218 Skip if (A1[7:1]-A2[7:1]) makes zero, N->L[2:1]
+ // 238 Skip if (A1[7:1]-A2[7:1]) makes non zero, N->L[2:1]
+ // 248 Skip if (A2[7:1]-A1[7:1]) makes zero, N->L[2:1]
+ // 268 Skip if (A2[7:1]-A1[7:1]) makes non zero, N->L[2:1]
+ // 258 Skip if (A2[7:1]-A2[7:1]) makes zero, N->L[2:1]
+ // 278 Skip if (A2[7:1]-A2[7:1]) makes non zero, N->L[2:1]
+ // 288 Skip if (M[H[5:1],L[2:1]][7:1]-A1[7:1]) makes zero, N->L[2:1]
+ // 2a8 Skip if (M[H[5:1],L[2:1]][7:1]-A1[7:1]) makes non zero, N->L[2:1]
+ // 298 Skip if (M[H[5:1],L[2:1]][7:1]-A2[7:1]) makes zero, N->L[2:1]
+ // 2b8 Skip if (M[H[5:1],L[2:1]][7:1]-A2[7:1]) makes non zero, N->L[2:1]
+ // 2c8 Skip if (H[5:1]-A1[5:1]) makes zero, N->L[2:1]
+ // 2e8 Skip if (H[5:1]-A1[5:1]) makes non zero, N->L[2:1]
+ // 2d8 Skip if (H[5:1]-A2[5:1]) makes zero, N->L[2:1]
+ // 2f8 Skip if (H[5:1]-A2[5:1]) makes non zero, N->L[2:1]
+
+ // optype -
+ // 20c Skip if (A1[7:1]-A1[7:1]) makes borrow, N->L[2:1]
+ // 22c Skip if (A1[7:1]-A1[7:1]) makes non borrow, N->L[2:1]
+ // 21c Skip if (A1[7:1]-A2[7:1]) makes borrow, N->L[2:1]
+ // 23c Skip if (A1[7:1]-A2[7:1]) makes non borrow, N->L[2:1]
+ // 24c Skip if (A2[7:1]-A1[7:1]) makes borrow, N->L[2:1]
+ // 26c Skip if (A2[7:1]-A1[7:1]) makes non borrow, N->L[2:1]
+ // 25c Skip if (A2[7:1]-A2[7:1]) makes borrow, N->L[2:1]
+ // 27c Skip if (A2[7:1]-A2[7:1]) makes non borrow, N->L[2:1]
+ // 28c Skip if (M[H[5:1],L[2:1]][7:1]-A1[7:1]) makes borrow, N->L[2:1]
+ // 2ac Skip if (M[H[5:1],L[2:1]][7:1]-A1[7:1]) makes non borrow, N->L[2:1]
+ // 29c Skip if (M[H[5:1],L[2:1]][7:1]-A2[7:1]) makes borrow, N->L[2:1]
+ // 2bc Skip if (M[H[5:1],L[2:1]][7:1]-A2[7:1]) makes non borrow, N->L[2:1]
+ // 2cc Skip if (H[5:1]-A1[5:1]) makes borrow, N->L[2:1]
+ // 2ec Skip if (H[5:1]-A1[5:1]) makes non borrow, N->L[2:1]
+ // 2dc Skip if (H[5:1]-A2[5:1]) makes borrow, N->L[2:1]
+ // 2fc Skip if (H[5:1]-A2[5:1]) makes non borrow, N->L[2:1]
+ const int non = inst & 0x20;
+ const int optype = (inst & 0x0c) >> 2;
+ const int reg1 = (inst & 0xc0) >> 6;
+ const int reg2 = (inst & 0x10) >> 4;
+ const int n = inst & 0x3;
+ util::stream_format(stream, "%s%s%s, 0x%d->L %s%s", get_reg_name(reg1), get_200optype_name(optype), get_reg_name(reg2), n, (optype == 3) ? "BOJ" : "EQJ", non ? "/" : "");
+ }
+ else if ((inst & 0b1111'1010'0000) == 0b0011'0010'0000)
+ {
+ // 0b0011'0r1R'oonn (where r = reg1, R = reg2, o = optype, and n = next l value)
+ // 320 AND A1[7:1] and A1[7:1], store to A1[7:1], N->L[2:1]
+ // 324 Add A1[7:1] and A1[7:1], store to A1[7:1], N->L[2:1]
+ // 328 OR A1[7:1] and A1[7:1], store to A1[7:1], N->L[2:1]
+ // 32c Subtract A1[7:1] and A1[7:1], store to A1[7:1], Skip if borrow, N->L[2:1]
+ // 330 AND A1[7:1] and A2[7:1], store to A1[7:1], N->L[2:1]
+ // 334 Add A1[7:1] and A2[7:1], store to A1[7:1], N->L[2:1]
+ // 338 OR A1[7:1] and A2[7:1], store to A1[7:1], N->L[2:1]
+ // 33c Subtract A1[7:1] and A2[7:1], store to A1[7:1], Skip if borrow, N->L[2:1]
+ // 360 AND A2[7:1] and A1[7:1], store to A2[7:1], N->L[2:1]
+ // 364 Add A2[7:1] and A1[7:1], store to A2[7:1], N->L[2:1]
+ // 368 OR A2[7:1] and A1[7:1], store to A2[7:1], N->L[2:1]
+ // 36c Subtract A2[7:1] and A1[7:1], store to A2[7:1], Skip if borrow, N->L[2:1]
+ // 370 AND A2[7:1] and A2[7:1], store to A2[7:1], N->L[2:1]
+ // 374 Add A2[7:1] and A2[7:1], store to A2[7:1], N->L[2:1]
+ // 378 OR A2[7:1] and A2[7:1], store to A2[7:1], N->L[2:1]
+ // 37c Subtract A2[7:1] and A2[7:1], store to A2[7:1], Skip if borrow, N->L[2:1]
+ const int optype = (inst & 0x0c) >> 2;
+ const int reg2 = (inst & 0x10) >> 4;
+ const int reg1 = (inst & 0x40) >> 6;
+ const int n = inst & 0x3;
+ util::stream_format(stream, "%s%s%s->%s, 0x%d->L %s", get_reg_name(reg1), get_300optype_name(optype), get_reg_name(reg2), get_reg_name(reg1), n, (optype == 3) ? "BOJ" : "");
+ }
+ else if ((inst & 0b1111'1110'0000) == 0b0011'1010'0000)
+ {
+ // 0b0011'101r'oonn (where r = reg, o = optype, n = next l value)
+ // 3a0 AND M[H[5:1],L[2:1]][7:1] and A1[7:1], store to M[H[5:1],L[2:1]][7:1], N->L[2:1]
+ // 3a4 Add M[H[5:1],L[2:1]][7:1] and A1[7:1], store to M[H[5:1],L[2:1]][7:1], N->L[2:1] Skip if carry
+ // 3a8 OR M[H[5:1],L[2:1]][7:1] and A1[7:1], store to M[H[5:1],L[2:1]][7:1], N->L[2:1]
+ // 3ac Subtract M[H[5:1],L[2:1]][7:1] and A1[7:1], store to M[H[5:1],L[2:1]][7:1], N->L[2:1] Skip if borrow
+ // 3b0 AND M[H[5:1],L[2:1]][7:1] and A2[7:1], store to M[H[5:1],L[2:1]][7:1], N->L[2:1]
+ // 3b4 Add M[H[5:1],L[2:1]][7:1] and A2[7:1], store to M[H[5:1],L[2:1]][7:1], N->L[2:1] Skip if carry
+ // 3b8 OR M[H[5:1],L[2:1]][7:1] and A2[7:1], store to M[H[5:1],L[2:1]][7:1], N->L[2:1]
+ // 3bc Subtract M[H[5:1],L[2:1]][7:1] and A2[7:1], store to M[H[5:1],L[2:1]][7:1], N->L[2:1] Skip if borrow
+ const int optype = (inst & 0x0c) >> 2;
+ const int reg2 = (inst & 0x10) >> 4;
+ const int n = inst & 0x3;
+ util::stream_format(stream, "M%s%s->M, 0x%d->L", get_300optype_name(optype), get_reg_name(reg2), n);
+ }
+ else if ((inst & 0b1111'1110'0000) == 0b0011'1110'0000)
+ {
+ // 0b0011'111r'oonn (where r = reg, o = optype, n = next l value)
+ // 3e0 AND H[5:1] and A1[5:1], store to H[5:1], N->L[2:1]
+ // 3e4 Add H[5:1] and A1[5:1], store to H[5:1], N->L[2:1]
+ // 3e8 OR H[5:1] and A1[5:1], store to H[5:1], N->L[2:1]
+ // 3ec Subtract H[5:1] and A1[5:1], store to H[5:1], Skip if borrow, N->L[2:1]
+ // 3f0 AND H[5:1] and A2[5:1], store to H[5:1], N->L[2:1]
+ // 3f4 Add H[5:1] and A2[5:1], store to H[5:1], N->L[2:1]
+ // 3f8 OR H[5:1] and A2[5:1], store to H[5:1], N->L[2:1]
+ // 3fc Subtract H[5:1] and A2[5:1], store to H[5:1], Skip if borrow, N->L[2:1]
+ const int optype = (inst & 0x0c) >> 2;
+ const int reg = (inst & 0x10) >> 4;
+ const int n = inst & 0x3;
+ util::stream_format(stream, "H%s%s->H, 0x%d->L", get_300optype_name(optype), get_reg_name(reg), n);
+ }
+ else if ((inst & 0b1111'1100'0010) == 0b0100'0100'0000)
+ {
+ // 0b0100'01dg'ks0n (where d = DISP, G = GPE, K = KIE, S = SME, n = A11)
+ // 440 Set D to DISP, G to GPE, K to KIE, S to SME, N->A[11]
+ const int d = (inst >> 5) & 0x1;
+ const int g = (inst >> 4) & 0x1;
+ const int k = (inst >> 3) & 0x1;
+ const int s = (inst >> 2) & 0x1;
+ const int n = inst & 0x1;
+ util::stream_format(stream, "%d->D, %d->G, %d->K, %d->S, %d->A11", d, g, k, s, n);
+ }
+ else if (inst == 0b0000'0000'0000)
+ {
+ // 000 No Operation
+ util::stream_format(stream, "NOP");
+ }
+ else if (inst == 0b0000'0000'0100)
+ {
+ // 004 Skip if (Gun Port Latch) = 1
+ util::stream_format(stream, "GPL");
+ }
+ else if (inst == 0b0000'0000'1000)
+ {
+ // 008 Move H[5:1] to Line Buffer Register[5:1]
+ util::stream_format(stream, "H->NRM");
+ }
+ else if (inst == 0b0000'0001'1000)
+ {
+ // 018 H[5:1]<->X4[5:1], 0->X4[7:6], 0->X3[7:1], 0->X1'[1], 0->A1'[1], L[2:1]<->L'[2:1]
+ util::stream_format(stream, "H<->X");
+ }
+ else if (inst == 0b0000'0010'0000)
+ {
+ // 020 Subroutine End, Pop down address stack
+ util::stream_format(stream, "SRE");
+ }
+ else if (inst == 0b0000'0100'1001)
+ {
+ // 049 Skip if (4H Horizontal Blank) = 1
+ util::stream_format(stream, "4H BLK");
+ }
+ else if (inst == 0b0000'0100'1010)
+ {
+ // 04a Skip if (Vertical Blank) = 1, 0->M[[18:00],[3]][1]
+ util::stream_format(stream, "VBLK");
+ }
+ else if (inst == 0b0000'0100'1100)
+ {
+ // 04c Skip if (GP&SW/ input) = 1
+ util::stream_format(stream, "GPSW/");
+ }
+ else if (inst == 0b0000'0101'0100)
+ {
+ // 054 Move (A4[7:1],A3[7:1],A2[7:1],A1[7:1]) to M[H[5:1]][28:1]
+ util::stream_format(stream, "A->MA");
+ }
+ else if (inst == 0b0000'0101'1000)
+ {
+ // 058 Move M[H[5:1]][28:1] to (A4[7:1],A3[7:1],A2[7:1],A1[7:1])
+ util::stream_format(stream, "MA->A");
+ }
+ else if (inst == 0b0000'0101'1100)
+ {
+ // 05c Exchange (A4[7:1],A3[7:1],A2[7:1],A1[7:1]) and M[H[5:1]][28:1]
+ util::stream_format(stream, "MA<->A");
+ }
+ else if (inst == 0b0000'0110'0000)
+ {
+ // 060 Subroutine End, Pop down address stack, Skip
+ util::stream_format(stream, "SRE+1");
+ }
+ else if (inst == 0b0011'0000'1000)
+ {
+ // 308 Move A1[7:1] to FLS[7:1], 0->L[2:1]
+ util::stream_format(stream, "A1->FLS, 0->L");
+ }
+ else if (inst == 0b0011'0100'1000)
+ {
+ // 348 Move A2[7:1] to FLS[7:1], 0->L[2:1]
+ util::stream_format(stream, "A2->FLS, 0->L");
+ }
+ else if (inst == 0b0011'1000'1000)
+ {
+ // 388 Move M[H[5:1],L[2:1]][7:1] to FLS[7:1], 0->L[2:1]
+ util::stream_format(stream, "M->FLS, 0->L");
+ }
+ else if (inst == 0b0011'0000'1001)
+ {
+ // 309 Move A1[7:1] to FRS[7:1], 1->L[2:1]
+ util::stream_format(stream, "A1->FRS, 1->L");
+ }
+ else if (inst == 0b0011'0100'1001)
+ {
+ // 349 Move A2[7:1] to FRS[7:1], 1->L[2:1]
+ util::stream_format(stream, "A2->FRS, 1->L");
+ }
+ else if (inst == 0b0011'1000'1001)
+ {
+ // 389 Move M[H[5:1],L[2:1]][7:1] to FRS[7:1], 1->L[2:1]
+ util::stream_format(stream, "M->FRS, 1->L");
+ }
+ else if ((inst == 0b0000'0010'1000) || (inst == 0b0000'0010'1001))
+ {
+ // 028 Shift STB[4:1], N->STB[1]
+ // STB is an input strobe / shifter
+ const int n = inst & 1;
+ util::stream_format(stream, "0x%d->STB", n);
+ }
+ else if ((inst == 0b0011'0000'1010) || (inst == 0b0011'0000'1011))
+ {
+ // 30a Move A1[7:1] to MODE[7:1], 1N->L[2:1]
+ const int n = (inst & 0x1) + 2;
+ util::stream_format(stream, "A1->MODE, 0x%d->L", n);
+ }
+ else if ((inst == 0b0011'01001010) || (inst == 0b0011'0100'1011))
+ {
+ // 34a Move A2[7:1] to MODE[7:1], 1N->L[2:1]
+ const int n = (inst & 0x1) + 2;
+ util::stream_format(stream, "A2->MODE, 0x%d->L", n);
+ }
+ else if ((inst == 0b0011'1000'1010) || (inst == 0b00111000'1011))
+ {
+ // 38a Move M[H[5:1],L[2:1]][7:1] to MODE[7:1], 1N->L[2:1]
+ const int n = (inst & 0x1) + 2;
+ util::stream_format(stream, "M->MODE, 0x%d->L", n);
+ }
+
+ else if ((inst == 0b0100'0000'0000) || (inst == 0b0100'0000'0001))
+ {
+ // 400 N->A[11]
+ const int n = inst & 0x1;
+ util::stream_format(stream, "%d->A11", n);
+ }
+ else if ((inst == 0b0100'0000'0010) || (inst == 0b0100'0000'0011))
+ {
+ // 402 Jump to (000,M[H[5:1],L[2:1]][5:1],1N), 0->L[2:1], N->A[11]
+ const int n = inst & 0x1;
+ util::stream_format(stream, "JPM, 0->L, %d->A11", n);
+ }
+ else if ((inst & 0b1111'1111'1100) == 0b0011'0000'0000)
+ {
+ // 300 N->L[2:1]
+ const int n = inst & 0x3;
+ util::stream_format(stream, "0x%d->L", n);
+ }
+ else if ((inst & 0b1111'1111'1100) == 0b0011'0001'0000)
+ {
+ // 310 Move A2[7:1] to A1[7:1], N->L[2:1]
+ const int n = inst & 0x3;
+ util::stream_format(stream, "A2->A1, 0x%d->L", n);
+ }
+ else if ((inst & 0b1111'1111'1100) == 0b0011'0100'0000)
+ {
+ // 340 Move A1[7:1] to A2[7:1], N->L[2:1]
+ const int n = inst & 0x3;
+ util::stream_format(stream, "A1->A2, 0x%d->L", n);
+ }
+
+ else if ((inst & 0b1111'1111'1100) == 0b0011'0001'1000)
+ {
+ // 318 Right shift A1[7:1], 0->A1[7], N->L[2:1]
+ const int n = inst & 0x3;
+ util::stream_format(stream, "A1->RS, 0x%d->L", n);
+ }
+ else if ((inst & 0b1111'1111'1100) == 0b0011'0101'1000)
+ {
+ // 358 Right shift A2[7:1], 0->A2[7], N->L[2:1]
+ const int n = inst & 0x3;
+ util::stream_format(stream, "A2->RS, 0x%d->L", n);
+ }
+ else if ((inst & 0b1111'1111'1100) == 0b0011'1001'1000)
+ {
+ // 398 Right shift M[H[5:1],L[2:1]][7:1], 0->M[H[5:1],L[2:1]][7], N->L[2:1]
+ const int n = inst & 0x3;
+ util::stream_format(stream, "M->RS, 0x%d->L", n);
+ }
+ else if ((inst & 0b1111'1111'1100) == 0b0011'0001'1100)
+ {
+ // 31c Subtract A1[7:1] and A2[7:1], store to A2[7:1], Skip if borrow, N->L[2:1]
+ const int n = inst & 0x3;
+ util::stream_format(stream, "A1-A2->A2, 0x%d->L", n);
+ }
+ else if ((inst & 0b1111'1111'1100) == 0b0011'0100'1100)
+ {
+ // 34c Subtract A2[7:1] and A1[7:1], store to A1[7:1], Skip if borrow, N->L[2:1]
+ const int n = inst & 0x3;
+ util::stream_format(stream, "A2-A1->A1, 0x%d->L", n);
+ }
+ else if ((inst & 0b1111'1110'1100) == 0b0011'1000'0000)
+ {
+ // 380 Move A1[7:1] to M[H[5:1],L[2:1]][7:1], N->L[2:1]
+ // 390 Move A2[7:1] to M[H[5:1],L[2:1]][7:1], N->L[2:1]
+ const int reg = (inst & 0x10) >> 4;
+ const int n = inst & 0x3;
+ util::stream_format(stream, "A%d->M, 0x%d->L", reg + 1, n);
+ }
+ else if ((inst & 0b1111'1110'1100) == 0b0011'1000'0100)
+ {
+ // 384 Exchange M[H[5:1],L[2:1]][7:1] and A1[7:1], N->L[2:1]
+ // 394 Exchange M[H[5:1],L[2:1]][7:1] and A2[7:1], N->L[2:1]
+ const int reg = (inst & 0x10) >> 4;
+ const int n = inst & 0x3;
+ util::stream_format(stream, "M<->A%d, 0x%d->L", reg + 1, n);
+ }
+ else if ((inst & 0b1111'1110'1100) == 0b0011'1000'1100)
+ {
+ // 38c Move M[H[5:1],L[2:1]][7:1] to A1[7:1], N->L[2:1]
+ // 39c Move M[H[5:1],L[2:1]][7:1] to A2[7:1], N->L[2:1]
+ const int reg = (inst & 0x10) >> 4;
+ const int n = inst & 0x3;
+ util::stream_format(stream, "M->A%d, 0x%d->L", reg + 1, n);
+ }
+ else if ((inst & 0b1111'1110'1100) == 0b0011'1100'0000)
+ {
+ // 3c0 Move A1[5:1] to H[5:1], N->L[2:1]
+ // 3d0 Move A2[5:1] to H[5:1], N->L[2:1]
+ const int reg = (inst & 0x10) >> 4;
+ const int n = inst & 0x3;
+ util::stream_format(stream, "A%d->H, 0x%d->L", reg + 1, n);
+ }
+ else if ((inst & 0b1111'1110'1100) == 0b0011'1100'1100)
+ {
+ // 3cc Move H[5:1] to A1[5:1], 0->A1[7:6], N->L[2:1]
+ // 3dc Move H[5:1] to A2[5:1], 0->A2[7:6], N->L[2:1]
+ const int reg = (inst & 0x10) >> 4;
+ const int n = inst & 0x3;
+ util::stream_format(stream, "H->A%d, 0x%d->L", reg + 1, n);
+ }
+ else if ((inst & 0b1111'1011'0011) == 0b0000'0011'0000)
+ {
+ // 30 Skip if (PD1 input) = 1
+ // 34 Skip if (PD2 input) = 1
+ // 38 Skip if (PD3 input) = 1
+ // 3c Skip if (PD4 input) = 1
+ // 70 Skip if (PD1 input) = 0
+ // 74 Skip if (PD2 input) = 0
+ // 78 Skip if (PD3 input) = 0
+ // 7c Skip if (PD4 input) = 0
+ const int which = (inst & 0x00c) >> 2;
+ const int inv = inst & 0x40;
+ util::stream_format(stream, "PD%d %sJ", which + 1, inv ? "/" : "");
+ }
+ else
+ {
+ util::stream_format(stream, "%04x <ILLEGAL>", inst);
+ }
+
+ return 1;
+}
diff --git a/src/devices/cpu/upd777/upd777dasm.h b/src/devices/cpu/upd777/upd777dasm.h
new file mode 100644
index 00000000000..b7729edbc30
--- /dev/null
+++ b/src/devices/cpu/upd777/upd777dasm.h
@@ -0,0 +1,34 @@
+// license:BSD-3-Clause
+// copyright-holders:Vas Crabb
+
+#ifndef MAME_CPU_UPD777_UPD777DASM_H
+#define MAME_CPU_UPD777_UPD777DASM_H
+
+#pragma once
+
+
+class upd777_disassembler : public util::disasm_interface
+{
+public:
+ // construction/destruction
+ upd777_disassembler();
+
+protected:
+ // disassembler overrides
+ virtual u32 opcode_alignment() const override { return 1; }
+ virtual u32 interface_flags() const override { return NONLINEAR_PC | PAGED; }
+ virtual u32 page_address_bits() const override { return 7; }
+ virtual offs_t pc_linear_to_real(offs_t pc) const override { return (pc & ~0x7f) | m_l2r[pc & 0x7f]; }
+ virtual offs_t pc_real_to_linear(offs_t pc) const override { return (pc & ~0x7f) | m_r2l[pc & 0x7f]; }
+ virtual offs_t disassemble(std::ostream &stream, offs_t pc, const data_buffer &opcodes, const data_buffer &params) override;
+
+private:
+ u8 m_l2r[0x80];
+ u8 m_r2l[0x80];
+
+ std::string get_300optype_name(int optype);
+ std::string get_200optype_name(int optype);
+ std::string get_reg_name(int reg);
+};
+
+#endif // MAME_CPU_UPD777_UPD777DASM_H
diff --git a/src/mame/epoch/cassvisn.cpp b/src/mame/epoch/cassvisn.cpp
new file mode 100644
index 00000000000..129d5136955
--- /dev/null
+++ b/src/mame/epoch/cassvisn.cpp
@@ -0,0 +1,112 @@
+// license:BSD-3-Clause
+// copyright-holders:Vas Crabb
+
+/*
+
+for architecture details see
+https://www.oguchi-rd.com/LSI%20products.php
+the linked 'design note' contains a large amount of useful information
+https://www.oguchi-rd.com/777/777%20Design%20Note.pdf
+
+*/
+
+#include "emu.h"
+
+#include "bus/generic/carts.h"
+#include "bus/generic/slot.h"
+#include "cpu/upd777/upd777.h"
+
+#include "softlist_dev.h"
+
+
+namespace {
+
+class cassvisn_state : public driver_device
+{
+public:
+ cassvisn_state(const machine_config &mconfig, device_type type, const char *tag) :
+ driver_device(mconfig, type, tag),
+ m_maincpu(*this, "maincpu"),
+ m_cart(*this, "cartslot"),
+ m_in(*this, "IN%u", 0)
+ { }
+
+ void cassvisn(machine_config &config);
+
+protected:
+ DECLARE_DEVICE_IMAGE_LOAD_MEMBER(cart_load);
+private:
+ u8 input_r(offs_t offset);
+
+ required_device<upd777_cpu_device> m_maincpu;
+ required_device<generic_slot_device> m_cart;
+ required_ioport_array<2> m_in;
+};
+
+u8 cassvisn_state::input_r(offs_t offset)
+{
+ return m_in[offset & 1]->read();
+}
+
+static INPUT_PORTS_START( cassvisn )
+ PORT_START("IN0")
+ PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_START1 )
+ PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT )
+ PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT )
+ PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_START2 )
+ PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_BUTTON4 )
+ PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_BUTTON3 )
+ PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_BUTTON1 ) // Jump on elvpanic
+
+ PORT_START("IN1")
+ PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_BUTTON10 )
+ PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_BUTTON9 )
+ PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_BUTTON8 )
+ PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_BUTTON7 )
+ PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_BUTTON6 )
+ PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_BUTTON5 )
+ PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_BUTTON2 ) // Jump on monstrmn
+INPUT_PORTS_END
+
+DEVICE_IMAGE_LOAD_MEMBER(cassvisn_state::cart_load)
+{
+ if (!image.loaded_through_softlist())
+ return std::make_pair(image_error::UNSUPPORTED, "Cartridges must be loaded from the software list");
+
+ auto const prgsize = m_cart->common_get_size("prg");
+ if (prgsize != 0xf00)
+ return std::make_pair(image_error::BADSOFTWARE, "prg region must be 0xf00 bytes in size");
+
+ m_cart->rom_alloc(prgsize, GENERIC_ROM16_WIDTH, ENDIANNESS_BIG);
+ m_cart->common_load_rom(m_cart->get_rom_base(), prgsize, "prg");
+ uint16_t *prgbase = m_maincpu->get_prgregion();
+ memcpy(prgbase, m_cart->get_rom_base(), prgsize);
+
+ auto const patsize = m_cart->common_get_size("pat");
+ if (patsize != 0x4d0)
+ return std::make_pair(image_error::BADSOFTWARE, "pat region patsize must be 0x4d0 bytes in size");
+
+ m_cart->common_load_rom(m_maincpu->get_patregion(), patsize, "pat");
+
+ return std::make_pair(std::error_condition(), std::string());
+}
+
+void cassvisn_state::cassvisn(machine_config &config)
+{
+ UPD777(config, m_maincpu, 2'000'000); // frequency? UPD774 / UPD778 in some carts?
+ m_maincpu->in_cb().set(FUNC(cassvisn_state::input_r));
+
+ GENERIC_CARTSLOT(config, m_cart, generic_plain_slot, "cassvisn_cart");
+ m_cart->set_width(GENERIC_ROM16_WIDTH);
+ m_cart->set_device_load(FUNC(cassvisn_state::cart_load));
+ m_cart->set_must_be_loaded(true);
+
+ SOFTWARE_LIST(config, "cart_list").set_original("cassvisn_cart");
+}
+
+ROM_START( cassvisn )
+ROM_END
+
+} // anonymous namespace
+
+CONS( 1981, cassvisn, 0, 0, cassvisn, cassvisn, cassvisn_state, empty_init, "Epoch", "Cassette Vision", MACHINE_IS_SKELETON )
diff --git a/src/mame/mame.lst b/src/mame/mame.lst
index 73ef62b4c7a..d7955d65016 100644
--- a/src/mame/mame.lst
+++ b/src/mame/mame.lst
@@ -17869,6 +17869,9 @@ touryuu // (c) 200? Yuki Enterprise
@source:eolith/vegaeo.cpp
crazywar // (c) 2002 Eolith
+@source:epoch/cassvisn.cpp
+cassvisn
+
@source:epoch/gamepock.cpp
gamepock // Epoch Game Pocket Computer
diff --git a/src/tools/unidasm.cpp b/src/tools/unidasm.cpp
index 4ccce7f1eeb..dadf946e5ac 100644
--- a/src/tools/unidasm.cpp
+++ b/src/tools/unidasm.cpp
@@ -197,6 +197,7 @@ using util::BIT;
#include "cpu/unsp/unspdasm.h"
#include "cpu/upd177x/upd177xd.h"
#include "cpu/upd7725/dasm7725.h"
+#include "cpu/upd777/upd777dasm.h"
#include "cpu/upd7810/upd7810_dasm.h"
#include "cpu/upd78k/upd78k0d.h"
#include "cpu/upd78k/upd78k1d.h"
@@ -673,6 +674,7 @@ static const dasm_table_entry dasm_table[] =
{ "unsp12", be, -1, []() -> util::disasm_interface * { return new unsp_12_disassembler; } },
{ "unsp20", be, -1, []() -> util::disasm_interface * { return new unsp_20_disassembler; } },
{ "upd177x", be, -1, []() -> util::disasm_interface * { return new upd177x_disassembler; } },
+ { "upd777", be, -1, []() -> util::disasm_interface * { return new upd777_disassembler; } },
{ "upd7725", be, -2, []() -> util::disasm_interface * { return new necdsp_disassembler; } },
{ "upd7801", le, 0, []() -> util::disasm_interface * { return new upd7801_disassembler; } },
{ "upd78c05", le, 0, []() -> util::disasm_interface * { return new upd78c05_disassembler; } },