summaryrefslogtreecommitdiffstatshomepage
diff options
context:
space:
mode:
-rw-r--r--scripts/src/cpu.lua28
-rw-r--r--src/devices/cpu/z80/z80.cpp91
-rw-r--r--src/devices/cpu/z80/z80.inc90
-rw-r--r--src/devices/cpu/z80/z80.lst188
-rw-r--r--src/devices/cpu/z80/z80dasm.cpp20
-rw-r--r--src/devices/cpu/z80/z80dasm.h7
-rw-r--r--src/devices/cpu/z80/z80n.cpp43
-rw-r--r--src/devices/cpu/z80/z80n.h44
-rw-r--r--src/devices/cpu/z80/z80ndasm.cpp76
-rw-r--r--src/devices/cpu/z80/z80ndasm.h23
-rw-r--r--src/mame/mame.lst3
-rw-r--r--src/mame/sinclair/screen_ula.cpp4
-rw-r--r--src/mame/sinclair/specnext.cpp3531
-rw-r--r--src/mame/sinclair/specnext_copper.cpp1
-rw-r--r--src/mame/sinclair/specnext_copper.h4
-rw-r--r--src/mame/sinclair/specnext_ctc.h1
-rw-r--r--src/mame/sinclair/specnext_dma.h4
-rw-r--r--src/mame/sinclair/specnext_layer2.cpp183
-rw-r--r--src/mame/sinclair/specnext_layer2.h66
-rw-r--r--src/mame/sinclair/specnext_lores.cpp111
-rw-r--r--src/mame/sinclair/specnext_lores.h65
-rw-r--r--src/mame/sinclair/specnext_multiface.h4
-rw-r--r--src/mame/sinclair/specnext_sprites.cpp322
-rw-r--r--src/mame/sinclair/specnext_sprites.h96
-rw-r--r--src/mame/sinclair/specnext_tiles.cpp157
-rw-r--r--src/mame/sinclair/specnext_tiles.h76
-rw-r--r--src/osd/modules/debugger/debuggdbstub.cpp1
27 files changed, 5132 insertions, 107 deletions
diff --git a/scripts/src/cpu.lua b/scripts/src/cpu.lua
index aa9fe9852d1..3f2f6f2de42 100644
--- a/scripts/src/cpu.lua
+++ b/scripts/src/cpu.lua
@@ -2950,13 +2950,14 @@ end
--------------------------------------------------
-- Zilog Z80
--@src/devices/cpu/z80/z80.h,CPUS["Z80"] = true
+--@src/devices/cpu/z80/z80n.h,CPUS["Z80N"] = true
--@src/devices/cpu/z80/kc82.h,CPUS["KC80"] = true
--@src/devices/cpu/z80/kl5c80a12.h,CPUS["KC80"] = true
--@src/devices/cpu/z80/kl5c80a16.h,CPUS["KC80"] = true
--@src/devices/cpu/z80/ky80.h,CPUS["KC80"] = true
--------------------------------------------------
-if (CPUS["Z80"]~=null or CPUS["KC80"]~=null) then
+if CPUS["Z80"] or CPUS["KC80"] or CPUS["Z80N"] then
files {
MAME_DIR .. "src/devices/cpu/z80/z80.cpp",
MAME_DIR .. "src/devices/cpu/z80/z80.h",
@@ -2978,12 +2979,27 @@ if (CPUS["Z80"]~=null or CPUS["KC80"]~=null) then
dependency {
{ MAME_DIR .. "src/devices/cpu/z80/z80.cpp", GEN_DIR .. "emu/cpu/z80/z80.hxx" },
- { MAME_DIR .. "src/devices/cpu/z80/z80.cpp", GEN_DIR .. "emu/cpu/z80/z80_ncs800.hxx" },
+ { MAME_DIR .. "src/devices/cpu/z80/z80.cpp", GEN_DIR .. "emu/cpu/z80/ncs800.hxx" },
}
custombuildtask {
{ MAME_DIR .. "src/devices/cpu/z80/z80.lst", GEN_DIR .. "emu/cpu/z80/z80.hxx", { MAME_DIR .. "src/devices/cpu/z80/z80make.py" }, { "@echo Generating Z80 source file...", PYTHON .. " $(1) $(<) $(@)" } },
- { MAME_DIR .. "src/devices/cpu/z80/z80.lst", GEN_DIR .. "emu/cpu/z80/z80_ncs800.hxx", { MAME_DIR .. "src/devices/cpu/z80/z80make.py" }, { "@echo Generating NSC800 source file...", PYTHON .. " $(1) ncs800 $(<) $(@)" } },
+ { MAME_DIR .. "src/devices/cpu/z80/z80.lst", GEN_DIR .. "emu/cpu/z80/ncs800.hxx", { MAME_DIR .. "src/devices/cpu/z80/z80make.py" }, { "@echo Generating NSC800 source file...", PYTHON .. " $(1) ncs800 $(<) $(@)" } },
+ }
+end
+
+if CPUS["Z80N"] then
+ files {
+ MAME_DIR .. "src/devices/cpu/z80/z80n.cpp",
+ MAME_DIR .. "src/devices/cpu/z80/z80n.h",
+ }
+
+ dependency {
+ { MAME_DIR .. "src/devices/cpu/z80/z80n.cpp", GEN_DIR .. "emu/cpu/z80/z80n.hxx" },
+ }
+
+ custombuildtask {
+ { MAME_DIR .. "src/devices/cpu/z80/z80.lst", GEN_DIR .. "emu/cpu/z80/z80n.hxx", { MAME_DIR .. "src/devices/cpu/z80/z80make.py" }, { "@echo Generating Z80N source file...", PYTHON .. " $(1) z80n $(<) $(@)" } },
}
end
@@ -3016,6 +3032,12 @@ if want_disasm_z80 or want_disasm_kc80 then
table.insert(disasm_files , MAME_DIR .. "src/devices/cpu/z80/z80dasm.h")
end
+if opt_tool(CPUS, "Z80N") then
+ table.insert(disasm_files , MAME_DIR .. "src/devices/cpu/z80/z80ndasm.cpp")
+ table.insert(disasm_files , MAME_DIR .. "src/devices/cpu/z80/z80dasm.cpp")
+ table.insert(disasm_files , MAME_DIR .. "src/devices/cpu/z80/z80ndasm.h")
+end
+
--------------------------------------------------
-- Sharp LR35902 (Game Boy CPU)
--@src/devices/cpu/lr35902/lr35902.h,CPUS["LR35902"] = true
diff --git a/src/devices/cpu/z80/z80.cpp b/src/devices/cpu/z80/z80.cpp
index 83d2b001f33..1912054de00 100644
--- a/src/devices/cpu/z80/z80.cpp
+++ b/src/devices/cpu/z80/z80.cpp
@@ -114,96 +114,7 @@
#include "z80.h"
#include "z80dasm.h"
-#define LOG_UNDOC (1U << 1)
-#define LOG_INT (1U << 2)
-#define LOG_TIME (1U << 3)
-
-#define VERBOSE ( LOG_UNDOC /*| LOG_INT*/ )
-#include "logmacro.h"
-
-#define LOGUNDOC(...) LOGMASKED(LOG_UNDOC, __VA_ARGS__)
-#define LOGINT(...) LOGMASKED(LOG_INT, __VA_ARGS__)
-
-
-/* On an NMOS Z80, if LD A,I or LD A,R is interrupted, P/V flag gets reset,
- even if IFF2 was set before this instruction. This issue was fixed on
- the CMOS Z80, so until knowing (most) Z80 types on hardware, it's disabled */
-#define HAS_LDAIR_QUIRK 0
-
-
-/****************************************************************************
- * The Z80 registers. halt is set to 1 when the CPU is halted, the refresh
- * register is calculated as follows: refresh = (r & 127) | (r2 & 128)
- ****************************************************************************/
-#define CF 0x01
-#define NF 0x02
-#define PF 0x04
-#define VF PF
-#define XF 0x08
-#define HF 0x10
-#define YF 0x20
-#define ZF 0x40
-#define SF 0x80
-
-#define INT_IRQ 0x01
-#define NMI_IRQ 0x02
-
-#define PRVPC m_prvpc.d // previous program counter
-
-#define PCD m_pc.d
-#define PC m_pc.w.l
-
-#define SPD m_sp.d
-#define SP m_sp.w.l
-
-#define AFD m_af.d
-#define AF m_af.w.l
-#define A m_af.b.h
-#define F m_af.b.l
-#define Q m_q
-#define QT m_qtemp
-#define I m_i
-#define R m_r
-#define R2 m_r2
-
-#define BCD m_bc.d
-#define BC m_bc.w.l
-#define B m_bc.b.h
-#define C m_bc.b.l
-
-#define DED m_de.d
-#define DE m_de.w.l
-#define D m_de.b.h
-#define E m_de.b.l
-
-#define HLD m_hl.d
-#define HL m_hl.w.l
-#define H m_hl.b.h
-#define L m_hl.b.l
-
-#define IXD m_ix.d
-#define IX m_ix.w.l
-#define HX m_ix.b.h
-#define LX m_ix.b.l
-
-#define IYD m_iy.d
-#define IY m_iy.w.l
-#define HY m_iy.b.h
-#define LY m_iy.b.l
-
-#define WZ m_wz.w.l
-#define WZ_H m_wz.b.h
-#define WZ_L m_wz.b.l
-
-
-#define TADR m_shared_addr.w // Typically represents values from A0..15 pins. 16bit input in steps.
-#define TADR_H m_shared_addr.b.h
-#define TADR_L m_shared_addr.b.l
-#define TDAT m_shared_data.w // 16bit input(if use as second parameter) or output in steps.
-#define TDAT2 m_shared_data2.w
-#define TDAT_H m_shared_data.b.h
-#define TDAT_L m_shared_data.b.l
-#define TDAT8 m_shared_data.b.l // Typically represents values from D0..8 pins. 8bit input or output in steps.
+#include "z80.inc"
static bool tables_initialised = false;
std::unique_ptr<u8[]> z80_device::SZ = std::make_unique<u8[]>(0x100); // zero and sign flags
diff --git a/src/devices/cpu/z80/z80.inc b/src/devices/cpu/z80/z80.inc
new file mode 100644
index 00000000000..95e41cae5d9
--- /dev/null
+++ b/src/devices/cpu/z80/z80.inc
@@ -0,0 +1,90 @@
+#define LOG_UNDOC (1U << 1)
+#define LOG_INT (1U << 2)
+#define LOG_TIME (1U << 3)
+
+#define VERBOSE ( LOG_UNDOC /*| LOG_INT*/ )
+#include "logmacro.h"
+
+#define LOGUNDOC(...) LOGMASKED(LOG_UNDOC, __VA_ARGS__)
+#define LOGINT(...) LOGMASKED(LOG_INT, __VA_ARGS__)
+
+
+/* On an NMOS Z80, if LD A,I or LD A,R is interrupted, P/V flag gets reset,
+ even if IFF2 was set before this instruction. This issue was fixed on
+ the CMOS Z80, so until knowing (most) Z80 types on hardware, it's disabled */
+#define HAS_LDAIR_QUIRK 0
+
+
+/****************************************************************************
+ * The Z80 registers. halt is set to 1 when the CPU is halted, the refresh
+ * register is calculated as follows: refresh = (r & 127) | (r2 & 128)
+ ****************************************************************************/
+#define CF 0x01
+#define NF 0x02
+#define PF 0x04
+#define VF PF
+#define XF 0x08
+#define HF 0x10
+#define YF 0x20
+#define ZF 0x40
+#define SF 0x80
+
+#define INT_IRQ 0x01
+#define NMI_IRQ 0x02
+
+#define PRVPC m_prvpc.d // previous program counter
+
+#define PCD m_pc.d
+#define PC m_pc.w.l
+
+#define SPD m_sp.d
+#define SP m_sp.w.l
+
+#define AFD m_af.d
+#define AF m_af.w.l
+#define A m_af.b.h
+#define F m_af.b.l
+#define Q m_q
+#define QT m_qtemp
+#define I m_i
+#define R m_r
+#define R2 m_r2
+
+#define BCD m_bc.d
+#define BC m_bc.w.l
+#define B m_bc.b.h
+#define C m_bc.b.l
+
+#define DED m_de.d
+#define DE m_de.w.l
+#define D m_de.b.h
+#define E m_de.b.l
+
+#define HLD m_hl.d
+#define HL m_hl.w.l
+#define H m_hl.b.h
+#define L m_hl.b.l
+
+#define IXD m_ix.d
+#define IX m_ix.w.l
+#define HX m_ix.b.h
+#define LX m_ix.b.l
+
+#define IYD m_iy.d
+#define IY m_iy.w.l
+#define HY m_iy.b.h
+#define LY m_iy.b.l
+
+#define WZ m_wz.w.l
+#define WZ_H m_wz.b.h
+#define WZ_L m_wz.b.l
+
+
+#define TADR m_shared_addr.w // Typically represents values from A0..15 pins. 16bit input in steps.
+#define TADR_H m_shared_addr.b.h
+#define TADR_L m_shared_addr.b.l
+#define TDAT m_shared_data.w // 16bit input(if use as second parameter) or output in steps.
+#define TDAT2 m_shared_data2.w
+#define TDAT_H m_shared_data.b.h
+#define TDAT_L m_shared_data.b.l
+#define TDAT8 m_shared_data.b.l // Typically represents values from D0..8 pins. 8bit input or output in steps.
diff --git a/src/devices/cpu/z80/z80.lst b/src/devices/cpu/z80/z80.lst
index eaaaec631de..0d23acf39c6 100644
--- a/src/devices/cpu/z80/z80.lst
+++ b/src/devices/cpu/z80/z80.lst
@@ -132,6 +132,20 @@ macro retn
call pop
PC = TDAT; LOGINT("RETN m_iff1:%d m_iff2:%d\n", m_iff1, m_iff2); WZ = PC; m_iff1 = m_iff2;
+macro z80n:retn
+ m_out_retn_seen_cb(0);
+ call pop
+ if (m_stackless)
+ {
+ m_pc.b.l = m_in_nextreg_cb(0xc2);
+ m_pc.b.h = m_in_nextreg_cb(0xc3);
+ }
+ else
+ {
+ PC = TDAT;
+ }
+ LOGINT("RETN m_iff1:%d m_iff2:%d\n", m_iff1, m_iff2); WZ = PC; m_iff1 = m_iff2;
+
macro reti
call pop
PC = TDAT; WZ = PC; m_iff1 = m_iff2; daisy_call_reti_device();
@@ -432,6 +446,18 @@ macro check_interrupts
call take_interrupt
}
+macro z80n:check_interrupts
+ if (m_nmi_pending) {
+ if (m_stackless) {
+ // on take_nmi
+ m_out_nextreg_cb(0xc2, m_pc.b.l);
+ m_out_nextreg_cb(0xc3, m_pc.b.h);
+ }
+ call take_nmi
+ } else if (m_irq_state != CLEAR_LINE && m_iff1 && !m_after_ei) {
+ call take_interrupt
+ }
+
macro nsc800_take_interrupt
// Check if processor was halted
leave_halt();
@@ -6644,3 +6670,165 @@ edff # DB ED
00ff # RST 7
call rst 0x38
+
+
+##########################################################
+# Z80N
+##########################################################
+macro ldix
+ {
+ TADR = HL;
+ call rm
+ if (TDAT8 != A) {
+ TADR = DE;
+ call wm
+ ;
+ }
+ HL++;
+ DE++;
+ BC--;
+ }
+
+macro lddx
+ {
+ TADR = HL;
+ call rm
+ if (TDAT8 != A) {
+ TADR = DE;
+ call wm
+ ;
+ }
+ DE++;
+ HL--;
+ BC--;
+ }
+
+z80n:ed23 # swapnib
+ A = (A << 4) | (A >> 4);
+
+z80n:ed24 # mirror a
+ A = bitswap<8>(A, 0, 1, 2, 3, 4, 5, 6, 7);
+
+z80n:ed27 # test *
+ TDAT_H = A;
+ call arg
+ and_a(TDAT8);
+ A = TDAT_H;
+
+z80n:ed28 # bsla de,b
+ DE <<= std::min(B & 31, 16);
+
+z80n:ed29 # bsra de,b
+ {
+ const u16 fill = (DE & 0x8000) ? ~u16(0) : u16(0);
+ DE = (DE >> std::min(B & 31, 16)) | (fill << (16 - std::min(B & 31, 16)));
+ }
+
+z80n:ed2a # bsrl de,b
+ DE >>= std::min(B & 31, 16);
+
+z80n:ed2b # bsrf de,b
+ DE = (DE >> std::min(B & 31, 16)) | (~u16(0) << (16 - std::min(B & 31, 16)));
+
+z80n:ed2c # brlc de,b
+ DE = (DE << (B & 15)) | (DE >> (16 - (B & 15)));
+
+z80n:ed30 # mul d,e
+ DE = D * E;
+
+z80n:ed31 # add hl,a
+ HL += A;
+
+z80n:ed32 # add de,a
+ DE += A;
+
+z80n:ed33 # add bc,a
+ BC += A;
+
+z80n:ed34 # add hl,**
+ call arg16
+ HL += TDAT;
+
+z80n:ed35 # add de,**
+ call arg16
+ DE += TDAT;
+
+z80n:ed36 # add bc,**
+ call arg16
+ BC += TDAT;
+
+z80n:ed8a # push **
+ call arg16
+ using std::swap; swap(TDAT_H, TDAT_L);
+ call push
+
+z80n:ed90 # outinb
+ TADR = HL;
+ call rm
+ TADR = BC;
+ call out
+ HL++;
+
+z80n:ed91 # nextreg *,*
+ call arg16
+ m_out_nextreg_cb(TDAT_L, TDAT_H);
+
+z80n:ed92 # nextreg *,a
+ call arg
+ m_out_nextreg_cb(TDAT8, A);
+
+z80n:ed93 # pixeldn
+ if (0x07 != (H & 0x07))
+ HL = HL + 0x100;
+ else if (0xe0 != (L & 0xe0))
+ HL = (HL & 0xf8ff) + 0x20;
+ else
+ HL = (HL & 0xf81f) + 0x800;
+
+z80n:ed94 # pixelad
+ HL = 0x4000 + ((D & 0xc0) << 5) + ((D & 0x07) << 8) + ((D & 0x38) << 2) + (E >> 3);
+
+z80n:ed95 # setae
+ A = 0x80 >> (E & 7);
+
+z80n:ed98 # jp (c)
+ TADR = BC;
+ call in
+ PCD = (PCD & 0xc000) + (TDAT8 << 6);
+
+z80n:eda4 # ldix
+ call ldix
+
+z80n:eda5 # ldws
+ TADR = HL;
+ call rm
+ TADR = DE;
+ call wm
+ L++;
+ inc(D);
+
+z80n:edac # lddx
+ call lddx
+
+z80n:edb4 # ldirx
+ call ldix
+ if (BC != 0)
+ PC -= 2;
+
+z80n:edb7 # ldpirx
+ TADR = (HL & 0xfff8) + (E & 7);
+ call rm
+ if (TDAT8 != A) {
+ TADR = DE;
+ call wm
+ ;
+ }
+ DE++;
+ BC--;
+ if (BC != 0)
+ PC -= 2;
+
+z80n:edbc # lddrx
+ call lddx
+ if (BC != 0)
+ PC -= 2;
diff --git a/src/devices/cpu/z80/z80dasm.cpp b/src/devices/cpu/z80/z80dasm.cpp
index 47c1049e98c..6970d55c28d 100644
--- a/src/devices/cpu/z80/z80dasm.cpp
+++ b/src/devices/cpu/z80/z80dasm.cpp
@@ -22,6 +22,11 @@ static const char *const s_mnemonic[] =
"retn","rl" ,"rla" ,"rlc" ,"rlca","rld" ,"rr" ,"rra" ,
"rrc" ,"rrca","rrd" ,"rst" ,"sbc" ,"scf" ,"set" ,"sla" ,
"sll" ,"sra" ,"srl" ,"sub" ,"xor "
+ // z80n
+ ,"swap","mirr","test",
+ "bsla","bsra","bsrl","bsrf","brlc","mul" ,"otib","nreg",
+ "pxdn","pxad","stae","ldix","ldws","lddx","lirx","lprx",
+ "ldrx"
};
const u32 z80_disassembler::s_flags[] =
@@ -35,6 +40,11 @@ const u32 z80_disassembler::s_flags[] =
STEP_OUT ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,
0 ,0 ,0 ,STEP_OVER,0 ,0 ,0 ,0 ,
0 ,0 ,0 ,0 ,0
+ // z80n
+ ,0 ,0 ,0 ,
+ 0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,
+ 0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,
+ 0
};
const z80_disassembler::z80dasm z80_disassembler::mnemonic_xx_cb[256] =
@@ -215,13 +225,13 @@ const z80_disassembler::z80dasm z80_disassembler::mnemonic_ed[256] =
{zDB,"?"}, {zDB,"?"}, {zDB,"?"}, {zDB,"?"},
{zDB,"?"}, {zDB,"?"}, {zDB,"?"}, {zDB,"?"},
{zDB,"?"}, {zDB,"?"}, {zDB,"?"}, {zDB,"?"},
- {zLDI,nullptr}, {zCPI,nullptr}, {zINI,nullptr}, {zOUTI,nullptr},
+ {zLDI,nullptr}, {zCPI,nullptr}, {zINI,nullptr}, {zOUTI,nullptr},
{zDB,"?"}, {zDB,"?"}, {zDB,"?"}, {zDB,"?"},
- {zLDD,nullptr}, {zCPD,nullptr}, {zIND,nullptr}, {zOUTD,nullptr},
+ {zLDD,nullptr}, {zCPD,nullptr}, {zIND,nullptr}, {zOUTD,nullptr},
{zDB,"?"}, {zDB,"?"}, {zDB,"?"}, {zDB,"?"},
- {zLDIR,nullptr}, {zCPIR,nullptr}, {zINIR,nullptr}, {zOTIR,nullptr},
+ {zLDIR,nullptr},{zCPIR,nullptr},{zINIR,nullptr},{zOTIR,nullptr},
{zDB,"?"}, {zDB,"?"}, {zDB,"?"}, {zDB,"?"},
- {zLDDR,nullptr}, {zCPDR,nullptr}, {zINDR,nullptr}, {zOTDR,nullptr},
+ {zLDDR,nullptr},{zCPDR,nullptr},{zINDR,nullptr},{zOTDR,nullptr},
{zDB,"?"}, {zDB,"?"}, {zDB,"?"}, {zDB,"?"},
{zDB,"?"}, {zDB,"?"}, {zDB,"?"}, {zDB,"?"},
{zDB,"?"}, {zDB,"?"}, {zDB,"?"}, {zDB,"?"},
@@ -414,7 +424,7 @@ offs_t z80_disassembler::disassemble(std::ostream &stream, offs_t pc, const data
d = &mnemonic_cb[op];
break;
case 0xed:
- d = &mnemonic_ed[opcodes.r8(pos++)];
+ d = &get_mnemonic_ed(opcodes.r8(pos++));
if (d->mnemonic == zDB)
pos--;
break;
diff --git a/src/devices/cpu/z80/z80dasm.h b/src/devices/cpu/z80/z80dasm.h
index 774f4df89a8..aa4f8fb43e1 100644
--- a/src/devices/cpu/z80/z80dasm.h
+++ b/src/devices/cpu/z80/z80dasm.h
@@ -30,6 +30,7 @@ protected:
enum e_mnemonics
{
+ // z80
zADC ,zADD ,zAND ,zBIT ,zCALL ,zCCF ,zCP ,zCPD ,
zCPDR ,zCPI ,zCPIR ,zCPL ,zDAA ,zDB ,zDEC ,zDI ,
zDJNZ ,zEI ,zEX ,zEXX ,zHLT ,zIM ,zIN ,zINC ,
@@ -39,6 +40,11 @@ protected:
zRETN ,zRL ,zRLA ,zRLC ,zRLCA ,zRLD ,zRR ,zRRA ,
zRRC ,zRRCA ,zRRD ,zRST ,zSBC ,zSCF ,zSET ,zSLA ,
zSLL ,zSRA ,zSRL ,zSUB ,zXOR
+ // z80n
+ ,zSWAP ,zMIRR ,zTEST ,
+ zBSLA ,zBSRA ,zBSRL ,zBSRF ,zBRLC ,zMUL ,zOTIB ,zNREG ,
+ zPXDN ,zPXAD ,zSTAE ,zLDIX ,zLDWS ,zLDDX ,zLIRX ,zLPRX ,
+ zLDRX
};
static inline char sign(s8 offset);
@@ -51,6 +57,7 @@ protected:
static const z80dasm mnemonic_xx[256];
static const z80dasm mnemonic_main[256];
+ virtual const z80dasm &get_mnemonic_ed(u8 opcode) { return mnemonic_ed[opcode]; }
};
#endif
diff --git a/src/devices/cpu/z80/z80n.cpp b/src/devices/cpu/z80/z80n.cpp
new file mode 100644
index 00000000000..6fc1751373b
--- /dev/null
+++ b/src/devices/cpu/z80/z80n.cpp
@@ -0,0 +1,43 @@
+// license:BSD-3-Clause
+// copyright-holders:Andrei I. Holub
+/***************************************************************************
+ Z80N
+***************************************************************************/
+
+#include "emu.h"
+#include "z80n.h"
+#include "z80ndasm.h"
+
+#include "z80.inc"
+
+DEFINE_DEVICE_TYPE(Z80N, z80n_device, "z80n", "Z80N")
+
+std::unique_ptr<util::disasm_interface> z80n_device::create_disassembler()
+{
+ return std::make_unique<z80n_disassembler>();
+}
+
+z80n_device::z80n_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock)
+ : z80_device(mconfig, Z80N, tag, owner, clock)
+ , m_out_retn_seen_cb(*this)
+ , m_in_nextreg_cb(*this, 0)
+ , m_out_nextreg_cb(*this)
+{
+}
+
+void z80n_device::do_op()
+{
+ #include "cpu/z80/z80n.hxx"
+}
+
+void z80n_device::device_start()
+{
+ z80_device::device_start();
+ save_item(NAME(m_stackless));
+}
+
+void z80n_device::device_reset()
+{
+ z80_device::device_reset();
+ m_stackless = 0;
+}
diff --git a/src/devices/cpu/z80/z80n.h b/src/devices/cpu/z80/z80n.h
new file mode 100644
index 00000000000..d2247a066c2
--- /dev/null
+++ b/src/devices/cpu/z80/z80n.h
@@ -0,0 +1,44 @@
+// license:BSD-3-Clause
+// copyright-holders:Andrei I. Holub
+#ifndef MAME_CPU_Z80_Z80N_H
+#define MAME_CPU_Z80_Z80N_H
+
+#pragma once
+
+#include "z80.h"
+
+class z80n_device : public z80_device
+{
+public:
+ // construction/destruction
+ z80n_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock);
+
+ auto in_nextreg_cb() { return m_in_nextreg_cb.bind(); }
+ auto out_nextreg_cb() { return m_out_nextreg_cb.bind(); }
+ auto out_retn_seen_cb() { return m_out_retn_seen_cb.bind(); }
+
+ bool nmi_stackless_r() { return m_stackless; }
+ void nmi_stackless_w(bool data) { m_stackless = data; }
+
+ void nmi() { m_nmi_pending = true; }
+
+protected:
+ virtual void device_start() override;
+ virtual void device_reset() override;
+
+ virtual void do_op() override;
+
+ devcb_write8 m_out_retn_seen_cb;
+ devcb_read8 m_in_nextreg_cb;
+ devcb_write8 m_out_nextreg_cb;
+
+ virtual std::unique_ptr<util::disasm_interface> create_disassembler() override;
+
+private:
+ bool m_stackless;
+
+};
+
+DECLARE_DEVICE_TYPE(Z80N, z80n_device)
+
+#endif // MAME_CPU_Z80_Z80N_H
diff --git a/src/devices/cpu/z80/z80ndasm.cpp b/src/devices/cpu/z80/z80ndasm.cpp
new file mode 100644
index 00000000000..402a682c427
--- /dev/null
+++ b/src/devices/cpu/z80/z80ndasm.cpp
@@ -0,0 +1,76 @@
+// license:BSD-3-Clause
+/*****************************************************************************
+ *
+ * Z80N disassembler
+ *
+ *****************************************************************************/
+
+#include "z80ndasm.h"
+
+const z80_disassembler::z80dasm z80n_disassembler::mnemonic_ed_n[256] =
+{
+ {zDB,"?"}, {zDB,"?"}, {zDB,"?"}, {zDB,"?"},
+ {zDB,"?"}, {zDB,"?"}, {zDB,"?"}, {zDB,"?"},
+ {zDB,"?"}, {zDB,"?"}, {zDB,"?"}, {zDB,"?"},
+ {zDB,"?"}, {zDB,"?"}, {zDB,"?"}, {zDB,"?"},
+ {zDB,"?"}, {zDB,"?"}, {zDB,"?"}, {zDB,"?"},
+ {zDB,"?"}, {zDB,"?"}, {zDB,"?"}, {zDB,"?"},
+ {zDB,"?"}, {zDB,"?"}, {zDB,"?"}, {zDB,"?"},
+ {zDB,"?"}, {zDB,"?"}, {zDB,"?"}, {zDB,"?"},
+ {zDB,"?"}, {zDB,"?"}, {zDB,"?"}, {zSWAP,nullptr},
+ {zMIRR,nullptr},{zDB,"?"}, {zDB,"?"}, {zTEST,"B"},
+ {zBSLA,"de,b"}, {zBSRA,"de,b"}, {zBSRL,"de,b"}, {zBSRF,"de,b"},
+ {zBRLC,"de,b"}, {zDB,"?"}, {zDB,"?"}, {zDB,"?"},
+ {zMUL,"d,e"}, {zADD,"hl,a"}, {zADD,"de,a"}, {zADD,"bc,a"},
+ {zADD,"hl,W"}, {zADD,"de,W"}, {zADD,"bc,W"}, {zDB,"?"},
+ {zDB,"?"}, {zDB,"?"}, {zDB,"?"}, {zDB,"?"},
+ {zDB,"?"}, {zDB,"?"}, {zDB,"?"}, {zDB,"?"},
+ {zIN,"b,(c)"}, {zOUT,"(c),b"}, {zSBC,"hl,bc"}, {zLD,"(W),bc"},
+ {zNEG,nullptr}, {zRETN,nullptr},{zIM,"0"}, {zLD,"i,a"},
+ {zIN,"c,(c)"}, {zOUT,"(c),c"}, {zADC,"hl,bc"}, {zLD,"bc,(W)"},
+ {zNEG,"*"}, {zRETI,nullptr},{zIM,"0"}, {zLD,"r,a"},
+ {zIN,"d,(c)"}, {zOUT,"(c),d"}, {zSBC,"hl,de"}, {zLD,"(W),de"},
+ {zNEG,"*"}, {zRETN,nullptr},{zIM,"1"}, {zLD,"a,i"},
+ {zIN,"e,(c)"}, {zOUT,"(c),e"}, {zADC,"hl,de"}, {zLD,"de,(W)"},
+ {zNEG,"*"}, {zRETI,nullptr},{zIM,"2"}, {zLD,"a,r"},
+ {zIN,"h,(c)"}, {zOUT,"(c),h"}, {zSBC,"hl,hl"}, {zLD,"(W),hl"},
+ {zNEG,"*"}, {zRETN,nullptr},{zIM,"0"}, {zRRD,"(hl)"},
+ {zIN,"l,(c)"}, {zOUT,"(c),l"}, {zADC,"hl,hl"}, {zLD,"hl,(W)"},
+ {zNEG,"*"}, {zRETI,nullptr},{zIM,"0"}, {zRLD,"(hl)"},
+ {zIN,"0,(c)"}, {zOUT,"(c),0"}, {zSBC,"hl,sp"}, {zLD,"(W),sp"},
+ {zNEG,"*"}, {zRETN,nullptr},{zIM,"1"}, {zDB,"?"},
+ {zIN,"a,(c)"}, {zOUT,"(c),a"}, {zADC,"hl,sp"}, {zLD,"sp,(W)"},
+ {zNEG,"*"}, {zRETI,nullptr},{zIM,"2"}, {zDB,"?"},
+ {zDB,"?"}, {zDB,"?"}, {zDB,"?"}, {zDB,"?"},
+ {zDB,"?"}, {zDB,"?"}, {zDB,"?"}, {zDB,"?"},
+ {zDB,"?"}, {zDB,"?"}, {zPUSH,"B,B"}, {zDB,"?"},
+ {zDB,"?"}, {zDB,"?"}, {zDB,"?"}, {zDB,"?"},
+ {zOTIB,nullptr},{zNREG,"B,B"}, {zNREG,"B,a"}, {zPXDN,nullptr},
+ {zPXAD,nullptr},{zSTAE,nullptr},{zDB,"?"}, {zDB,"?"},
+ {zJP,"(c)"}, {zDB,"?"}, {zDB,"?"}, {zDB,"?"},
+ {zDB,"?"}, {zDB,"?"}, {zDB,"?"}, {zDB,"?"},
+ {zLDI,nullptr}, {zCPI,nullptr}, {zINI,nullptr}, {zOUTI,nullptr},
+ {zLDIX,nullptr},{zLDWS,nullptr},{zDB,"?"}, {zDB,"?"},
+ {zLDD,nullptr}, {zCPD,nullptr}, {zIND,nullptr}, {zOUTD,nullptr},
+ {zLDDX,nullptr},{zDB,"?"}, {zDB,"?"}, {zDB,"?"},
+ {zLDIR,nullptr},{zCPIR,nullptr},{zINIR,nullptr},{zOTIR,nullptr},
+ {zLIRX,nullptr},{zDB,"?"}, {zDB,"?"}, {zLPRX,nullptr},
+ {zLDDR,nullptr},{zCPDR,nullptr},{zINDR,nullptr},{zOTDR,nullptr},
+ {zLDRX,nullptr},{zDB,"?"}, {zDB,"?"}, {zDB,"?"},
+ {zDB,"?"}, {zDB,"?"}, {zDB,"?"}, {zDB,"?"},
+ {zDB,"?"}, {zDB,"?"}, {zDB,"?"}, {zDB,"?"},
+ {zDB,"?"}, {zDB,"?"}, {zDB,"?"}, {zDB,"?"},
+ {zDB,"?"}, {zDB,"?"}, {zDB,"?"}, {zDB,"?"},
+ {zDB,"?"}, {zDB,"?"}, {zDB,"?"}, {zDB,"?"},
+ {zDB,"?"}, {zDB,"?"}, {zDB,"?"}, {zDB,"?"},
+ {zDB,"?"}, {zDB,"?"}, {zDB,"?"}, {zDB,"?"},
+ {zDB,"?"}, {zDB,"?"}, {zDB,"?"}, {zDB,"?"},
+ {zDB,"?"}, {zDB,"?"}, {zDB,"?"}, {zDB,"?"},
+ {zDB,"?"}, {zDB,"?"}, {zDB,"?"}, {zDB,"?"},
+ {zDB,"?"}, {zDB,"?"}, {zDB,"?"}, {zDB,"?"},
+ {zDB,"?"}, {zDB,"?"}, {zDB,"?"}, {zDB,"?"},
+ {zDB,"?"}, {zDB,"?"}, {zDB,"?"}, {zDB,"?"},
+ {zDB,"?"}, {zDB,"?"}, {zDB,"?"}, {zDB,"?"},
+ {zDB,"?"}, {zDB,"?"}, {zDB,"?"}, {zDB,"?"},
+ {zDB,"?"}, {zDB,"?"}, {zDB,"?"}, {zDB,"?"}
+};
diff --git a/src/devices/cpu/z80/z80ndasm.h b/src/devices/cpu/z80/z80ndasm.h
new file mode 100644
index 00000000000..7ae021ced93
--- /dev/null
+++ b/src/devices/cpu/z80/z80ndasm.h
@@ -0,0 +1,23 @@
+// license:BSD-3-Clause
+/*****************************************************************************
+ *
+ * Z80N disassembler
+ *
+ *****************************************************************************/
+
+#ifndef MAME_CPU_Z80_Z80NDASM_H
+#define MAME_CPU_Z80_Z80NDASM_H
+
+#pragma once
+
+#include "z80dasm.h"
+
+class z80n_disassembler : public z80_disassembler
+{
+protected:
+ static const z80dasm mnemonic_ed_n[256];
+
+ virtual const z80dasm &get_mnemonic_ed(u8 opcode) override { return mnemonic_ed_n[opcode]; }
+};
+
+#endif
diff --git a/src/mame/mame.lst b/src/mame/mame.lst
index 286da99a3fd..49725de320d 100644
--- a/src/mame/mame.lst
+++ b/src/mame/mame.lst
@@ -41656,6 +41656,9 @@ zx80 // 1980 Sinclair ZX-80
zx81 // 1981 Sinclair ZX-81
zx97 //
+@source:sinclair/specnext.cpp
+tbblue // ZX Spectrum Next: TBBlue
+
@source:skeleton/600cat.cpp
600cat //
diff --git a/src/mame/sinclair/screen_ula.cpp b/src/mame/sinclair/screen_ula.cpp
index 5bcd64609dc..ef455e80603 100644
--- a/src/mame/sinclair/screen_ula.cpp
+++ b/src/mame/sinclair/screen_ula.cpp
@@ -215,8 +215,8 @@ std::pair<rgb_t, rgb_t> screen_ula_device::parse_attribute(u8 attr)
}
else if (m_ulap_en)
{
- ink = 0xc0 | ((attr & 0xc0) >> 2) | BIT(attr, 0, 3);
- pap = 0xc8 | ((attr & 0xc0) >> 2) | BIT(attr, 3, 3);
+ ink = 0xc0 | ((attr & 0xc0) >> 3) | BIT(attr, 0, 3);
+ pap = 0xe0 | ((attr & 0xc0) >> 3) | BIT(attr, 3, 3);
}
else
{
diff --git a/src/mame/sinclair/specnext.cpp b/src/mame/sinclair/specnext.cpp
new file mode 100644
index 00000000000..28fcfc0446f
--- /dev/null
+++ b/src/mame/sinclair/specnext.cpp
@@ -0,0 +1,3531 @@
+// license:BSD-3-Clause
+// copyright-holders:Andrei I. Holub
+/**********************************************************************
+ Spectrum Next
+
+ Versions: TBBlue 1.2, Issue 0, Issue 1, Issue 2,
+ Issue 2B (Kickstarter 1), Issue 2D, Issue 2E, Issue 2H,
+ Issue 4 (Kickstarter 2)
+ Current implementation is based on Issue 4. Only limited difference
+ tracked through PORT_CONFIG
+
+ TODO:
+ * improve zxnDMA
+ * contention
+ * internal_port_enable() support
+ * (1) invalidate tiles/sprites caches on region w, not every frame
+
+**********************************************************************/
+
+#include "emu.h"
+
+#include "beta_m.h"
+#include "screen_ula.h"
+#include "spec128.h"
+#include "specnext_copper.h"
+#include "specnext_ctc.h"
+#include "specnext_divmmc.h"
+#include "specnext_dma.h"
+#include "specnext_multiface.h"
+#include "specnext_layer2.h"
+#include "specnext_lores.h"
+#include "specnext_sprites.h"
+#include "specnext_tiles.h"
+
+#include "bus/spectrum/zxbus.h"
+#include "cpu/z80/z80n.h"
+#include "machine/i2cmem.h"
+#include "machine/spi_sdcard.h"
+#include "sound/ay8910.h"
+#include "sound/dac.h"
+#include "speaker.h"
+
+
+#define LOG_IO (1U << 1)
+#define LOG_MEM (1U << 2)
+#define LOG_WARN (1U << 3)
+
+#define VERBOSE ( LOG_GENERAL | LOG_IO | /*LOG_MEM |*/ LOG_WARN )
+#include "logmacro.h"
+
+#define LOGIO(...) LOGMASKED(LOG_IO, __VA_ARGS__)
+#define LOGMEM(...) LOGMASKED(LOG_MEM, __VA_ARGS__)
+#define LOGWARN(...) LOGMASKED(LOG_WARN, __VA_ARGS__)
+
+
+namespace {
+
+#define TIMINGS_PERFECT 0
+
+static const u16 CYCLES_HORIZ = (228 * 2) << 1;
+static const u16 CYCLES_VERT = 311;
+static const rectangle SCR_256x192 = { 48 << 1, (304 << 1) - 1, 40, 231 };
+static const rectangle SCR_320x256 = { 16 << 1, (336 << 1) - 1, 8, 263 };
+
+class specnext_state : public spectrum_128_state
+{
+public:
+ specnext_state(const machine_config &mconfig, device_type type, const char *tag)
+ : spectrum_128_state(mconfig, type, tag)
+ , m_maincpu(*this, "maincpu")
+ , m_bank_boot_rom(*this, "bootrom")
+ , m_bank_ram(*this, "bank_ram%u", 0U)
+ , m_view0(*this, "mem_view0")
+ , m_view1(*this, "mem_view1")
+ , m_view2(*this, "mem_view2")
+ , m_view3(*this, "mem_view3")
+ , m_view4(*this, "mem_view4")
+ , m_view5(*this, "mem_view5")
+ , m_view6(*this, "mem_view6")
+ , m_view7(*this, "mem_view7")
+ , m_copper(*this, "copper")
+ , m_ctc(*this, "ctc")
+ , m_dma(*this, "dma")
+ , m_i2cmem(*this, "i2cmem")
+ , m_sdcard(*this, "sdcard")
+ , m_ay(*this, "ay%u", 0U)
+ , m_dac(*this, "dac%u", 0U)
+ , m_palette(*this, "palette")
+ , m_regs_map(*this, "regs_map")
+ , m_mf(*this, "multiface")
+ , m_divmmc(*this, "divmmc")
+ , m_ula(*this, "ula")
+ , m_tiles(*this, "tiles")
+ , m_layer2(*this, "layer2")
+ , m_lores(*this, "lores")
+ , m_sprites(*this, "sprites")
+ , m_io_issue(*this, "ISSUE")
+ , m_io_mouse(*this, "mouse_input%u", 1U)
+ {}
+
+ void tbblue(machine_config &config);
+
+ INPUT_CHANGED_MEMBER(on_mf_nmi);
+ INPUT_CHANGED_MEMBER(on_divmmc_nmi);
+
+protected:
+ virtual void machine_start() override ATTR_COLD;
+ virtual void machine_reset() override ATTR_COLD;
+ void reset_hard();
+ virtual void video_start() override;
+ virtual void spectrum_128_update_memory() override {}
+
+ u8 do_m1(offs_t offset);
+ void do_mf_nmi();
+ void leave_nmi(int status);
+ void map_fetch(address_map &map);
+ void map_mem(address_map &map);
+ void map_io(address_map &map);
+ void map_regs(address_map &map);
+ u8 reg_r(offs_t reg);
+ void reg_w(offs_t reg, u8 data);
+ void mmu_w(offs_t bank, u8 data);
+ void mmu_x2_w(offs_t bank, u8 data);
+ u8 dma_r(bool dma_mode);
+ void dma_w(bool dma_mode, u8 data);
+ u8 spi_data_r();
+ void spi_data_w(u8 data);
+ void spi_miso_w(u8 data);
+ void i2c_scl_w(u8 data);
+ void palette_val_w(u8 nr_palette_priority, u16 nr_palette_value);
+ u8 port_ff_r();
+ void port_ff_w(u8 data);
+ void turbosound_address_w(u8 data);
+ u8 mf_port_r(offs_t addr);
+ void mf_port_w(offs_t addr, u8 data);
+ attotime cooper_until_pos_r(u16 pos);
+
+ void bank_update(u8 bank, u8 count);
+ void bank_update(u8 bank);
+ void memory_change(u16 port, u8 data);
+ u16 get_layer2_active_page(u8 bank);
+ u32 screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect);
+
+ required_device<z80n_device> m_maincpu;
+
+private:
+ static const u8 MACHINE_TBBLUE = 0x08;
+ static const u8 MACHINE_NEXT = 0x0a;
+ static const u8 MACHINE_NEXT_AB = 0xfa; // Anti Brick (reset disabled, bootrom)
+ static const u8 G_VERSION = 0x32; // 3.02
+ static const u8 G_SUB_VERSION = 0x01;
+ static const u8 G_VIDEO_INC = 0b11;
+ static const u16 UTM_FALLBACK_PEN = 0x800;
+
+ virtual TIMER_CALLBACK_MEMBER(irq_off) override;
+ virtual TIMER_CALLBACK_MEMBER(irq_on) override;
+ INTERRUPT_GEN_MEMBER(specnext_interrupt);
+ TIMER_CALLBACK_MEMBER(line_irq_on);
+ INTERRUPT_GEN_MEMBER(line_interrupt);
+ IRQ_CALLBACK_MEMBER(irq_callback);
+ TIMER_CALLBACK_MEMBER(spi_clock);
+ void line_irq_adjust();
+
+ u8 g_machine_id() { return m_io_issue->read() ? MACHINE_NEXT : MACHINE_TBBLUE; }
+ u8 g_board_issue() { return m_io_issue->read(); }
+ bool machine_type_48() const { return m_nr_03_machine_type == 0 || m_nr_03_machine_type == 1; }
+ bool machine_type_128() const { return m_nr_03_machine_type == 2 || m_nr_03_machine_type == 4; }
+ bool machine_type_p3() const { return !machine_type_48() && !machine_type_128(); }
+
+ void nr_02_w(u8 nr_wr_dat);
+ bool nr_02_iotrap() { return m_nr_da_iotrap_cause & 3; }
+ void nr_07_cpu_speed_w(u8 data);
+
+ void nr_0a_mf_type_w(u8 data) { m_nr_0a_mf_type = data; m_mf->mf_mode_w(m_nr_0a_mf_type); }
+ void nr_12_layer2_active_bank_w(u8 data) { m_nr_12_layer2_active_bank = data; bank_update(0, 6); m_layer2->layer2_active_bank_w(m_nr_12_layer2_active_bank); }
+ void nr_13_layer2_shadow_bank_w(u8 data) { m_nr_13_layer2_shadow_bank = data; bank_update(0, 6); }
+ void nr_14_global_transparent_rgb_w(u8 data);
+ void nr_15_sprite_priority_w(bool data) { m_nr_15_sprite_priority = data; m_sprites->zero_on_top_w(m_nr_15_sprite_priority); }
+ void nr_15_sprite_border_clip_en_w(bool data) { m_nr_15_sprite_border_clip_en = data; m_sprites->border_clip_en_w(m_nr_15_sprite_border_clip_en); }
+ void nr_15_sprite_over_border_en_w(bool data) { m_nr_15_sprite_over_border_en = data; m_sprites->over_border_w(m_nr_15_sprite_over_border_en); }
+ void nr_16_layer2_scrollx_w(u8 data) { m_nr_16_layer2_scrollx = data; m_layer2->scroll_x_w((m_nr_71_layer2_scrollx_msb << 8) | m_nr_16_layer2_scrollx); }
+ void nr_17_layer2_scrolly_w(u8 data) { m_nr_17_layer2_scrolly = data; m_layer2->scroll_y_w(m_nr_17_layer2_scrolly); }
+ void nr_18_layer2_clip_x1_w(u8 data) { m_nr_18_layer2_clip_x1 = data; m_layer2->clip_x1_w(m_nr_18_layer2_clip_x1); }
+ void nr_18_layer2_clip_x2_w(u8 data) { m_nr_18_layer2_clip_x2 = data; m_layer2->clip_x2_w(m_nr_18_layer2_clip_x2); }
+ void nr_18_layer2_clip_y1_w(u8 data) { m_nr_18_layer2_clip_y1 = data; m_layer2->clip_y1_w(m_nr_18_layer2_clip_y1); }
+ void nr_18_layer2_clip_y2_w(u8 data) { m_nr_18_layer2_clip_y2 = data; m_layer2->clip_y2_w(m_nr_18_layer2_clip_y2); }
+ void nr_19_sprite_clip_x1_w(u8 data) { m_nr_19_sprite_clip_x1 = data; m_sprites->clip_x1_w(m_nr_19_sprite_clip_x1); }
+ void nr_19_sprite_clip_x2_w(u8 data) { m_nr_19_sprite_clip_x2 = data; m_sprites->clip_x2_w(m_nr_19_sprite_clip_x2); }
+ void nr_19_sprite_clip_y1_w(u8 data) { m_nr_19_sprite_clip_y1 = data; m_sprites->clip_y1_w(m_nr_19_sprite_clip_y1); }
+ void nr_19_sprite_clip_y2_w(u8 data) { m_nr_19_sprite_clip_y2 = data; m_sprites->clip_y2_w(m_nr_19_sprite_clip_y2); }
+ void nr_1a_ula_clip_x1_w(u8 data) { m_nr_1a_ula_clip_x1 = data; m_ula->ula_clip_x1_w(m_nr_1a_ula_clip_x1); m_lores->clip_x1_w(m_nr_1a_ula_clip_x1); }
+ void nr_1a_ula_clip_x2_w(u8 data) { m_nr_1a_ula_clip_x2 = data; m_ula->ula_clip_x2_w(m_nr_1a_ula_clip_x2); m_lores->clip_x2_w(m_nr_1a_ula_clip_x2); }
+ void nr_1a_ula_clip_y1_w(u8 data) { m_nr_1a_ula_clip_y1 = data; m_ula->ula_clip_y1_w(m_nr_1a_ula_clip_y1); m_lores->clip_y1_w(m_nr_1a_ula_clip_y1); }
+ void nr_1a_ula_clip_y2_w(u8 data);
+ void nr_1b_tm_clip_x1_w(u8 data) { m_nr_1b_tm_clip_x1 = data; m_tiles->clip_x1_w(m_nr_1b_tm_clip_x1); }
+ void nr_1b_tm_clip_x2_w(u8 data) { m_nr_1b_tm_clip_x2 = data; m_tiles->clip_x2_w(m_nr_1b_tm_clip_x2); }
+ void nr_1b_tm_clip_y1_w(u8 data) { m_nr_1b_tm_clip_y1 = data; m_tiles->clip_y1_w(m_nr_1b_tm_clip_y1); }
+ void nr_1b_tm_clip_y2_w(u8 data) { m_nr_1b_tm_clip_y2 = data; m_tiles->clip_y2_w(m_nr_1b_tm_clip_y2); }
+ void nr_26_ula_scrollx_w(u8 data) { m_nr_26_ula_scrollx = data; m_ula->ula_scroll_x_w(m_nr_26_ula_scrollx); }
+ void nr_27_ula_scrolly_w(u8 data) { m_nr_27_ula_scrolly = data; m_ula->ula_scroll_y_w(m_nr_27_ula_scrolly); }
+
+ void nr_30_tm_scrollx_w(u16 data) { m_nr_30_tm_scrollx = data; m_tiles->tm_scroll_x_w(m_nr_30_tm_scrollx); }
+ void nr_31_tm_scrolly_w(u8 data) { m_nr_31_tm_scrolly = data; m_tiles->tm_scroll_y_w(m_nr_31_tm_scrolly); }
+ void nr_32_lores_scrollx_w(u8 data) { m_nr_32_lores_scrollx = data; m_lores->scroll_x_w(m_nr_32_lores_scrollx); }
+ void nr_33_lores_scrolly_w(u8 data) { m_nr_33_lores_scrolly = data; m_lores->scroll_y_w(m_nr_33_lores_scrolly); }
+
+ void nr_42_ulanext_format_w(u8 data) { m_nr_42_ulanext_format = data; m_ula->ulanext_format_w(m_nr_42_ulanext_format); }
+ void nr_43_ulanext_en_w(bool data) { m_nr_43_ulanext_en = data; m_ula->ulanext_en_w(m_nr_43_ulanext_en); m_lores->ulap_en_w(m_port_ff3b_ulap_en && !m_nr_43_ulanext_en); }
+ void nr_43_active_ula_palette_w(bool data) { m_nr_43_active_ula_palette = data; m_ula->ula_palette_select_w(m_nr_43_active_ula_palette); m_lores->lores_palette_select_w(m_nr_43_active_ula_palette); }
+ void nr_43_active_layer2_palette_w(bool data) { m_nr_43_active_layer2_palette = data; m_layer2->layer2_palette_select_w(m_nr_43_active_layer2_palette); }
+ void nr_43_active_sprite_palette_w(bool data) { m_nr_43_active_sprite_palette = data; m_sprites->sprite_palette_select_w(m_nr_43_active_sprite_palette); }
+
+ void nr_4b_sprite_transparent_index_w(u8 data) { m_nr_4b_sprite_transparent_index = data; m_sprites->transp_colour_w(m_nr_4b_sprite_transparent_index); }
+ void nr_4c_tm_transparent_index_w(u8 data) { m_nr_4c_tm_transparent_index = data; m_tiles->transp_colour_w(m_nr_4c_tm_transparent_index); }
+
+ void nr_62_copper_mode_w(u8 data) { m_nr_62_copper_mode = data; m_copper->copper_en_w(m_nr_62_copper_mode); }
+ void nr_68_ula_fine_scroll_x_w(bool data) { m_nr_68_ula_fine_scroll_x = data; m_ula->ula_fine_scroll_x_w(m_nr_68_ula_fine_scroll_x); }
+
+ void nr_6a_lores_radastan_w(bool data) { m_nr_6a_lores_radastan = data; m_lores->mode_w(m_nr_6a_lores_radastan); }
+ void nr_6a_lores_radastan_xor_w(bool data) { m_nr_6a_lores_radastan_xor = data; m_lores->dfile_w(BIT(m_port_ff_data, 0) != m_nr_6a_lores_radastan_xor); }
+ void nr_6a_lores_palette_offset_w(u8 data) { m_nr_6a_lores_palette_offset = data; m_lores->lores_palette_offset_w(m_nr_6a_lores_palette_offset); }
+ void nr_6b_tm_control_w(u8 data) { m_nr_6b_tm_control = data; m_tiles->control_w(m_nr_6b_tm_control); }
+ void nr_6c_tm_default_attr_w(u8 data) { m_nr_6c_tm_default_attr = data; m_tiles->default_flags_w(m_nr_6c_tm_default_attr); }
+ void nr_6e_tilemap_base_w(bool data7, u8 data5_0) { m_nr_6e_tilemap_base_7 = data7; m_nr_6e_tilemap_base = data5_0; m_tiles->tm_map_base_w((m_nr_6e_tilemap_base_7 << 6) | m_nr_6e_tilemap_base); }
+ void nr_6f_tilemap_tiles_w(bool data7, u8 data5_0) { m_nr_6f_tilemap_tiles_7 = data7; m_nr_6f_tilemap_tiles = data5_0; m_tiles->tm_tile_base_w((m_nr_6f_tilemap_tiles_7 << 6) | m_nr_6f_tilemap_tiles); }
+
+ void nr_70_layer2_resolution_w(u8 data) { m_nr_70_layer2_resolution = data; m_layer2->resolution_w(m_nr_70_layer2_resolution); }
+ void nr_70_layer2_palette_offset_w(u8 data) { m_nr_70_layer2_palette_offset = data; m_layer2->palette_offset_w(m_nr_70_layer2_palette_offset); }
+ void nr_71_layer2_scrollx_msb_w(bool data) { m_nr_71_layer2_scrollx_msb = data; m_layer2->scroll_x_w((m_nr_71_layer2_scrollx_msb << 8) | m_nr_16_layer2_scrollx); }
+
+ bool nr_8c_altrom_en() const { return BIT(m_nr_8c_altrom, 7); }
+ bool nr_8c_altrom_rw() const { return BIT(m_nr_8c_altrom, 6); }
+ bool nr_8c_altrom_lock_rom1() const { return BIT(m_nr_8c_altrom, 5); }
+ bool nr_8c_altrom_lock_rom0() const { return BIT(m_nr_8c_altrom, 4); }
+
+ bool nr_8f_mapping_mode_profi() const { return 0; }
+ bool nr_8f_mapping_mode_pentagon() const { return m_nr_8f_mapping_mode == 0b10 || nr_8f_mapping_mode_pentagon_1024_en(); }
+ bool nr_8f_mapping_mode_pentagon_1024() const { return m_nr_8f_mapping_mode == 0b11; }
+ bool nr_8f_mapping_mode_pentagon_1024_en() const { return nr_8f_mapping_mode_pentagon_1024() && BIT(~m_port_eff7_data, 2); }
+
+ void nr_c0_im2_vector_w(u8 data) { m_nr_c0_im2_vector = data; m_ctc->write(0, m_nr_c0_im2_vector << 5); }
+
+ u32 internal_port_enable() const;
+ bool port_ff_io_en() const { return BIT(internal_port_enable(), 0); }
+ bool port_7ffd_io_en() const { return BIT(internal_port_enable(), 1); }
+ bool port_dffd_io_en() const { return BIT(internal_port_enable(), 2); }
+ bool port_1ffd_io_en() const { return BIT(internal_port_enable(), 3); }
+ bool port_p3_floating_bus_io_en() const { return BIT(internal_port_enable(), 4); }
+ bool port_dma_6b_io_en() const { return BIT(internal_port_enable(), 5); }
+ bool port_1f_io_en() const { return BIT(internal_port_enable(), 6); }
+ bool port_37_io_en() const { return BIT(internal_port_enable(), 7); }
+ bool port_divmmc_io_en() const { return BIT(internal_port_enable(), 8); }
+ bool port_multiface_io_en() const { return BIT(internal_port_enable(), 9); }
+ bool port_i2c_io_en() const { return BIT(internal_port_enable(), 10); }
+ bool port_spi_io_en() const { return BIT(internal_port_enable(), 11); }
+ bool port_uart_io_en() const { return BIT(internal_port_enable(), 12); }
+ bool port_mouse_io_en() const { return BIT(internal_port_enable(), 13); }
+ bool port_sprite_io_en() const { return BIT(internal_port_enable(), 14); }
+ bool port_layer2_io_en() const { return BIT(internal_port_enable(), 15); }
+ bool port_ay_io_en() const { return BIT(internal_port_enable(), 16); }
+ bool port_dac_sd1_ABCD_1f0f4f5f_io_en() const { return BIT(internal_port_enable(), 17); }
+ bool port_dac_sd2_ABCD_f1f3f9fb_io_en() const { return BIT(internal_port_enable(), 18); }
+ bool port_dac_stereo_AD_3f5f_io_en() const { return BIT(internal_port_enable(), 19); }
+ bool port_dac_stereo_BC_0f4f_io_en() const { return BIT(internal_port_enable(), 20); }
+ bool port_dac_mono_AD_fb_io_en() const { return BIT(internal_port_enable(), 21); }
+ bool port_dac_mono_BC_b3_io_en() const { return BIT(internal_port_enable(), 22); }
+ bool port_dac_mono_AD_df_io_en() const { return BIT(internal_port_enable(), 23); }
+ bool port_ulap_io_en() const { return BIT(internal_port_enable(), 24); }
+ bool port_dma_0b_io_en() const { return BIT(internal_port_enable(), 25); }
+ bool port_eff7_io_en() const { return BIT(internal_port_enable(), 26); }
+ bool port_ctc_io_en() const { return BIT(internal_port_enable(), 27); }
+
+ u8 port_7ffd_bank() const { return (((nr_8f_mapping_mode_pentagon() || nr_8f_mapping_mode_profi()) ? 0 : BIT(m_port_dffd_data, 3)) << 6) | ((!nr_8f_mapping_mode_pentagon() ? BIT(m_port_dffd_data, 2) : (nr_8f_mapping_mode_pentagon_1024_en() && BIT(m_port_7ffd_data, 5))) << 5) | ((nr_8f_mapping_mode_pentagon() ? BIT(m_port_7ffd_data, 6, 2) : (m_port_dffd_data & 3)) << 3) | (m_port_7ffd_data & 7); }
+ bool port_7ffd_shadow() const { return BIT(m_port_7ffd_data, 3); }
+ bool port_7ffd_locked() const { return (nr_8f_mapping_mode_pentagon_1024_en() || (nr_8f_mapping_mode_profi() && BIT(m_port_dffd_data, 4))) ? 0 : BIT(m_port_7ffd_data, 5); }
+ bool port_1ffd_special() const { return BIT(m_port_1ffd_data, 0); }
+ u8 port_1ffd_rom() const { return (BIT(m_port_1ffd_data, 2) << 1) | BIT(m_port_7ffd_data, 4); }
+ void port_7ffd_reg_w(u8 data);
+ bool port_ff_interrupt_disable() { return BIT(m_port_ff_data, 6); }
+
+ void port_123b_layer2_en_w(bool data) { m_screen->update_now(); m_port_123b_layer2_en = data; m_layer2->layer2_en_w(m_port_123b_layer2_en); }
+
+ void port_ff3b_ulap_en_w(bool data) { m_port_ff3b_ulap_en = data; m_ula->ulap_en_w(m_port_ff3b_ulap_en); m_lores->ulap_en_w(m_port_ff3b_ulap_en && !m_nr_43_ulanext_en); }
+ u16 nr_palette_dat();
+
+ void port_e3_reg_w(u8 data);
+ void port_e7_reg_w(u8 data);
+
+ memory_access<8, 0, 0, ENDIANNESS_LITTLE>::specific m_next_regs;
+ memory_access<16, 0, 0, ENDIANNESS_LITTLE>::specific m_program;
+ memory_access<16, 0, 0, ENDIANNESS_LITTLE>::specific m_io;
+ memory_bank_creator m_bank_boot_rom;
+ memory_bank_array_creator<8> m_bank_ram;
+ memory_view m_view0, m_view1, m_view2, m_view3, m_view4, m_view5, m_view6, m_view7;
+ required_device<specnext_copper_device> m_copper;
+ required_device<specnext_ctc_device> m_ctc;
+ required_device<specnext_dma_device> m_dma;
+ required_device<i2cmem_device> m_i2cmem;
+ required_device<spi_sdcard_sdhc_device> m_sdcard;
+ required_device_array<ym2149_device, 3> m_ay;
+ required_device_array<dac_byte_interface, 4> m_dac;
+ required_device<device_palette_interface> m_palette;
+ required_device<address_map_bank_device> m_regs_map;
+ required_device<specnext_multiface_device> m_mf;
+ required_device<specnext_divmmc_device> m_divmmc;
+ required_device<screen_ula_device> m_ula;
+ required_device<specnext_tiles_device> m_tiles;
+ required_device<specnext_layer2_device> m_layer2;
+ required_device<specnext_lores_device> m_lores;
+ required_device<specnext_sprites_device> m_sprites;
+ required_ioport m_io_issue;
+ required_ioport_array<3> m_io_mouse;
+
+ int m_page_shadow[8];
+ bool m_bootrom_en;
+ u8 m_port_ff_data;
+ bool m_port_1ffd_special_old;
+ u8 m_port_1ffd_data;
+ u8 m_port_7ffd_data;
+ u8 m_port_dffd_data;
+ u8 m_port_eff7_data;
+ u8 m_port_e7_reg;
+ u8 m_nr_register;
+ u8 m_port_e3_reg;
+ bool m_divmmc_delayed_check;
+ u16 m_global_transparent;
+
+ u8 m_sram_rom;
+ bool m_sram_rom3;
+ bool m_sram_alt_128_n;
+
+ u8 m_mmu[8];
+ bool m_nr_02_bus_reset;
+ bool m_nr_02_generate_mf_nmi;
+ bool m_nr_02_generate_divmmc_nmi;
+ bool m_nr_02_hard_reset;
+ u8 m_nr_02_reset_type; // u3
+ u8 m_nr_03_machine_type; // u3
+ bool m_nr_03_user_dt_lock;
+ u8 m_nr_03_machine_timing; // u3
+ bool m_nr_03_config_mode;
+ u8 m_nr_04_romram_bank; // u7
+ u8 m_nr_05_joy1; // u2
+ u8 m_nr_05_joy0; // u2
+ u8 m_nr_06_psg_mode; // u2
+ bool m_nr_06_ps2_mode;
+ bool m_nr_06_button_m1_nmi_en;
+ bool m_nr_06_button_drive_nmi_en;
+ bool m_nr_06_hotkey_5060_en;
+ bool m_nr_06_internal_speaker_beep;
+ bool m_nr_06_hotkey_cpu_speed_en;
+ u8 m_nr_07_cpu_speed; // u2
+ bool m_nr_08_keyboard_issue2;
+ bool m_nr_08_psg_turbosound_en;
+ bool m_nr_08_port_ff_rd_en;
+ bool m_nr_08_dac_en;
+ bool m_nr_08_internal_speaker_en;
+ bool m_nr_08_psg_stereo_mode;
+ bool m_nr_08_contention_disable;
+ bool m_nr_09_hdmi_audio_en;
+ bool m_nr_09_sprite_tie;
+ u8 m_nr_09_psg_mono; // u3
+ u8 m_nr_0a_mouse_dpi; // u2
+ bool m_nr_0a_mouse_button_reverse;
+ bool m_nr_0a_divmmc_automap_en;
+ u8 m_nr_0a_mf_type; // u2
+ bool m_nr_0b_joy_iomode_0;
+ u8 m_nr_0b_joy_iomode; // u2
+ bool m_nr_0b_joy_iomode_en;
+ u8 m_nr_10_coreid; // u5
+ u8 m_nr_11_video_timing; // u3
+ bool m_nr_10_flashboot;
+ u8 m_nr_12_layer2_active_bank; // u7
+ u8 m_nr_13_layer2_shadow_bank; // u7
+ u8 m_nr_14_global_transparent_rgb;
+ bool m_nr_15_sprite_en;
+ bool m_nr_15_sprite_over_border_en;
+ u8 m_nr_15_layer_priority; // u3
+ bool m_nr_15_sprite_border_clip_en;
+ bool m_nr_15_sprite_priority;
+ bool m_nr_15_lores_en;
+ u8 m_nr_16_layer2_scrollx;
+ u8 m_nr_17_layer2_scrolly;
+ u8 m_nr_18_layer2_clip_x1;
+ u8 m_nr_18_layer2_clip_x2;
+ u8 m_nr_18_layer2_clip_y1;
+ u8 m_nr_18_layer2_clip_y2;
+ u8 m_nr_18_layer2_clip_idx; // u2
+ u8 m_nr_19_sprite_clip_x1;
+ u8 m_nr_19_sprite_clip_x2;
+ u8 m_nr_19_sprite_clip_y1;
+ u8 m_nr_19_sprite_clip_y2;
+ u8 m_nr_19_sprite_clip_idx; // u2
+ u8 m_nr_1a_ula_clip_x1;
+ u8 m_nr_1a_ula_clip_x2;
+ u8 m_nr_1a_ula_clip_y1;
+ u8 m_nr_1a_ula_clip_y2;
+ u8 m_nr_1a_ula_clip_idx; // u2
+ u8 m_nr_1b_tm_clip_x1;
+ u8 m_nr_1b_tm_clip_x2;
+ u8 m_nr_1b_tm_clip_y1;
+ u8 m_nr_1b_tm_clip_y2;
+ u8 m_nr_1b_tm_clip_idx; // u2
+ bool m_nr_22_line_interrupt_en;
+ u16 m_nr_23_line_interrupt; // u9
+ u8 m_nr_26_ula_scrollx;
+ u8 m_nr_27_ula_scrolly;
+ u8 m_nr_2d_i2s_sample; // u2
+ u16 m_nr_30_tm_scrollx; // u10
+ u8 m_nr_31_tm_scrolly;
+ u8 m_nr_32_lores_scrollx;
+ u8 m_nr_33_lores_scrolly;
+ u8 m_nr_palette_idx;
+ bool m_nr_palette_sub_idx;
+ u8 m_nr_42_ulanext_format;
+ bool m_nr_43_palette_autoinc_disable;
+ u8 m_nr_43_palette_write_select; // u3
+ bool m_nr_43_active_sprite_palette;
+ bool m_nr_43_active_layer2_palette;
+ bool m_nr_43_active_ula_palette;
+ bool m_nr_43_ulanext_en;
+ u8 m_nr_stored_palette_value;
+ u8 m_nr_4a_fallback_rgb;
+ u8 m_nr_4b_sprite_transparent_index;
+ u8 m_nr_4c_tm_transparent_index; // u4
+ u16 m_nr_copper_addr; // u11
+ u8 m_nr_copper_data_stored;
+ u8 m_nr_62_copper_mode; // u2
+ u8 m_nr_64_copper_offset;
+ bool m_nr_68_ula_en;
+ u8 m_nr_68_blend_mode; // u2
+ bool m_nr_68_cancel_extended_keys;
+ bool m_nr_68_ula_fine_scroll_x;
+ bool m_nr_68_ula_stencil_mode;
+ bool m_nr_6a_lores_radastan;
+ bool m_nr_6a_lores_radastan_xor;
+ u8 m_nr_6a_lores_palette_offset; // u4
+ bool m_nr_6b_tm_en;
+ u8 m_nr_6b_tm_control; // u7
+ u8 m_nr_6c_tm_default_attr;
+ u8 m_nr_6e_tilemap_base; // u6
+ bool m_nr_6e_tilemap_base_7;
+ u8 m_nr_6f_tilemap_tiles; // u6
+ bool m_nr_6f_tilemap_tiles_7;
+ u8 m_nr_70_layer2_resolution; // u2
+ u8 m_nr_70_layer2_palette_offset; // u4
+ bool m_nr_71_layer2_scrollx_msb;
+ u8 m_nr_7f_user_register_0;
+ u8 m_nr_80_expbus;
+ u8 m_nr_81_expbus_speed; // u2
+ bool m_nr_81_expbus_clken;
+ bool m_nr_81_expbus_nmi_debounce_disable;
+ bool m_nr_81_expbus_ula_override;
+ u8 m_nr_82_internal_port_enable;
+ u8 m_nr_83_internal_port_enable;
+ u8 m_nr_84_internal_port_enable;
+ u8 m_nr_85_internal_port_enable; // u4
+ bool m_nr_85_internal_port_reset_type;
+ u8 m_nr_86_bus_port_enable;
+ u8 m_nr_87_bus_port_enable;
+ u8 m_nr_88_bus_port_enable;
+ u8 m_nr_89_bus_port_enable; // u4
+ bool m_nr_89_bus_port_reset_type;
+ u8 m_nr_8a_bus_port_propagate; // u6
+ u8 m_nr_8c_altrom;
+ u8 m_nr_8f_mapping_mode; // u2
+ u8 m_nr_90_pi_gpio_o_en;
+ u8 m_nr_91_pi_gpio_o_en;
+ u8 m_nr_92_pi_gpio_o_en;
+ u8 m_nr_93_pi_gpio_o_en; // u4
+ u8 m_nr_98_pi_gpio_o;
+ u8 m_nr_99_pi_gpio_o;
+ u8 m_nr_9a_pi_gpio_o;
+ u8 m_nr_9b_pi_gpio_o; // u4
+ u8 m_nr_a0_pi_peripheral_en;
+ u8 m_nr_a2_pi_i2s_ctl;
+ bool m_nr_a8_esp_gpio0_en;
+ bool m_nr_a9_esp_gpio0;
+ u8 m_nr_b8_divmmc_ep_0;
+ u8 m_nr_b9_divmmc_ep_valid_0;
+ u8 m_nr_ba_divmmc_ep_timing_0;
+ u8 m_nr_bb_divmmc_ep_1;
+ u8 m_nr_c0_im2_vector; // u3
+ bool m_nr_c0_stackless_nmi;
+ bool m_nr_c0_int_mode_pulse_0_im2_1;
+ u8 m_nr_c2_retn_address_lsb;
+ u8 m_nr_c3_retn_address_msb;
+ bool m_nr_c4_int_en_0_expbus;
+ u8 m_nr_c6_int_en_2_654; // u3
+ u8 m_nr_c6_int_en_2_210; // u3
+ bool m_nr_cc_dma_int_en_0_7;
+ u8 m_nr_cc_dma_int_en_0_10; // u2
+ u8 m_nr_cd_dma_int_en_1;
+ u8 m_nr_ce_dma_int_en_2_654; // u3
+ u8 m_nr_ce_dma_int_en_2_210; // u3
+ bool m_nr_d8_io_trap_fdc_en;
+ u8 m_nr_d9_iotrap_write;
+ u8 m_nr_da_iotrap_cause; // u2
+ bool m_nr_f0_select;
+ bool m_nr_f0_xdna_en;
+ bool m_nr_f0_xadc_en;
+ u8 m_nr_f0_xdev_cmd;
+ bool m_nr_f0_xadc_eoc;
+ bool m_nr_f0_xadc_eos;
+ bool m_nr_f8_xadc_dwe;
+ u8 m_nr_f8_xadc_daddr; // u7
+ bool m_nr_f8_xadc_den;
+ u8 m_nr_f9_xadc_d0;
+ u8 m_nr_fa_xadc_d1;
+
+ bool m_pulse_int_n;
+ u8 m_nr_09_scanlines; // u2
+
+ u8 m_eff_nr_03_machine_timing; // u3
+ bool m_eff_nr_05_5060;
+ bool m_eff_nr_05_scandouble_en;
+ bool m_eff_nr_08_contention_disable;
+ u8 m_eff_nr_09_scanlines; // u2
+
+ bool m_port_123b_layer2_en;
+ bool m_port_123b_layer2_map_wr_en;
+ bool m_port_123b_layer2_map_rd_en;
+ bool m_port_123b_layer2_map_shadow;
+ u8 m_port_123b_layer2_map_segment; // u2
+ u8 m_port_123b_layer2_offset; // u3
+
+ u8 m_port_bf3b_ulap_mode; // u2
+ u8 m_port_bf3b_ulap_index; // u6
+ bool m_port_ff3b_ulap_en;
+
+ u8 m_ay_select; // u2
+
+ emu_timer *m_irq_line_timer;
+ emu_timer *m_spi_clock;
+ int m_spi_clock_cycles;
+ bool m_spi_clock_state;
+ u8 m_spi_mosi_dat;
+ u8 m_spi_miso_dat;
+ bool m_i2c_scl_data;
+
+ u16 m_irq_mask;
+};
+
+void specnext_state::bank_update(u8 bank, u8 count)
+{
+ for (auto b = bank; count; ++b, --count)
+ bank_update(b);
+}
+
+void specnext_state::bank_update(u8 bank)
+{
+ using views_link = std::reference_wrapper<memory_view>;
+ views_link views[] = { m_view0, m_view1, m_view2, m_view3, m_view4, m_view5, m_view6, m_view7 };
+
+ const bool is_rom = (bank >> 1) == 0;
+ if (m_bootrom_en && is_rom)
+ return views[bank].get().select(1);
+
+ if (machine_type_48())
+ {
+ m_sram_rom = 0b00;
+ m_sram_rom3 = 1;
+ m_sram_alt_128_n = !(!nr_8c_altrom_lock_rom1() && nr_8c_altrom_lock_rom0());
+ }
+ else if (machine_type_p3())
+ {
+ if (nr_8c_altrom_lock_rom1() || nr_8c_altrom_lock_rom0())
+ {
+ m_sram_rom = (nr_8c_altrom_lock_rom1() << 1) | nr_8c_altrom_lock_rom0();
+ m_sram_rom3 = nr_8c_altrom_lock_rom1() && nr_8c_altrom_lock_rom0();
+ m_sram_alt_128_n = nr_8c_altrom_lock_rom1();
+ }
+ else
+ {
+ m_sram_rom = port_1ffd_rom();
+ m_sram_rom3 = port_1ffd_rom() == 0x03;
+ m_sram_alt_128_n = port_1ffd_rom() & 1;
+ }
+ }
+ else
+ {
+ if (nr_8c_altrom_lock_rom1() || nr_8c_altrom_lock_rom0())
+ {
+ m_sram_rom = nr_8c_altrom_lock_rom1();
+ m_sram_rom3 = nr_8c_altrom_lock_rom1();
+ m_sram_alt_128_n = nr_8c_altrom_lock_rom1();
+ }
+ else
+ {
+ m_sram_rom = port_1ffd_rom() & 1;
+ m_sram_rom3 = port_1ffd_rom() & 1;
+ m_sram_alt_128_n = port_1ffd_rom() & 1;
+ }
+ }
+
+ const u8 mem_active_page = m_mmu[bank];
+ const bool mem_active_bank5 = mem_active_page == 0x0a || mem_active_page == 0x0b;
+ const bool mem_active_bank7 = mem_active_page == 0x0e;
+ const u16 mmu_A21_A13 = ((0b0001 + BIT(mem_active_page, 5, 3)) << 5) | BIT(mem_active_page, 0, 5);
+
+ const u8 layer2_active_bank_offset_pre = m_port_123b_layer2_map_segment == 0b11 ? BIT(bank, 1, 2) : m_port_123b_layer2_map_segment;
+ const u8 layer2_active_bank_offset = layer2_active_bank_offset_pre + m_port_123b_layer2_offset;
+ const u8 layer2_active_bank = m_port_123b_layer2_map_shadow ? m_nr_13_layer2_shadow_bank : m_nr_12_layer2_active_bank;
+ const u8 layer2_active_page = ((layer2_active_bank + layer2_active_bank_offset) << 1) | BIT(bank, 0);
+ const u16 layer2_A21_A13 = ((0b0001 + BIT(layer2_active_page, 5, 3)) << 5) | BIT(layer2_active_page, 0, 5);
+
+ // sram_pre_romcs_replace = expbus_romcs_replace;
+ const bool sram_pre_alt_en = nr_8c_altrom_en();
+ const bool sram_pre_alt_128_n = m_sram_alt_128_n;
+ const bool sram_pre_rom3 = m_sram_rom3;
+ const bool sram_pre_layer2_rd_en = m_port_123b_layer2_map_rd_en;
+ const bool sram_pre_layer2_wr_en = m_port_123b_layer2_map_wr_en;
+ const u16 sram_pre_layer2_A21_A13 = layer2_A21_A13;
+
+ u8 sram_pre_A20_A13, sram_pre_override; // u3: divmmc & layer 2 & romcs
+ bool sram_pre_active, sram_pre_bank5, sram_pre_bank7, sram_pre_rdonly;
+ if (is_rom) // 0-1
+ {
+ const bool cpu_a13 = bank & 1;
+ m_mf->enable_w(port_multiface_io_en());
+ if (m_mf->mf_enabled_r())
+ {
+ sram_pre_A20_A13 = 0b00001010 | cpu_a13;
+ sram_pre_active = 1;
+ sram_pre_bank5 = sram_pre_bank7 = 0;
+ sram_pre_rdonly = !cpu_a13;
+ sram_pre_override = 0b000;
+ }
+ else if (BIT(~mmu_A21_A13, 8))
+ {
+ sram_pre_A20_A13 = mmu_A21_A13 & 0xff;
+ sram_pre_active = !mem_active_bank5 && !mem_active_bank7;
+ sram_pre_bank5 = mem_active_bank5;
+ sram_pre_bank7 = mem_active_bank7;
+ sram_pre_rdonly = 0;
+ sram_pre_override = 0b110;
+ }
+ else if (m_nr_03_config_mode)
+ {
+ sram_pre_A20_A13 = (m_nr_04_romram_bank << 1) | cpu_a13;
+ sram_pre_active = 1;
+ sram_pre_bank5 = sram_pre_bank7 = 0;
+ sram_pre_rdonly = 0;
+ sram_pre_override = 0b110;
+ }
+ else
+ {
+ sram_pre_A20_A13 = (m_sram_rom << 1) | cpu_a13;
+ sram_pre_active = 1;
+ sram_pre_bank5 = sram_pre_bank7 = 0;
+ sram_pre_rdonly = !(nr_8c_altrom_en() && nr_8c_altrom_rw());
+ sram_pre_override = 0b111;
+ }
+ }
+ else
+ {
+ sram_pre_A20_A13 = mmu_A21_A13 & 0xff;
+ sram_pre_active = BIT(~mmu_A21_A13, 8) && !mem_active_bank5 && !mem_active_bank7;
+ sram_pre_bank5 = mem_active_bank5;
+ sram_pre_bank7 = mem_active_bank7;
+ sram_pre_rdonly = 0;
+ sram_pre_override = (((bank & 0x06) != 0x06) && (m_port_123b_layer2_map_segment == 0x03)) << 1;
+ }
+
+ const bool sram_pre_romcs_n = 0; // expbus_eff_en && !expbus_eff_disable_mem;
+ const bool sram_romcs = BIT(sram_pre_override, 0) && sram_pre_romcs_n;
+ const bool sram_divmmc_automap_en = BIT(sram_pre_override, 2);
+
+ m_divmmc->cpu_a_15_13_w(bank);
+ m_divmmc->en_w(port_divmmc_io_en());
+ m_divmmc->automap_reset_w(!port_divmmc_io_en() || !m_nr_0a_divmmc_automap_en);
+ m_divmmc->automap_active_w(sram_divmmc_automap_en);
+ m_divmmc->retn_seen_w(0);
+ m_divmmc->divmmc_button_w(m_nr_02_generate_divmmc_nmi);
+
+ for (s8 cpu_rd_n = 1; cpu_rd_n >= 0; --cpu_rd_n) // check W then R
+ {
+ const bool sram_layer2_map_en = BIT(sram_pre_override, 1) && ((sram_pre_layer2_wr_en && cpu_rd_n) || (sram_pre_layer2_rd_en && !cpu_rd_n));
+ const bool sram_altrom_en = !(BIT(~sram_pre_override, 0) || !sram_pre_alt_en || (sram_pre_rdonly && cpu_rd_n) || (!sram_pre_rdonly && !cpu_rd_n));
+ const bool sram_divmmc_automap_rom3_en = ((sram_pre_override & 0x05) == 0x05) && !sram_layer2_map_en && !sram_romcs && ((sram_altrom_en && sram_pre_alt_128_n) || (sram_pre_rom3 && !sram_altrom_en));
+
+ m_divmmc->automap_rom3_active_w(sram_divmmc_automap_rom3_en);
+ m_divmmc->clock_w();
+
+ u8 sram_A20_A13;
+ bool sram_active, sram_bank5, sram_bank7, sram_rdonly, sram_romcs_en, sram_mem_hide_n;
+ if (BIT(sram_pre_override, 2) && m_divmmc->divmmc_rom_en_r())
+ {
+ sram_A20_A13 = 0b00001000;
+ sram_active = 1;
+ sram_bank5 = sram_bank7 = 0;
+ sram_rdonly = 1;
+ sram_romcs_en = 0;
+ sram_mem_hide_n = 0;
+ }
+ else if (BIT(sram_pre_override, 2) && m_divmmc->divmmc_ram_en_r())
+ {
+ sram_A20_A13 = 0b00010000 | m_divmmc->divmmc_ram_bank_r();
+ sram_active = 1;
+ sram_bank5 = sram_bank7 = 0;
+ sram_rdonly = m_divmmc->divmmc_rdonly_r();
+ sram_romcs_en = 0;
+ sram_mem_hide_n = 0;
+ }
+ else if (sram_layer2_map_en)
+ {
+ sram_A20_A13 = sram_pre_layer2_A21_A13 & 0xff;
+ sram_active = BIT(~sram_pre_layer2_A21_A13, 8);
+ sram_bank5 = sram_bank7 = 0;
+ sram_rdonly = 0;
+ sram_romcs_en = 0;
+ sram_mem_hide_n = 0;
+ }
+ else if (sram_romcs)
+ {
+ sram_A20_A13 = 0b00011110 | BIT(sram_pre_A20_A13, 0);
+ sram_active = 1;
+ sram_bank5 = sram_bank7 = 0;
+ sram_rdonly = 1;
+ sram_romcs_en = 1;
+ sram_mem_hide_n = 1;
+ }
+ else if (sram_altrom_en)
+ {
+ sram_A20_A13 = 0b00001100 | (sram_pre_alt_128_n << 1) | BIT(sram_pre_A20_A13, 0);
+ sram_active = 1;
+ sram_bank5 = sram_bank7 = 0;
+ sram_rdonly = sram_pre_rdonly;
+ sram_romcs_en = 0;
+ sram_mem_hide_n = 1;
+ }
+ else
+ {
+ sram_A20_A13 = sram_pre_A20_A13;
+ sram_active = sram_pre_active;
+ sram_bank5 = sram_pre_bank5;
+ sram_bank7 = sram_pre_bank7;
+ sram_rdonly = sram_pre_rdonly;
+ sram_romcs_en = 0;
+ sram_mem_hide_n = BIT(sram_pre_override, 0);
+ }
+
+ if (false) // unused in current implementation, makes compiler happy
+ printf("%d", sram_active + sram_bank5 + sram_romcs_en + sram_mem_hide_n);
+
+ if (cpu_rd_n)
+ {
+ m_page_shadow[bank] = sram_rdonly ? ~0 : sram_A20_A13;
+ }
+ else
+ {
+ m_bank_ram[bank]->set_entry(sram_A20_A13);
+ if (sram_rdonly || (m_page_shadow[bank] != sram_A20_A13))
+ {
+ views[bank].get().select(0);
+ LOGMEM("ROM%d = %x\n", bank, sram_A20_A13);
+ }
+ else
+ {
+ if (m_page_shadow[bank] == sram_A20_A13)
+ m_page_shadow[bank] = ~0;
+ views[bank].get().disable();
+ LOGMEM("RAM%d = %x\n", bank, sram_A20_A13);
+ }
+ }
+ }
+}
+
+void specnext_state::memory_change(u16 port, u8 data) // port_memory_change_dly
+{
+ if (port_1ffd_special())
+ {
+ u8 b3 = (BIT(m_port_1ffd_data, 2) || BIT(m_port_1ffd_data, 1)) << 3;
+ mmu_x2_w(0, b3 | 0b000);
+ const u8 b2 = (BIT(m_port_1ffd_data, 2) && BIT(m_port_1ffd_data, 1)) << 2;
+ mmu_x2_w(2, b3 | b2 | 0b10);
+ mmu_x2_w(4, b3 | 0b100);
+ b3 = (BIT(~m_port_1ffd_data, 2) && BIT(m_port_1ffd_data, 1)) << 3;
+ mmu_x2_w(6, b3 | 0b110);
+ }
+ else
+ {
+ const bool mode_profi = nr_8f_mapping_mode_profi();
+
+ if (BIT(m_port_eff7_data, 3) || (mode_profi && BIT(m_port_dffd_data, 4)))
+ mmu_x2_w(0, 0x00);
+ else
+ mmu_x2_w(0, 0xff);
+
+ if (mode_profi && BIT(m_port_dffd_data, 3))
+ mmu_x2_w(2, (port_7ffd_bank() << 1) | 0);
+ else if (mode_profi || m_port_1ffd_special_old)
+ mmu_x2_w(2, 0x0a);
+
+ if (mode_profi && BIT(m_port_dffd_data, 6))
+ mmu_x2_w(4, 0x0c);
+ else if (mode_profi || m_port_1ffd_special_old)
+ mmu_x2_w(4, 0x04);
+
+ const bool port_memory_ram_change_dly = !(port == 0x8e && !BIT(data, 3));
+ if (mode_profi && BIT(m_port_dffd_data, 3))
+ mmu_x2_w(6, 0x0e);
+ else if (m_port_1ffd_special_old || port_memory_ram_change_dly)
+ mmu_x2_w(6, (port_7ffd_bank() << 1) | 0);
+ }
+
+ m_port_1ffd_special_old = port_1ffd_special();
+}
+
+u32 specnext_state::screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect)
+{
+ rectangle clip256x192 = SCR_256x192;
+ clip256x192 &= cliprect;
+ rectangle clip320x256 = SCR_320x256;
+ clip320x256 &= cliprect;
+
+ screen.priority().fill(0, cliprect);
+ const bool flash = u64(screen.frame_number() / m_frame_invert_count) & 1;
+ if (m_nr_15_layer_priority < 0b110)
+ {
+ // background
+ if (m_nr_68_ula_en)
+ {
+ m_ula->draw_border(bitmap, cliprect, m_port_fe_data & 0x07);
+ }
+ else {
+ bitmap.fill(m_palette->pen_color(UTM_FALLBACK_PEN), cliprect);
+ }
+
+ static const u8 lcfg[][3] =
+ {
+ // tiles+ula priority; l2 prioryty; l2 mask
+ // + l2 pushes priority colors to 8 (foreground)
+ { 1, 1, 0 }, // SLU
+ { 1, 8, 0 }, // LSU
+ { 1, 1, 1 }, // SUL
+ { 8, 8, 0 }, // LUS
+ { 8, 1, 8 }, // USL
+ { 8, 8, 8 } // ULS
+ };
+
+ const u8 (&l)[3] = lcfg[m_nr_15_layer_priority];
+ if (m_nr_6b_tm_en) m_tiles->draw(screen, bitmap, clip320x256, TILEMAP_DRAW_CATEGORY(1), l[0]);
+ if (m_nr_68_ula_en && BIT(~m_nr_6b_tm_control, 3))
+ {
+ if (m_nr_15_lores_en) m_lores->draw(screen, bitmap, clip256x192, l[0]);
+ else m_ula->draw(screen, bitmap, clip256x192, flash, l[0]);
+ }
+ if (m_nr_6b_tm_en) m_tiles->draw(screen, bitmap, clip320x256, TILEMAP_DRAW_CATEGORY(2), l[0]);
+ m_layer2->draw(screen, bitmap, clip320x256, l[1], l[2]);
+ }
+ else // colors mixing case
+ {
+ bitmap.fill(m_palette->pen_color(UTM_FALLBACK_PEN), cliprect);
+
+ if (m_nr_68_blend_mode != 0b11)
+ //if (m_nr_68_blend_mode == 0b00)
+ {
+ if (m_nr_68_ula_en && BIT(~m_nr_6b_tm_control, 3))
+ {
+ if (m_nr_15_lores_en) m_lores->draw(screen, bitmap, clip256x192, 1);
+ else m_ula->draw(screen, bitmap, clip256x192, flash, 1);
+ }
+ if (m_nr_6b_tm_en) m_tiles->draw(screen, bitmap, clip320x256, TILEMAP_DRAW_CATEGORY(1), 2);
+ if (m_nr_6b_tm_en) m_tiles->draw(screen, bitmap, clip320x256, TILEMAP_DRAW_CATEGORY(2), 8);
+ }
+ /* TODO No tests for modes below yet
+ else if (m_nr_68_blend_mode == 0b10)
+ {
+ }
+ else if (m_nr_68_blend_mode == 0b11)
+ {
+ }
+ */
+ else
+ {
+ if (m_nr_68_ula_en && BIT(~m_nr_6b_tm_control, 3))
+ {
+ if (m_nr_15_lores_en) m_lores->draw(screen, bitmap, clip256x192, 1);
+ else m_ula->draw(screen, bitmap, clip256x192, flash, 1);
+ }
+ if (m_nr_6b_tm_en) m_tiles->draw(screen, bitmap, clip320x256, TILEMAP_DRAW_CATEGORY(1), 2);
+ if (m_nr_6b_tm_en) m_tiles->draw(screen, bitmap, clip320x256, TILEMAP_DRAW_CATEGORY(2), 2);
+ }
+ // mixes only to 1
+ m_layer2->draw_mix(screen, bitmap, clip320x256, m_nr_15_layer_priority & 1);
+ }
+ // sprites below foreground
+ if (m_nr_15_sprite_en) m_sprites->draw(screen, bitmap, clip320x256, GFX_PMASK_8);
+
+ return 0;
+}
+
+u32 specnext_state::internal_port_enable() const
+{
+ u32 ports_en = (m_nr_85_internal_port_enable << 24) | (m_nr_84_internal_port_enable << 16) | (m_nr_83_internal_port_enable << 8) | (m_nr_82_internal_port_enable << 0);
+ if (m_nr_80_expbus & 0x80)
+ {
+ ports_en &= (m_nr_89_bus_port_enable << 24) | (m_nr_88_bus_port_enable << 16) | (m_nr_87_bus_port_enable << 8) | (m_nr_86_bus_port_enable << 0);
+ }
+ return ports_en;
+}
+
+u16 specnext_state::nr_palette_dat()
+{
+ u16 nr_palette_index = (BIT(m_nr_43_palette_write_select, 1) << 9) | (BIT(m_nr_43_palette_write_select, 2) << 8) | m_nr_palette_idx;
+ if ((BIT(m_nr_43_palette_write_select, 1) != BIT(m_nr_43_palette_write_select, 0)))
+ nr_palette_index |= 0x400;
+
+ rgb_t rgb = m_palette->pen_color(nr_palette_index);
+ return ((rgb.r() >> 5) << 6) | ((rgb.g() >> 5) << 3) | ((rgb.b() >> 5) << 0);
+}
+
+void specnext_state::palette_val_w(u8 nr_palette_priority, u16 nr_palette_value)
+{
+ m_screen->update_now();
+ u16 nr_palette_index = (BIT(m_nr_43_palette_write_select, 1) << 9) | (BIT(m_nr_43_palette_write_select, 2) << 8) | m_nr_palette_idx;
+ if ((BIT(m_nr_43_palette_write_select, 1) xor BIT(m_nr_43_palette_write_select, 0)))
+ nr_palette_index |= 0x400;
+
+ m_palette->set_pen_color(nr_palette_index, rgbexpand<3,3,3>(nr_palette_value, 6, 3, 0));
+ m_layer2->pen_priority_w(nr_palette_index, BIT(nr_palette_priority, 1));
+}
+
+u8 specnext_state::port_ff_r()
+{
+ return (m_nr_08_port_ff_rd_en && port_ff_io_en())
+ ? m_port_ff_data // ==port_ff_dat_tmx
+ : floating_bus_r();
+}
+
+void specnext_state::port_ff_w(u8 data)
+{
+ m_port_ff_data = data; // ==port_ff_dat_tmx
+ m_ula->port_ff_reg_w(m_port_ff_data);
+ nr_6a_lores_radastan_xor_w(m_nr_6a_lores_radastan_xor);
+
+ // TODO confirm this
+ u16 nr_palette_value = (data << 1) | BIT(data, 1) | BIT(data, 0);
+ u16 nr_palette_index_utm = (BIT(m_nr_43_palette_write_select, 2) << 8) | (0b11 << 6) | m_port_bf3b_ulap_index;
+
+ m_palette->set_pen_color(nr_palette_index_utm, rgbexpand<3,3,3>(nr_palette_value, 6, 3, 0));
+}
+
+void specnext_state::port_7ffd_reg_w(u8 data)
+{
+ m_port_7ffd_data = data;
+ m_ula->ula_shadow_en_w(port_7ffd_shadow());
+}
+
+void specnext_state::port_e3_reg_w(u8 data)
+{
+ m_port_e3_reg = data & ~0x30;
+ m_divmmc->divmmc_reg_w(m_port_e3_reg & ~0x30);
+ bank_update(0, 2);
+}
+
+void specnext_state::port_e7_reg_w(u8 data)
+{
+ if ((data & 3) == 0b10)
+ m_port_e7_reg = 0xfe;
+ else if ((data & 3) == 0x01)
+ m_port_e7_reg = 0xfd;
+ else if ((data == 0xfb) || (data == 0xf7))
+ m_port_e7_reg = data;
+ else if ((data == 0x7f) && (m_nr_03_config_mode || BIT(m_nr_02_reset_type, 2)))
+ m_port_e7_reg = 0x7f;
+ else
+ m_port_e7_reg = 0xff;
+
+ // bit 7 = fpga flash, bit 3 = rpi1, bit 2 = rpi0, bit 1 = sd1, bit 0 = sd0
+ m_sdcard->spi_ss_w(BIT(~m_port_e7_reg, 0));
+}
+
+u8 specnext_state::spi_data_r()
+{
+ u8 din = m_spi_miso_dat;
+ if (!machine().side_effects_disabled())
+ spi_data_w(0xff);
+
+ return din;
+}
+
+void specnext_state::spi_data_w(u8 data)
+{
+ m_spi_mosi_dat = data;
+#if TIMINGS_PERFECT
+ m_spi_clock_cycles = 8;
+ m_spi_clock->adjust(m_maincpu->clocks_to_attotime(1) / 4, 0, m_maincpu->clocks_to_attotime(1) / 4);
+#else
+ for (u8 m = 0x80; m; m >>= 1)
+ {
+ m_sdcard->spi_mosi_w(m_spi_mosi_dat & m ? 1 : 0);
+ m_sdcard->spi_clock_w(CLEAR_LINE);
+ m_sdcard->spi_clock_w(ASSERT_LINE);
+ }
+#endif
+}
+
+void specnext_state::spi_miso_w(u8 data)
+{
+ m_spi_miso_dat <<= 1;
+ m_spi_miso_dat |= data;
+}
+
+void specnext_state::i2c_scl_w(u8 data)
+{
+ if (port_i2c_io_en())
+ {
+ m_i2c_scl_data = data & 1;
+ m_i2cmem->write_scl(m_i2c_scl_data);
+ }
+}
+
+void specnext_state::turbosound_address_w(u8 data)
+{
+ if ((data & 0x9c) == 0x9c)
+ m_ay_select = (data & 3) % 3;
+ else
+ m_ay[m_nr_08_psg_turbosound_en ? m_ay_select : 0]->address_w(data);
+}
+
+u8 specnext_state::mf_port_r(offs_t addr)
+{
+ if (!machine().side_effects_disabled())
+ {
+ const u8 port = addr & 0xff;
+ u8 port_mf_enable_io_a = 0x3f;
+ u8 port_mf_disable_io_a = 0xbf;
+ if (m_nr_0a_mf_type & 2)
+ {
+ port_mf_enable_io_a = 0x9f;
+ port_mf_disable_io_a = 0x1f;
+ }
+ else if (m_nr_0a_mf_type & 1)
+ {
+ port_mf_enable_io_a = 0xbf;
+ port_mf_disable_io_a = 0x3f;
+ }
+
+ m_mf->port_mf_enable_rd_w(port_multiface_io_en() && (port == port_mf_enable_io_a));
+ m_mf->port_mf_disable_rd_w(port_multiface_io_en() && (port == port_mf_disable_io_a));
+ m_mf->port_mf_enable_wr_w(0);
+ m_mf->port_mf_disable_wr_w(0);
+ m_mf->clock_w();
+
+ bank_update(0, 2);
+ }
+
+ u8 data;
+ if (!m_mf->mf_port_en_r())
+ data = 0x00;
+ else if (m_nr_0a_mf_type != 0b00)
+ data = (BIT(m_port_7ffd_data, 3) << 7) | 0x7f;
+ else
+ {
+ switch (BIT(addr, 12, 4))
+ {
+ case 0b0001: data = m_port_1ffd_data; break;
+ case 0b0111: data = m_port_7ffd_data; break;
+ case 0b1101: data = m_port_dffd_data; break;
+ case 0b1110: data = m_port_eff7_data & 0xc0; break;
+ default: data = m_port_fe_data & 0x07;
+ }
+ }
+
+ if (!machine().side_effects_disabled())
+ {
+ m_mf->port_mf_enable_rd_w(0);
+ m_mf->port_mf_disable_rd_w(0);
+ }
+ return data;
+}
+
+void specnext_state::mf_port_w(offs_t addr, u8 data)
+{
+ const u8 port = addr & 0xff;
+ u8 port_mf_enable_io_a = 0x3f;
+ u8 port_mf_disable_io_a = 0xbf;
+ if (m_nr_0a_mf_type & 2)
+ {
+ port_mf_enable_io_a = 0x9f;
+ port_mf_disable_io_a = 0x1f;
+ }
+ else if (m_nr_0a_mf_type & 1)
+ {
+ port_mf_enable_io_a = 0xbf;
+ port_mf_disable_io_a = 0x3f;
+ }
+
+ m_mf->port_mf_enable_rd_w(0);
+ m_mf->port_mf_disable_rd_w(0);
+ m_mf->port_mf_enable_wr_w(port_multiface_io_en() && (port == port_mf_enable_io_a));
+ m_mf->port_mf_disable_wr_w(port_multiface_io_en() && (port == port_mf_disable_io_a));
+ m_mf->clock_w();
+ bank_update(0, 2);
+ m_mf->port_mf_enable_rd_w(0);
+ m_mf->port_mf_disable_rd_w(0);
+}
+
+attotime specnext_state::cooper_until_pos_r(u16 pos)
+{
+ const u16 vcount = BIT(pos, 0, 9);
+ const u16 hcount = ((BIT(pos, 9, 6) << 3) + (BIT(pos, 15) ? 12 : 0)) << 1;
+ return ((vcount < m_screen->height()) && (hcount < m_screen->width()))
+ ? m_screen->time_until_pos(SCR_256x192.top() + vcount + m_nr_64_copper_offset, SCR_256x192.left() + hcount)
+ : attotime::never;
+}
+
+
+TIMER_CALLBACK_MEMBER(specnext_state::spi_clock)
+{
+ if (m_spi_clock_cycles > 0)
+ {
+ if (m_spi_clock_state)
+ {
+ m_sdcard->spi_clock_w(ASSERT_LINE);
+ m_spi_clock_cycles--;
+ }
+ else
+ {
+ m_sdcard->spi_mosi_w(BIT(m_spi_mosi_dat, m_spi_clock_cycles - 1));
+ m_sdcard->spi_clock_w(CLEAR_LINE);
+ }
+
+ m_spi_clock_state = !m_spi_clock_state;
+ }
+ else
+ {
+ m_spi_clock_state = false;
+ m_spi_clock->reset();
+ }
+}
+
+void specnext_state::mmu_w(offs_t bank, u8 data)
+{
+ m_mmu[bank] = data;
+ bank_update(bank);
+}
+
+void specnext_state::mmu_x2_w(offs_t bank, u8 data)
+{
+ mmu_w(bank, data);
+ mmu_w(bank | 1, data == 0xff ? 0xff : (data | 1));
+}
+
+u8 specnext_state::dma_r(bool dma_mode)
+{
+ m_dma->dma_mode_w(dma_mode);
+ return m_dma->read();
+}
+
+void specnext_state::dma_w(bool dma_mode, u8 data)
+{
+ m_dma->dma_mode_w(dma_mode);
+ m_dma->write(data);
+}
+
+u8 specnext_state::reg_r(offs_t nr_register)
+{
+ u8 port_253b_dat;
+
+ switch (nr_register)
+ {
+ case 0x00:
+ port_253b_dat = g_machine_id();
+ break;
+ case 0x01:
+ port_253b_dat = G_VERSION;
+ break;
+ case 0x02:
+ port_253b_dat = (m_nr_02_bus_reset << 7) | (0b00 << 5) | (nr_02_iotrap() << 4) | (m_nr_02_generate_mf_nmi << 3) | (m_nr_02_generate_divmmc_nmi << 2) | BIT(m_nr_02_reset_type, 0, 2);
+ break;
+ case 0x03:
+ port_253b_dat = (m_nr_palette_sub_idx << 7) | (0b00 << 5) | (m_nr_03_machine_timing << 4) | (m_nr_03_user_dt_lock << 3) | m_nr_03_machine_type;
+ break;
+ case 0x05:
+ port_253b_dat = (BIT(m_nr_05_joy0, 0, 2) << 6) | (BIT(m_nr_05_joy1, 0, 2) << 4) | (BIT(m_nr_05_joy0, 2) << 3) | (m_eff_nr_05_5060 << 2) | (BIT(m_nr_05_joy1, 2) << 1) | m_eff_nr_05_scandouble_en;
+ break;
+ case 0x06:
+ port_253b_dat = (m_nr_06_hotkey_cpu_speed_en << 7) | (m_nr_06_internal_speaker_beep << 6) | (m_nr_06_hotkey_5060_en << 5) | (m_nr_06_button_drive_nmi_en << 4) | (m_nr_06_button_m1_nmi_en << 3) | (m_nr_06_ps2_mode << 2) | m_nr_06_psg_mode;
+ break;
+ case 0x07:
+ {
+ u8 cpu_speed = 0;
+ for (u8 clock_scale = BIT(u8(m_maincpu->clock_scale()), 0, 3) >> 1; clock_scale; clock_scale >>= 1, ++cpu_speed);
+ port_253b_dat = (0b00 << 6) | (cpu_speed << 4) | (0b00 << 2) | m_nr_07_cpu_speed;
+ }
+ break;
+ case 0x08:
+ port_253b_dat = ((!port_7ffd_locked()) << 7) | (m_eff_nr_08_contention_disable << 6) | (m_nr_08_psg_stereo_mode << 5) | (m_nr_08_internal_speaker_en << 4) | (m_nr_08_dac_en << 3) | (m_nr_08_port_ff_rd_en << 2) | (m_nr_08_psg_turbosound_en << 1) | m_nr_08_keyboard_issue2;
+ break;
+ case 0x09:
+ port_253b_dat = (m_nr_09_psg_mono << 5) | (m_nr_09_sprite_tie << 4) | (0 << 3) | ((!m_nr_09_hdmi_audio_en) << 2) | m_nr_09_scanlines; // m_eff_nr_09_scanlines
+ break;
+ case 0x0a:
+ port_253b_dat = (m_nr_0a_mf_type << 6) | (0 << 5) | (m_nr_0a_divmmc_automap_en << 4) | (m_nr_0a_mouse_button_reverse << 3) | (0 << 2) | m_nr_0a_mouse_dpi;
+ break;
+ case 0x0b:
+ port_253b_dat = (m_nr_0b_joy_iomode_en << 7) | (0 << 6) | (m_nr_0b_joy_iomode << 4) | (0b000 << 1) | m_nr_0b_joy_iomode_0;
+ break;
+ case 0x0e:
+ port_253b_dat = G_SUB_VERSION;
+ break;
+ case 0x0f:
+ port_253b_dat = (0b0000 << 4) | g_board_issue();
+ break;
+ case 0x10:
+ port_253b_dat = (0 << 7) | (m_nr_10_coreid << 2) | 0b00;// | i_SPKEY_BUTTONS(1 downto 0);
+ break;
+ case 0x11:
+ port_253b_dat = (0b00000 << 3) | m_nr_11_video_timing;
+ break;
+ case 0x12:
+ port_253b_dat = (0 << 7) | m_nr_12_layer2_active_bank;
+ break;
+ case 0x13:
+ port_253b_dat = (0 << 7) | m_nr_13_layer2_shadow_bank;
+ break;
+ case 0x14:
+ port_253b_dat = m_nr_14_global_transparent_rgb;
+ break;
+ case 0x15:
+ port_253b_dat = (m_nr_15_lores_en << 7) | (m_nr_15_sprite_priority << 6) | (m_nr_15_sprite_border_clip_en << 5) | (m_nr_15_layer_priority << 2) | (m_nr_15_sprite_over_border_en << 1) | m_nr_15_sprite_en;
+ break;
+ case 0x16:
+ port_253b_dat = m_nr_16_layer2_scrollx;
+ break;
+ case 0x17:
+ port_253b_dat = m_nr_17_layer2_scrolly;
+ break;
+ case 0x18:
+ switch (m_nr_18_layer2_clip_idx)
+ {
+ case 0b00:
+ port_253b_dat = m_nr_18_layer2_clip_x1;
+ break;
+ case 0b01:
+ port_253b_dat = m_nr_18_layer2_clip_x2;
+ break;
+ case 0b10:
+ port_253b_dat = m_nr_18_layer2_clip_y1;
+ break;
+ default:
+ port_253b_dat = m_nr_18_layer2_clip_y2;
+ }
+ break;
+ case 0x19:
+ switch (m_nr_19_sprite_clip_idx)
+ {
+ case 0b00:
+ port_253b_dat = m_nr_19_sprite_clip_x1;
+ break;
+ case 0b01:
+ port_253b_dat = m_nr_19_sprite_clip_x2;
+ break;
+ case 0b10:
+ port_253b_dat = m_nr_19_sprite_clip_y1;
+ break;
+ default:
+ port_253b_dat = m_nr_19_sprite_clip_y2;
+ }
+ break;
+ case 0x1a:
+ switch (m_nr_1a_ula_clip_idx)
+ {
+ case 0b00:
+ port_253b_dat = m_nr_1a_ula_clip_x1;
+ break;
+ case 0b01:
+ port_253b_dat = m_nr_1a_ula_clip_x2;
+ break;
+ case 0b10:
+ port_253b_dat = m_nr_1a_ula_clip_y1;
+ break;
+ default:
+ port_253b_dat = m_nr_1a_ula_clip_y2;
+ }
+ break;
+ case 0x1b:
+ switch (m_nr_1b_tm_clip_idx)
+ {
+ case 0b00:
+ port_253b_dat = m_nr_1b_tm_clip_x1;
+ break;
+ case 0b01:
+ port_253b_dat = m_nr_1b_tm_clip_x2;
+ break;
+ case 0b10:
+ port_253b_dat = m_nr_1b_tm_clip_y1;
+ break;
+ default:
+ port_253b_dat = m_nr_1b_tm_clip_y2;
+ }
+ break;
+ case 0x1c:
+ port_253b_dat = (m_nr_1b_tm_clip_idx << 6) | (m_nr_1a_ula_clip_idx << 4) | (m_nr_19_sprite_clip_idx << 2) | m_nr_18_layer2_clip_idx;
+ break;
+ case 0x1e:
+ port_253b_dat = BIT((m_screen->vpos() - 40) % m_screen->height(), 8);
+ break;
+ case 0x1f:
+ port_253b_dat = ((m_screen->vpos() - 40) % m_screen->height()) & 0xff;
+ break;
+ case 0x20:
+ port_253b_dat = 0;//(BIT(im2_int_status, 0) <<) | (BIT(im2_int_status, 11) <<) | (0b00 <<) | BIT(im2_int_status, 3, 4);
+ break;
+ case 0x22:
+ port_253b_dat = ((!m_pulse_int_n) << 7) | (0b0000 << 3) | (port_ff_interrupt_disable() << 2) | (m_nr_22_line_interrupt_en << 1) | BIT(m_nr_23_line_interrupt, 8);
+ break;
+ case 0x23:
+ port_253b_dat = BIT(m_nr_23_line_interrupt, 0, 8);
+ break;
+ case 0x26:
+ port_253b_dat = m_nr_26_ula_scrollx;
+ break;
+ case 0x27:
+ port_253b_dat = m_nr_27_ula_scrolly;
+ break;
+ case 0x28:
+ port_253b_dat = m_nr_stored_palette_value;
+ break;
+ case 0x2c:
+ port_253b_dat = 0;//pi_audio_L(9 downto 2);
+ //m_nr_2d_i2s_sample = pi_audio_L(1 downto 0);
+ break;
+ case 0x2d:
+ port_253b_dat = (m_nr_2d_i2s_sample << 6) | 0b000000;
+ break;
+ case 0x2e:
+ port_253b_dat = 0;//pi_audio_R(9 downto 2);
+ //m_nr_2d_i2s_sample = pi_audio_R(1 downto 0);
+ break;
+ case 0x2f:
+ port_253b_dat = (0b000000 << 2) | BIT(m_nr_30_tm_scrollx, 8, 2);
+ break;
+ case 0x30:
+ port_253b_dat = BIT(m_nr_30_tm_scrollx, 0, 8);
+ break;
+ case 0x31:
+ port_253b_dat = m_nr_31_tm_scrolly;
+ break;
+ case 0x32:
+ port_253b_dat = m_nr_32_lores_scrollx;
+ break;
+ case 0x33:
+ port_253b_dat = m_nr_33_lores_scrolly;
+ break;
+ case 0x34:
+ port_253b_dat = m_sprites->mirror_num_r() & 0x7f;
+ break;
+ case 0x40:
+ port_253b_dat = m_nr_palette_idx;
+ break;
+ case 0x41:
+ port_253b_dat = BIT(nr_palette_dat(), 1, 8);
+ break;
+ case 0x42:
+ port_253b_dat = m_nr_42_ulanext_format;
+ break;
+ case 0x43:
+ port_253b_dat = (m_nr_43_palette_autoinc_disable << 7) | (m_nr_43_palette_write_select << 4) | (m_nr_43_active_sprite_palette << 3) | (m_nr_43_active_layer2_palette << 2) | (m_nr_43_active_ula_palette << 1) | m_nr_43_ulanext_en;
+ break;
+ case 0x44:
+ port_253b_dat = (BIT(nr_palette_dat(), 9, 2) << 6) | (0b00000 << 1) | BIT(nr_palette_dat(), 0);
+ break;
+ case 0x4a:
+ port_253b_dat = m_nr_4a_fallback_rgb;
+ break;
+ case 0x4b:
+ port_253b_dat = m_nr_4b_sprite_transparent_index;
+ break;
+ case 0x4c:
+ port_253b_dat = (0b0000 << 4) | m_nr_4c_tm_transparent_index;
+ break;
+ case 0x50: case 0x51: case 0x52: case 0x53:
+ case 0x54: case 0x55: case 0x56: case 0x57:
+ port_253b_dat = m_mmu[nr_register - 0x50];
+ break;
+ case 0x61:
+ port_253b_dat = BIT(m_nr_copper_addr, 0, 8);
+ break;
+ case 0x62:
+ port_253b_dat = (m_nr_62_copper_mode << 6) | (0b000 << 3) | BIT(m_nr_copper_addr, 8, 3);
+ break;
+ case 0x64:
+ port_253b_dat = m_nr_64_copper_offset;
+ break;
+ case 0x68:
+ port_253b_dat = ((!m_nr_68_ula_en) << 7) | (m_nr_68_blend_mode << 5) | (m_nr_68_cancel_extended_keys << 4) | (m_port_ff3b_ulap_en << 3) | (m_nr_68_ula_fine_scroll_x << 2) | (0 << 1) | m_nr_68_ula_stencil_mode;
+ break;
+ case 0x69:
+ port_253b_dat = (m_port_123b_layer2_en << 7) | (port_7ffd_shadow() << 6) | BIT(m_port_ff_data, 0, 6);
+ break;
+ case 0x6a:
+ port_253b_dat = (0b00 << 6) | (m_nr_6a_lores_radastan << 5) | (m_nr_6a_lores_radastan_xor << 4) | m_nr_6a_lores_palette_offset;
+ break;
+ case 0x6b:
+ port_253b_dat = (m_nr_6b_tm_en << 7) | m_nr_6b_tm_control;
+ break;
+ case 0x6c:
+ port_253b_dat = m_nr_6c_tm_default_attr;
+ break;
+ case 0x6e:
+ port_253b_dat = (m_nr_6e_tilemap_base_7 << 7) | (0 << 6) | m_nr_6e_tilemap_base;
+ break;
+ case 0x6f:
+ port_253b_dat = (m_nr_6f_tilemap_tiles_7 << 7) | (0 << 6) | m_nr_6f_tilemap_tiles;
+ break;
+ case 0x70:
+ port_253b_dat = (0b00 << 6) | (m_nr_70_layer2_resolution << 4) | m_nr_70_layer2_palette_offset;
+ break;
+ case 0x71:
+ port_253b_dat = (0b0000000 << 1) | m_nr_71_layer2_scrollx_msb;
+ break;
+ case 0x7f:
+ port_253b_dat = m_nr_7f_user_register_0;
+ break;
+ case 0x80:
+ port_253b_dat = m_nr_80_expbus;
+ break;
+ case 0x81:
+ port_253b_dat = /*(i_BUS_ROMCS_n <<) |*/ (m_nr_81_expbus_ula_override << 6) | (m_nr_81_expbus_nmi_debounce_disable << 5) | (m_nr_81_expbus_clken << 4) | (0b00 << 2) | m_nr_81_expbus_speed;
+ break;
+ case 0x82:
+ port_253b_dat = m_nr_82_internal_port_enable;
+ break;
+ case 0x83:
+ port_253b_dat = m_nr_83_internal_port_enable;
+ break;
+ case 0x84:
+ port_253b_dat = m_nr_84_internal_port_enable;
+ break;
+ case 0x85:
+ port_253b_dat = (m_nr_85_internal_port_reset_type << 7) | (0b000 << 4) | m_nr_85_internal_port_enable;
+ break;
+ case 0x86:
+ port_253b_dat = m_nr_86_bus_port_enable;
+ break;
+ case 0x87:
+ port_253b_dat = m_nr_87_bus_port_enable;
+ break;
+ case 0x88:
+ port_253b_dat = m_nr_88_bus_port_enable;
+ break;
+ case 0x89:
+ port_253b_dat = (m_nr_89_bus_port_reset_type << 7) | (0b000 << 4) | m_nr_89_bus_port_enable;
+ break;
+ case 0x8a:
+ port_253b_dat = (0b00 << 6) | m_nr_8a_bus_port_propagate;
+ break;
+ case 0x8c:
+ port_253b_dat = m_nr_8c_altrom;
+ break;
+ case 0x8e:
+ port_253b_dat = (BIT(m_port_dffd_data, 0) << 7) | (BIT(m_port_7ffd_data, 0, 3) << 4) | (1 << 3) | (BIT(m_port_1ffd_data, 0) << 2)
+ | (BIT(m_port_1ffd_data, 2) << 1) | ((BIT(m_port_7ffd_data, 4) && !BIT(m_port_1ffd_data, 0)) || (BIT(m_port_1ffd_data, 1) && BIT(m_port_1ffd_data, 0)));;
+ break;
+ case 0x8f:
+ port_253b_dat = (0b000000 << 2) | m_nr_8f_mapping_mode;
+ break;
+ case 0x90:
+ port_253b_dat = m_nr_90_pi_gpio_o_en;
+ break;
+ case 0x91:
+ port_253b_dat = m_nr_91_pi_gpio_o_en;
+ break;
+ case 0x92:
+ port_253b_dat = m_nr_92_pi_gpio_o_en;
+ break;
+ case 0x93:
+ port_253b_dat = (0b0000 << 4) | m_nr_93_pi_gpio_o_en;
+ break;
+ case 0x98:
+ port_253b_dat = 0;//i_GPIO(7 downto 0);
+ break;
+ case 0x99:
+ port_253b_dat = 0;//i_GPIO(15 downto 8);
+ break;
+ case 0x9a:
+ port_253b_dat = 0;//i_GPIO(23 downto 16);
+ break;
+ case 0x9b:
+ port_253b_dat = (0b0000 << 4);// | i_GPIO(27 downto 24);
+ break;
+ case 0xa0:
+ port_253b_dat = (0b00 << 6) | (BIT(m_nr_a0_pi_peripheral_en, 3, 3) << 2) | (0b00 << 1) | BIT(m_nr_a0_pi_peripheral_en, 0);
+ break;
+ case 0xa2:
+ port_253b_dat = (BIT(m_nr_a2_pi_i2s_ctl, 6, 2) << 6) | (0 << 5) | (BIT(m_nr_a2_pi_i2s_ctl, 2, 3) << 2) | (1 << 1) | BIT(m_nr_a2_pi_i2s_ctl, 0);
+ break;
+ case 0xa8:
+ port_253b_dat = (0b0000000 << 7) | m_nr_a8_esp_gpio0_en;
+ break;
+ case 0xa9:
+ port_253b_dat = (0b00000 << 3);// | (i_ESP_GPIO_20(2) <<) | (0 << 1) | i_ESP_GPIO_20(0);
+ break;
+ case 0xb0:
+ port_253b_dat = 0;//(i_KBD_EXTENDED_KEYS(8) <<) | (i_KBD_EXTENDED_KEYS(9) <<) | (i_KBD_EXTENDED_KEYS(10) <<) | (i_KBD_EXTENDED_KEYS(11) <<) | (i_KBD_EXTENDED_KEYS(1) <<) | i_KBD_EXTENDED_KEYS(15 downto 13);
+ break;
+ case 0xb1:
+ port_253b_dat = 0;//(i_KBD_EXTENDED_KEYS(12) <<) | (i_KBD_EXTENDED_KEYS(7 downto 2) <<) | i_KBD_EXTENDED_KEYS(0);
+ break;
+ case 0xb2:
+ port_253b_dat = 0;//(i_JOY_RIGHT(10 downto 8) <<) | (i_JOY_RIGHT(11) <<) | (i_JOY_LEFT(10 downto 8) <<) | i_JOY_LEFT(11);
+ break;
+ case 0xb8:
+ port_253b_dat = m_nr_b8_divmmc_ep_0;
+ break;
+ case 0xb9:
+ port_253b_dat = m_nr_b9_divmmc_ep_valid_0;
+ break;
+ case 0xba:
+ port_253b_dat = m_nr_ba_divmmc_ep_timing_0;
+ break;
+ case 0xbb:
+ port_253b_dat = m_nr_bb_divmmc_ep_1;
+ break;
+ case 0xc0:
+ port_253b_dat = (m_nr_c0_im2_vector << 5) | (0 << 4) | (m_nr_c0_stackless_nmi << 3) | /*(z80_im_mode << 1) |*/ m_nr_c0_int_mode_pulse_0_im2_1;
+ break;
+ case 0xc2:
+ port_253b_dat = m_nr_c2_retn_address_lsb;
+ break;
+ case 0xc3:
+ port_253b_dat = m_nr_c3_retn_address_msb;
+ break;
+ case 0xc4:
+ {
+ const u8 ula_int_en = (m_nr_22_line_interrupt_en << 1) | !port_ff_interrupt_disable();
+ port_253b_dat = (m_nr_c4_int_en_0_expbus << 7) | (0b00000 << 2) | ula_int_en;
+ }
+ break;
+ case 0xc5:
+ port_253b_dat = 0;//ctc_int_en;
+ break;
+ case 0xc6:
+ port_253b_dat = (0 << 7) | (m_nr_c6_int_en_2_654 << 4) | (0 << 3) | m_nr_c6_int_en_2_210;
+ break;
+ case 0xc8:
+ port_253b_dat = (0b000000 << 2);// | (im2_int_status(0) << 1) | im2_int_status(11);
+ break;
+ case 0xc9:
+ port_253b_dat = 0;//im2_int_status(10 downto 3);
+ break;
+ case 0xca:
+ port_253b_dat = (0 << 7);// | (im2_int_status(13) << 6) | (im2_int_status(2) << 5) | (im2_int_status(2) << 4) | (0 << 3) | (im2_int_status(12) << 2) | (im2_int_status(1) << 1) | im2_int_status(1);
+ break;
+ case 0xcc:
+ port_253b_dat = (m_nr_cc_dma_int_en_0_7 << 7) | (0b00000 << 2) | m_nr_cc_dma_int_en_0_10;
+ break;
+ case 0xcd:
+ port_253b_dat = m_nr_cd_dma_int_en_1;
+ break;
+ case 0xce:
+ port_253b_dat = (0 << 7) | (m_nr_ce_dma_int_en_2_654 << 4) | (0 << 3) | m_nr_ce_dma_int_en_2_210;
+ break;
+ case 0xd8:
+ port_253b_dat = (0b0000000 << 1) | m_nr_d8_io_trap_fdc_en;
+ break;
+ case 0xd9:
+ port_253b_dat = m_nr_d9_iotrap_write;
+ break;
+ case 0xda:
+ port_253b_dat = (0b000000 << 2) | m_nr_da_iotrap_cause;
+ break;
+ case 0xf0:
+ port_253b_dat = m_nr_f0_xdev_cmd;
+ break;
+ case 0xf8:
+ port_253b_dat = (0 << 7) | m_nr_f8_xadc_daddr;
+ break;
+ case 0xf9:
+ port_253b_dat = m_nr_f9_xadc_d0;
+ break;
+ case 0xfa:
+ port_253b_dat = m_nr_fa_xadc_d1;
+ break;
+ default:
+ port_253b_dat = 0x00;
+ if (!machine().side_effects_disabled())
+ LOGWARN("rR: %X -> %x\n", nr_register, port_253b_dat);
+ }
+
+ return port_253b_dat;
+}
+
+void specnext_state::reg_w(offs_t nr_wr_reg, u8 nr_wr_dat)
+{
+ switch (nr_wr_reg)
+ {
+ case 0x02:
+ nr_02_w(nr_wr_dat);
+ break;
+ case 0x03:
+ m_bootrom_en = 0;
+
+ if (BIT(nr_wr_dat, 7) == 1 && m_nr_03_user_dt_lock == 0 && BIT(nr_wr_dat, 3) == 0)
+ {
+ switch (BIT(nr_wr_dat, 4, 3))
+ {
+ case 0b000:
+ m_nr_03_machine_timing = 0b001;
+ break;
+ case 0b001:
+ m_nr_03_machine_timing = 0b001;
+ break;
+ case 0b010:
+ m_nr_03_machine_timing = 0b010;
+ break;
+ case 0b011:
+ m_nr_03_machine_timing = 0b011;
+ break;
+ case 0b100:
+ m_nr_03_machine_timing = 0b100;
+ break;
+ default:
+ m_nr_03_machine_timing = 0b011;
+ }
+ }
+
+ m_nr_03_user_dt_lock = m_nr_03_user_dt_lock ^ BIT(nr_wr_dat, 3);
+
+ if (m_nr_03_config_mode == 1)
+ {
+ switch (BIT(nr_wr_dat, 0, 3))
+ {
+ case 0b001:
+ m_nr_03_machine_type = 0b001;
+ break;
+ case 0b010:
+ m_nr_03_machine_type = 0b010;
+ break;
+ case 0b011:
+ m_nr_03_machine_type = 0b011;
+ break;
+ case 0b100:
+ m_nr_03_machine_type = 0b100;
+ break;
+ }
+ }
+
+ if (BIT(nr_wr_dat, 0, 3) == 0b111)
+ m_nr_03_config_mode = 1;
+ else if (BIT(nr_wr_dat, 0, 3) != 0b000)
+ m_nr_03_config_mode = 0;
+
+ break;
+ case 0x04:
+ m_nr_04_romram_bank = BIT(nr_wr_dat, 0, 7);
+ bank_update(0, 2);
+ break;
+ case 0x05:
+ m_nr_05_joy0 = (BIT(nr_wr_dat, 3) << 2) | BIT(nr_wr_dat, 6, 2);
+ m_nr_05_joy1 = (BIT(nr_wr_dat, 1) << 2) | BIT(nr_wr_dat, 4, 2);
+ break;
+ case 0x06:
+ m_nr_06_hotkey_cpu_speed_en = BIT(nr_wr_dat, 7);
+ m_nr_06_internal_speaker_beep = BIT(nr_wr_dat, 6);
+ m_nr_06_hotkey_5060_en = BIT(nr_wr_dat, 5);
+ m_nr_06_button_drive_nmi_en = BIT(nr_wr_dat, 4);
+ m_nr_06_button_m1_nmi_en = BIT(nr_wr_dat, 3);
+ if (m_nr_03_config_mode == 1)
+ m_nr_06_ps2_mode = BIT(nr_wr_dat, 2);
+ m_nr_06_psg_mode = BIT(nr_wr_dat, 0, 2);
+ break;
+ case 0x07:
+ nr_07_cpu_speed_w(BIT(nr_wr_dat, 0, 2));
+ break;
+ case 0x08:
+ m_nr_08_contention_disable = BIT(nr_wr_dat, 6);
+ m_eff_nr_08_contention_disable = m_nr_08_contention_disable;
+ m_nr_08_psg_stereo_mode = BIT(nr_wr_dat, 5);
+ m_nr_08_internal_speaker_en = BIT(nr_wr_dat, 4);
+ m_nr_08_dac_en = BIT(nr_wr_dat, 3);
+ m_nr_08_port_ff_rd_en = BIT(nr_wr_dat, 2);
+ m_nr_08_psg_turbosound_en = BIT(nr_wr_dat, 1);
+ m_nr_08_keyboard_issue2 = BIT(nr_wr_dat, 0);
+
+ if (BIT(nr_wr_dat, 7))
+ port_7ffd_reg_w(m_port_7ffd_data &= ~0x20);
+ break;
+ case 0x09:
+ m_nr_09_psg_mono = BIT(nr_wr_dat, 5, 3);
+ m_nr_09_sprite_tie = BIT(nr_wr_dat, 4);
+ m_sprites->mirror_tie_w(m_nr_09_sprite_tie);
+ m_nr_09_hdmi_audio_en = not BIT(nr_wr_dat, 2);
+
+ if (BIT(nr_wr_dat, 3))
+ port_e3_reg_w(m_port_e3_reg & ~0x40);
+ m_nr_09_scanlines = BIT(nr_wr_dat, 0, 2);
+ break;
+ case 0x0a:
+ if (m_nr_03_config_mode == 1)
+ nr_0a_mf_type_w(BIT(nr_wr_dat, 6, 2));
+ m_nr_0a_divmmc_automap_en = BIT(nr_wr_dat, 4);
+ m_nr_0a_mouse_button_reverse = BIT(nr_wr_dat, 3);
+ m_nr_0a_mouse_dpi = BIT(nr_wr_dat, 0, 2);
+ break;
+ case 0x0b:
+ m_nr_0b_joy_iomode_en = BIT(nr_wr_dat, 7);
+ m_nr_0b_joy_iomode = BIT(nr_wr_dat, 4, 2);
+ m_nr_0b_joy_iomode_0 = BIT(nr_wr_dat, 0);
+ break;
+ case 0x10:
+ m_nr_10_flashboot = BIT(nr_wr_dat, 7);
+ if (m_nr_03_config_mode)
+ {
+ if (g_board_issue() != 2)
+ {
+ m_nr_10_coreid = BIT(nr_wr_dat, 0, 5);
+ }
+ else if (g_board_issue() == 2)
+ {
+ if (BIT(~nr_wr_dat, 4) && BIT(nr_wr_dat, 0, 4) != 0b1111)
+ m_nr_10_coreid = BIT(nr_wr_dat, 0, 4);
+ }
+ }
+ break;
+ case 0x11:
+ if (m_nr_03_config_mode == 1)
+ {
+ if (BIT(nr_wr_dat, 0, 3) == 0b111)
+ m_nr_11_video_timing = 0b000;
+ else if (G_VIDEO_INC == 0b10)
+ m_nr_11_video_timing = (0b00 << 1) | BIT(nr_wr_dat, 0);
+ else
+ m_nr_11_video_timing = BIT(nr_wr_dat, 0, 3);
+ }
+ break;
+ case 0x12:
+ nr_12_layer2_active_bank_w(nr_wr_dat);
+ break;
+ case 0x13:
+ nr_13_layer2_shadow_bank_w(nr_wr_dat);
+ break;
+ case 0x14:
+ nr_14_global_transparent_rgb_w(nr_wr_dat);
+ break;
+ case 0x15:
+ m_screen->update_now();
+ m_nr_15_lores_en = BIT(nr_wr_dat, 7);
+ nr_15_sprite_priority_w(BIT(nr_wr_dat, 6));
+ nr_15_sprite_border_clip_en_w(BIT(nr_wr_dat, 5));
+ m_nr_15_layer_priority = BIT(nr_wr_dat, 2, 3);
+ nr_15_sprite_over_border_en_w(BIT(nr_wr_dat, 1));
+ m_nr_15_sprite_en = BIT(nr_wr_dat, 0);
+ break;
+ case 0x16:
+ m_screen->update_now();
+ nr_16_layer2_scrollx_w(nr_wr_dat);
+ break;
+ case 0x17:
+ m_screen->update_now();
+ nr_17_layer2_scrolly_w(nr_wr_dat);
+ break;
+ case 0x18:
+ switch (m_nr_18_layer2_clip_idx)
+ {
+ case 0b00:
+ nr_18_layer2_clip_x1_w(nr_wr_dat);
+ break;
+ case 0b01:
+ nr_18_layer2_clip_x2_w(nr_wr_dat);
+ break;
+ case 0b10:
+ nr_18_layer2_clip_y1_w(nr_wr_dat);
+ break;
+ default:
+ nr_18_layer2_clip_y2_w(nr_wr_dat);
+ }
+ ++m_nr_18_layer2_clip_idx &= 0x03;
+ break;
+ case 0x19:
+ switch (m_nr_19_sprite_clip_idx)
+ {
+ case 0b00:
+ nr_19_sprite_clip_x1_w(nr_wr_dat);
+ break;
+ case 0b01:
+ nr_19_sprite_clip_x2_w(nr_wr_dat);
+ break;
+ case 0b10:
+ nr_19_sprite_clip_y1_w(nr_wr_dat);
+ break;
+ default:
+ nr_19_sprite_clip_y2_w(nr_wr_dat);
+ }
+ ++m_nr_19_sprite_clip_idx &= 0x03;
+ break;
+ case 0x1a:
+ switch (m_nr_1a_ula_clip_idx)
+ {
+ case 0b00:
+ nr_1a_ula_clip_x1_w(nr_wr_dat);
+ break;
+ case 0b01:
+ nr_1a_ula_clip_x2_w(nr_wr_dat);
+ break;
+ case 0b10:
+ nr_1a_ula_clip_y1_w(nr_wr_dat);
+ break;
+ default:
+ nr_1a_ula_clip_y2_w(nr_wr_dat);
+ }
+ ++m_nr_1a_ula_clip_idx &= 0x03;
+ break;
+ case 0x1b:
+ switch (m_nr_1b_tm_clip_idx)
+ {
+ case 0b00:
+ nr_1b_tm_clip_x1_w(nr_wr_dat);
+ break;
+ case 0b01:
+ nr_1b_tm_clip_x2_w(nr_wr_dat);
+ break;
+ case 0b10:
+ nr_1b_tm_clip_y1_w(nr_wr_dat);
+ break;
+ default:
+ nr_1b_tm_clip_y2_w(nr_wr_dat);
+ }
+ ++m_nr_1b_tm_clip_idx &= 0x03;
+ break;
+ case 0x1c:
+ if (BIT(nr_wr_dat, 0) == 1)
+ m_nr_18_layer2_clip_idx = 0b00;
+ if (BIT(nr_wr_dat, 1) == 1)
+ m_nr_19_sprite_clip_idx = 0b00;
+ if (BIT(nr_wr_dat, 2) == 1)
+ m_nr_1a_ula_clip_idx = 0b00;
+ if (BIT(nr_wr_dat, 3) == 1)
+ m_nr_1b_tm_clip_idx = 0b00;
+ break;
+ case 0x22:
+ m_nr_22_line_interrupt_en = BIT(nr_wr_dat, 1);
+ m_nr_23_line_interrupt = (m_nr_23_line_interrupt & ~0x0100) | (BIT(nr_wr_dat, 0) << 8);
+ line_irq_adjust();
+ m_port_ff_data = (m_port_ff_data & 0xbf) | (BIT(nr_wr_dat, 1) << 6);
+ break;
+ case 0x23:
+ m_nr_23_line_interrupt = (m_nr_23_line_interrupt & ~0x00ff) | nr_wr_dat;
+ line_irq_adjust();
+ break;
+ case 0x26:
+ m_screen->update_now();
+ nr_26_ula_scrollx_w(nr_wr_dat);
+ break;
+ case 0x27:
+ m_screen->update_now();
+ nr_27_ula_scrolly_w(nr_wr_dat);
+ break;
+ case 0x28:
+ //nr_keymap_sel <= nr_wr_dat(7);
+ //nr_keymap_addr(8) <= nr_wr_dat(0);
+ break;
+ case 0x29:
+ //nr_keymap_addr(7 downto 0) <= nr_wr_dat;
+ break;
+ case 0x2a:
+ break;
+ case 0x2b:
+ // nr_keymap_addr <= nr_keymap_addr + 1;
+ break;
+ case 0x2c:
+ m_dac[1]->data_w(nr_wr_dat);
+ break;
+ case 0x2d:
+ m_dac[0]->data_w(nr_wr_dat);
+ m_dac[3]->data_w(nr_wr_dat);
+ break;
+ case 0x2e:
+ m_dac[2]->data_w(nr_wr_dat);
+ break;
+ case 0x2f:
+ m_screen->update_now();
+ nr_30_tm_scrollx_w((m_nr_30_tm_scrollx & ~0x0300) | (BIT(nr_wr_dat, 0, 2) << 8));
+ break;
+ case 0x30:
+ m_screen->update_now();
+ nr_30_tm_scrollx_w((m_nr_30_tm_scrollx & ~0x00ff) | nr_wr_dat);
+ break;
+ case 0x31:
+ m_screen->update_now();
+ nr_31_tm_scrolly_w(nr_wr_dat);
+ break;
+ case 0x32:
+ m_screen->update_now();
+ nr_32_lores_scrollx_w(nr_wr_dat);
+ break;
+ case 0x33:
+ m_screen->update_now();
+ nr_33_lores_scrolly_w(nr_wr_dat);
+ break;
+ case 0x34:
+ m_sprites->mirror_data_w(nr_wr_dat);
+ break;
+ case 0x35: case 0x36: case 0x37: case 0x38: case 0x39:
+ case 0x75: case 0x76: case 0x77: case 0x78: case 0x79:
+ m_sprites->mirror_inc_w(BIT(nr_wr_reg, 6));
+ m_sprites->mirror_index_w((nr_wr_reg & 0x3f) - 0x35);
+ m_sprites->mirror_data_w(nr_wr_dat);
+ break;
+ case 0x40:
+ m_nr_palette_idx = nr_wr_dat;
+ m_nr_palette_sub_idx = 0;
+ break;
+ case 0x41:
+ palette_val_w(0b00, (nr_wr_dat << 1) | BIT(nr_wr_dat, 1) | BIT(nr_wr_dat, 0));
+ if (m_nr_43_palette_autoinc_disable == 0)
+ ++m_nr_palette_idx;
+ m_nr_palette_sub_idx = 0;
+ break;
+ case 0x42:
+ nr_42_ulanext_format_w(nr_wr_dat);
+ break;
+ case 0x43:
+ m_nr_43_palette_autoinc_disable = BIT(nr_wr_dat, 7);
+ m_nr_43_palette_write_select = BIT(nr_wr_dat, 4, 3);
+ nr_43_active_sprite_palette_w(BIT(nr_wr_dat, 3));
+ nr_43_active_layer2_palette_w(BIT(nr_wr_dat, 2));
+ nr_43_active_ula_palette_w(BIT(nr_wr_dat, 1));
+ nr_43_ulanext_en_w(BIT(nr_wr_dat, 0));
+ m_nr_palette_sub_idx = 0;
+ break;
+ case 0x44:
+ if (m_nr_palette_sub_idx == 0)
+ m_nr_stored_palette_value = nr_wr_dat;
+ else
+ {
+ palette_val_w(BIT(nr_wr_dat, 6, 2), (m_nr_stored_palette_value << 1) | BIT(nr_wr_dat, 0));
+ if (m_nr_43_palette_autoinc_disable == 0)
+ ++m_nr_palette_idx;
+ }
+ m_nr_palette_sub_idx = !m_nr_palette_sub_idx;
+ break;
+ case 0x4a:
+ {
+ m_nr_4a_fallback_rgb = nr_wr_dat;
+ rgb_t fb = rgbexpand<3,3,3>((m_nr_4a_fallback_rgb << 1) | BIT(m_nr_4a_fallback_rgb, 1) | BIT(m_nr_4a_fallback_rgb, 0), 6, 3, 0);
+ m_palette->set_pen_color(UTM_FALLBACK_PEN, fb);
+ }
+ break;
+ case 0x4b:
+ nr_4b_sprite_transparent_index_w(nr_wr_dat);
+ break;
+ case 0x4c:
+ nr_4c_tm_transparent_index_w(BIT(nr_wr_dat, 0, 4));
+ break;
+ case 0x50: case 0x51: case 0x52: case 0x53:
+ case 0x54: case 0x55: case 0x56: case 0x57:
+ mmu_w(nr_wr_reg - 0x50, nr_wr_dat);
+ break;
+ case 0x60:
+ case 0x63:
+ {
+ const bool nr_copper_write_8 = nr_wr_reg == 0x60;
+ if (BIT(m_nr_copper_addr, 0) == 0)
+ {
+ if (nr_copper_write_8)
+ m_copper->data_w(m_nr_copper_addr, nr_wr_dat); // msb
+ }
+ else
+ {
+ if (!nr_copper_write_8)
+ m_copper->data_w(m_nr_copper_addr & ~1, m_nr_copper_data_stored); // msb
+ m_copper->data_w(m_nr_copper_addr, nr_wr_dat); // lsb
+ }
+
+ if (BIT(m_nr_copper_addr, 0) == 0)
+ m_nr_copper_data_stored = nr_wr_dat;
+ ++m_nr_copper_addr %= 0x800;
+ }
+ break;
+ case 0x61:
+ m_nr_copper_addr = (m_nr_copper_addr & ~0x00ff) | nr_wr_dat;
+ break;
+ case 0x62:
+ nr_62_copper_mode_w(BIT(nr_wr_dat, 6, 2));
+ m_nr_copper_addr = (m_nr_copper_addr & ~0x0700) | (BIT(nr_wr_dat, 0, 3) << 8);
+ break;
+ case 0x64:
+ m_nr_64_copper_offset = nr_wr_dat;
+ break;
+ case 0x68:
+ m_screen->update_now();
+ m_nr_68_ula_en = not BIT(nr_wr_dat, 7);
+ m_nr_68_blend_mode = BIT(nr_wr_dat, 5, 2);
+ m_nr_68_cancel_extended_keys = BIT(nr_wr_dat, 4);
+ port_ff3b_ulap_en_w(BIT(nr_wr_dat, 3));
+ nr_68_ula_fine_scroll_x_w(BIT(nr_wr_dat, 2));
+ m_nr_68_ula_stencil_mode = BIT(nr_wr_dat, 0);
+ break;
+ case 0x69:
+ m_port_ff_data = (m_port_ff_data & 0xc0) | (nr_wr_dat & 0x3f);
+ port_7ffd_reg_w((m_port_7ffd_data & ~0x08) | (BIT(nr_wr_dat, 6) << 3));
+ port_123b_layer2_en_w(BIT(nr_wr_dat, 7));
+ break;
+ case 0x6a:
+ nr_6a_lores_radastan_w(BIT(nr_wr_dat, 5));
+ nr_6a_lores_radastan_xor_w(BIT(nr_wr_dat, 4));
+ nr_6a_lores_palette_offset_w(BIT(nr_wr_dat, 0, 4));
+ break;
+ case 0x6b:
+ m_screen->update_now();
+ m_nr_6b_tm_en = BIT(nr_wr_dat, 7);
+ nr_6b_tm_control_w(BIT(nr_wr_dat, 0, 7));
+ break;
+ case 0x6c:
+ nr_6c_tm_default_attr_w(nr_wr_dat);
+ break;
+ case 0x6e:
+ nr_6e_tilemap_base_w(BIT(nr_wr_dat, 7), BIT(nr_wr_dat, 0, 6));
+ break;
+ case 0x6f:
+ nr_6f_tilemap_tiles_w(BIT(nr_wr_dat, 7), BIT(nr_wr_dat, 0, 6));
+ break;
+ case 0x70:
+ nr_70_layer2_resolution_w(BIT(nr_wr_dat, 4, 2));
+ nr_70_layer2_palette_offset_w(BIT(nr_wr_dat, 0, 4));
+ break;
+ case 0x71:
+ nr_71_layer2_scrollx_msb_w(BIT(nr_wr_dat, 0));
+ break;
+ case 0x7f:
+ m_nr_7f_user_register_0 = nr_wr_dat;
+ break;
+ case 0x80:
+ m_nr_80_expbus = nr_wr_dat;
+ break;
+ case 0x81:
+ m_nr_81_expbus_ula_override = BIT(nr_wr_dat, 6);
+ m_nr_81_expbus_nmi_debounce_disable = BIT(nr_wr_dat, 5);
+ m_nr_81_expbus_clken = BIT(nr_wr_dat, 4);
+ m_nr_81_expbus_speed = 0b00;
+ break;
+ case 0x82:
+ m_nr_82_internal_port_enable = nr_wr_dat;
+ break;
+ case 0x83:
+ m_nr_83_internal_port_enable = nr_wr_dat;
+ break;
+ case 0x84:
+ m_nr_84_internal_port_enable = nr_wr_dat;
+ break;
+ case 0x85:
+ m_nr_85_internal_port_enable = BIT(nr_wr_dat, 0, 4);
+ m_nr_85_internal_port_reset_type = BIT(nr_wr_dat, 7);
+ break;
+ case 0x86:
+ m_nr_86_bus_port_enable = nr_wr_dat;
+ break;
+ case 0x87:
+ m_nr_87_bus_port_enable = nr_wr_dat;
+ break;
+ case 0x88:
+ m_nr_88_bus_port_enable = nr_wr_dat;
+ break;
+ case 0x89:
+ m_nr_89_bus_port_enable = BIT(nr_wr_dat, 0, 4);
+ m_nr_89_bus_port_reset_type = BIT(nr_wr_dat, 7);
+ break;
+ case 0x8a:
+ m_nr_8a_bus_port_propagate = BIT(nr_wr_dat, 0, 6);
+ break;
+ case 0x8c:
+ m_nr_8c_altrom = nr_wr_dat;
+ bank_update(0, 2);
+ break;
+ case 0x8e:
+ LOGMEM("8e (%x): 1f=%x 7f=%x df=%x\n", nr_wr_dat, m_port_1ffd_data, m_port_7ffd_data, m_port_dffd_data);
+ if (BIT(nr_wr_dat, 3))
+ {
+ if (!nr_8f_mapping_mode_profi())
+ m_port_dffd_data &= ~0x08;
+ m_port_dffd_data = (m_port_dffd_data & ~0x07) | BIT(nr_wr_dat, 7);
+ port_7ffd_reg_w((m_port_7ffd_data & ~0x07) | BIT(nr_wr_dat, 4, 3));
+ }
+
+ if (BIT(~nr_wr_dat, 2))
+ port_7ffd_reg_w((m_port_7ffd_data & ~0x10) | ((nr_wr_dat & 1) << 4));
+
+ m_port_1ffd_data = (m_port_1ffd_data & ~0x04) | (BIT(nr_wr_dat, 1) << 2);
+ m_port_1ffd_data = (m_port_1ffd_data & ~0x02) | (BIT(nr_wr_dat, 0) << 1);
+ m_port_1ffd_data = (m_port_1ffd_data & ~0x01) | BIT(nr_wr_dat, 2);
+
+ memory_change(0x8e, nr_wr_dat);
+ LOGMEM("8e: 1f=%x 7f=%x df=%x\n", m_port_1ffd_data, m_port_7ffd_data, m_port_dffd_data);
+ break;
+ case 0x8f:
+ m_nr_8f_mapping_mode = BIT(nr_wr_dat, 0, 2);
+ memory_change(0x8f, nr_wr_dat);
+ break;
+ case 0x90:
+ m_nr_90_pi_gpio_o_en = (BIT(nr_wr_dat, 2, 6) << 2) | 0b00;
+ break;
+ case 0x91:
+ m_nr_91_pi_gpio_o_en = nr_wr_dat;
+ break;
+ case 0x92:
+ m_nr_92_pi_gpio_o_en = nr_wr_dat;
+ break;
+ case 0x93:
+ m_nr_93_pi_gpio_o_en = BIT(nr_wr_dat, 0, 4);
+ break;
+ case 0x98:
+ m_nr_98_pi_gpio_o = nr_wr_dat;
+ break;
+ case 0x99:
+ m_nr_99_pi_gpio_o = nr_wr_dat;
+ break;
+ case 0x9a:
+ m_nr_9a_pi_gpio_o = nr_wr_dat;
+ break;
+ case 0x9b:
+ m_nr_9b_pi_gpio_o = BIT(nr_wr_dat, 0, 4);
+ break;
+ case 0xa0:
+ m_nr_a0_pi_peripheral_en = nr_wr_dat;
+ break;
+ case 0xa2:
+ m_nr_a2_pi_i2s_ctl = nr_wr_dat;
+ break;
+ case 0xa8:
+ m_nr_a8_esp_gpio0_en = BIT(nr_wr_dat, 0);
+ break;
+ case 0xa9:
+ m_nr_a9_esp_gpio0 = BIT(nr_wr_dat, 0);
+ break;
+ case 0xb8:
+ m_nr_b8_divmmc_ep_0 = nr_wr_dat;
+ break;
+ case 0xb9:
+ m_nr_b9_divmmc_ep_valid_0 = nr_wr_dat;
+ break;
+ case 0xba:
+ m_nr_ba_divmmc_ep_timing_0 = nr_wr_dat;
+ break;
+ case 0xbb:
+ m_nr_bb_divmmc_ep_1 = nr_wr_dat;
+ break;
+ case 0xc0:
+ nr_c0_im2_vector_w(BIT(nr_wr_dat, 5, 3));
+ m_nr_c0_stackless_nmi = BIT(nr_wr_dat, 3);
+ m_maincpu->nmi_stackless_w(m_nr_c0_stackless_nmi);
+ m_nr_c0_int_mode_pulse_0_im2_1 = BIT(nr_wr_dat, 0);
+ break;
+ case 0xc2:
+ m_nr_c2_retn_address_lsb = nr_wr_dat;
+ break;
+ case 0xc3:
+ m_nr_c3_retn_address_msb = nr_wr_dat;
+ break;
+ case 0xc4:
+ m_nr_c4_int_en_0_expbus = BIT(nr_wr_dat, 7);
+ m_nr_22_line_interrupt_en = BIT(nr_wr_dat, 1);
+ m_port_ff_data = (m_port_ff_data & 0xbf) | (BIT(~nr_wr_dat, 0) << 6);
+ break;
+ case 0xc5:
+ {
+ u8 active = BIT(nr_wr_dat, 0, 4);
+ for (auto ch = 0; ch < 4; ++ch, active >>= 1)
+ m_ctc->write(ch, 1 | ((active & 1) ? 0 : 2)); // for inactive: CONTROL + RESET
+ }
+ break;
+ case 0xc6:
+ m_nr_c6_int_en_2_654 = (BIT(nr_wr_dat, 6) << 2) | (BIT(nr_wr_dat, 5) << 1) | BIT(nr_wr_dat, 4);
+ m_nr_c6_int_en_2_210 = (BIT(nr_wr_dat, 2) << 2) | (BIT(nr_wr_dat, 1) << 1) | BIT(nr_wr_dat, 0);
+ break;
+ case 0xcc:
+ m_nr_cc_dma_int_en_0_7 = BIT(nr_wr_dat, 7);
+ m_nr_cc_dma_int_en_0_10 = BIT(nr_wr_dat, 0, 2);
+ break;
+ case 0xcd:
+ m_nr_cd_dma_int_en_1 = nr_wr_dat;
+ break;
+ case 0xce:
+ m_nr_ce_dma_int_en_2_654 = BIT(nr_wr_dat, 4, 3);
+ m_nr_ce_dma_int_en_2_210 = BIT(nr_wr_dat, 0, 3);
+ break;
+ case 0xd8:
+ m_nr_d8_io_trap_fdc_en = BIT(nr_wr_dat, 0);
+ break;
+ case 0xd9:
+ m_nr_d9_iotrap_write = nr_wr_dat;
+ break;
+ case 0xff:
+ popmessage("Debug: #%02X\n", nr_wr_dat); // LED
+ break;
+ default:
+ LOGWARN("wR: %X <- %x\n", nr_wr_reg, nr_wr_dat);
+ break;
+ }
+
+ m_sprites->mirror_index_w(0b111);
+ m_sprites->mirror_inc_w(0);
+}
+
+void specnext_state::nr_02_w(u8 nr_wr_dat)
+{
+ m_nr_02_bus_reset = BIT(nr_wr_dat, 7);
+ if (BIT(~nr_wr_dat, 4))
+ m_nr_da_iotrap_cause = 0;
+
+ m_nr_02_generate_mf_nmi = BIT(nr_wr_dat, 3);
+ do_mf_nmi();
+
+ if (BIT(nr_wr_dat, 2))
+ {
+ if (m_nr_06_button_drive_nmi_en)
+ {
+ m_nr_02_generate_divmmc_nmi = 1;
+ m_maincpu->nmi();
+ }
+ }
+ else
+ {
+ m_nr_02_generate_divmmc_nmi = 0;
+ }
+
+ if (BIT(nr_wr_dat, 1)) // hard reset
+ {
+ m_nr_02_hard_reset = 1;
+ machine().schedule_soft_reset();
+ }
+ else if (BIT(nr_wr_dat, 0)) // soft reset
+ {
+ m_nr_02_reset_type = (BIT(m_nr_02_reset_type, 2) << 1) | BIT(m_nr_02_reset_type, 1) | BIT(m_nr_02_reset_type, 0);
+ machine().schedule_soft_reset();
+ }
+}
+
+void specnext_state::nr_07_cpu_speed_w(u8 data)
+{
+ m_nr_07_cpu_speed = data & 3;
+ m_maincpu->set_clock_scale(1 << m_nr_07_cpu_speed);
+ m_dma->set_clock_scale(1 << m_nr_07_cpu_speed);
+}
+
+void specnext_state::nr_14_global_transparent_rgb_w(u8 data)
+{
+ m_nr_14_global_transparent_rgb = data;
+ m_global_transparent = (m_nr_14_global_transparent_rgb << 1) | BIT(m_nr_14_global_transparent_rgb, 1) | BIT(m_nr_14_global_transparent_rgb, 0);
+ m_ula->set_global_transparent(data);
+ m_lores->set_global_transparent(data);
+ m_layer2->set_global_transparent(data);
+}
+
+void specnext_state::nr_1a_ula_clip_y2_w(u8 data)
+{
+ m_nr_1a_ula_clip_y2 = data;
+ const u8 ula_clip_y2_0 = ((m_nr_1a_ula_clip_y2 & 0xc0) == 0xc0) ? 0xbf : m_nr_1a_ula_clip_y2;
+ m_ula->ula_clip_y2_w(ula_clip_y2_0);
+ m_lores->clip_y2_w(ula_clip_y2_0);
+}
+
+static const z80_daisy_config z80_daisy_chain[] =
+{
+ { "ctc" },
+ { nullptr }
+};
+
+TIMER_CALLBACK_MEMBER(specnext_state::irq_off)
+{
+ spectrum_state::irq_off(param);
+ m_irq_mask = 0;
+}
+
+TIMER_CALLBACK_MEMBER(specnext_state::irq_on)
+{
+ spectrum_state::irq_on(param);
+ m_irq_mask |= 1 << 11;
+ m_irq_off_timer->adjust(m_maincpu->clocks_to_attotime(32));
+}
+
+TIMER_CALLBACK_MEMBER(specnext_state::line_irq_on)
+{
+ m_screen->update_now();
+ m_maincpu->set_input_line(INPUT_LINE_IRQ0, ASSERT_LINE);
+ m_irq_mask |= 1 << 0;
+ m_irq_off_timer->adjust(m_maincpu->clocks_to_attotime(32));
+}
+
+INTERRUPT_GEN_MEMBER(specnext_state::specnext_interrupt)
+{
+ m_tiles->control_w(m_nr_6b_tm_control); // TODO (1): Santa's Pressie, The Next War
+
+ line_irq_adjust();
+ if (!port_ff_interrupt_disable())
+ {
+ m_irq_on_timer->adjust(m_screen->time_until_pos(SCR_256x192.top(), SCR_256x192.left())
+ - attotime::from_ticks(14365, m_maincpu->unscaled_clock()));
+ }
+}
+
+void specnext_state::line_irq_adjust()
+{
+ if (m_nr_22_line_interrupt_en) {
+ u16 line = m_nr_64_copper_offset + m_nr_23_line_interrupt;
+ if (line < m_screen->width())
+ m_irq_line_timer->adjust(m_screen->time_until_pos(
+ (SCR_256x192.top() - 1 + line) % m_screen->height(),
+ SCR_256x192.right() + 2));
+ else
+ m_irq_line_timer->reset();
+ }
+}
+
+IRQ_CALLBACK_MEMBER(specnext_state::irq_callback)
+{
+ if (!m_nr_02_bus_reset)
+ {
+ return 0xff;
+ }
+ else
+ {
+ u8 vector = 11;
+ if (m_irq_mask)
+ {
+ vector = 0;
+ u16 i = 1;
+ for (; ~m_irq_mask & i; ++vector, i <<= 1);
+ m_irq_mask &= ~i;
+
+ }
+ return (m_nr_c0_im2_vector << 5) | (vector << 2);
+ }
+}
+
+INPUT_CHANGED_MEMBER(specnext_state::on_mf_nmi)
+{
+ m_nr_02_generate_mf_nmi = newval & 1;
+ do_mf_nmi();
+ m_nr_02_generate_mf_nmi = 0;
+}
+
+INPUT_CHANGED_MEMBER(specnext_state::on_divmmc_nmi)
+{
+ m_nr_02_generate_divmmc_nmi = newval & 1;
+ if (m_nr_06_button_drive_nmi_en && m_nr_02_generate_divmmc_nmi)
+ m_maincpu->nmi();
+}
+
+void specnext_state::do_mf_nmi()
+{
+ if (m_nr_06_button_m1_nmi_en && m_nr_02_generate_mf_nmi)
+ {
+ m_maincpu->nmi();
+ m_mf->button_w(1);
+ m_mf->clock_w();
+ m_mf->button_w(0);
+ }
+}
+
+void specnext_state::leave_nmi(int status)
+{
+ m_mf->cpu_retn_seen_w(1);
+ m_mf->clock_w();
+
+ m_mf->cpu_retn_seen_w(0);
+ m_mf->clock_w();
+ bank_update(0, 2);
+}
+
+u8 specnext_state::do_m1(offs_t offset)
+{
+ u8 bank = offset >> 13;
+ // pre M1
+ m_divmmc->cpu_mreq_n_w(1);
+ m_divmmc->clock_w();
+
+ // M1
+ m_divmmc->cpu_mreq_n_w(0);
+ m_divmmc->cpu_m1_n_w(0);
+ m_divmmc->clock_w();
+ if (bank < 2)
+ bank_update(bank);
+ const u8 data = m_program.read_byte(offset);
+
+ // after M1
+ m_divmmc->automap_instant_on_w(0);
+ m_divmmc->automap_delayed_on_w(0);
+ m_divmmc->automap_delayed_off_w(0);
+ m_divmmc->automap_rom3_instant_on_w(0);
+ m_divmmc->automap_rom3_delayed_on_w(0);
+ m_divmmc->automap_nmi_instant_on_w(0);
+ m_divmmc->automap_nmi_delayed_on_w(0);
+
+ m_divmmc->cpu_m1_n_w(1);
+ m_divmmc->clock_w();
+ bank_update(0, 2);
+
+ m_divmmc_delayed_check = 1;
+ return data;
+}
+
+void specnext_state::map_fetch(address_map &map)
+{
+ map(0x0000, 0xffff).lr8(NAME([this](offs_t offset)
+ {
+ if (m_divmmc_delayed_check && !machine().side_effects_disabled())
+ {
+ /* Happens after RW cycles (before next M1 fetch).
+ Fell like side effects check must be ignored here,
+ because doesn't matter who reset this lines and such
+ approach gives better experience in debugger UI. */
+ do_m1(offset);
+ m_divmmc_delayed_check = 0;
+ }
+
+ return m_program.read_byte(offset);
+ }));
+ map(0x0000, 0x0000).select(0x0038).lr8(NAME([this](offs_t offset)
+ {
+ if (!machine().side_effects_disabled())
+ {
+ const u8 b = offset >> 3;
+ const bool divmmc_rst_ep = BIT(m_nr_b8_divmmc_ep_0, b);
+ const bool divmmc_rst_ep_valid = BIT(m_nr_b9_divmmc_ep_valid_0, b);
+ const bool divmmc_rst_ep_timing = BIT(m_nr_ba_divmmc_ep_timing_0, b);
+
+ m_divmmc->automap_instant_on_w(divmmc_rst_ep && divmmc_rst_ep_valid && divmmc_rst_ep_timing);
+ m_divmmc->automap_delayed_on_w(divmmc_rst_ep && divmmc_rst_ep_valid && !divmmc_rst_ep_timing);
+ m_divmmc->automap_rom3_instant_on_w(divmmc_rst_ep && !divmmc_rst_ep_valid && divmmc_rst_ep_timing);
+ m_divmmc->automap_rom3_delayed_on_w(divmmc_rst_ep && !divmmc_rst_ep_valid && !divmmc_rst_ep_timing);
+
+ return do_m1(offset);
+ }
+ else
+ return m_program.read_byte(offset);
+ }));
+ map(0x1ff8, 0x1fff).lr8(NAME([this](offs_t offset)
+ {
+ if (!machine().side_effects_disabled())
+ {
+ m_divmmc->automap_delayed_off_w(BIT(m_nr_bb_divmmc_ep_1, 6));
+ return do_m1(0x1ff8 + offset);
+ }
+ else
+ return m_program.read_byte(0x1ff8 + offset);
+ }));
+ map(0x3d00, 0x3dff).lr8(NAME([this](offs_t offset)
+ {
+ if (!machine().side_effects_disabled())
+ {
+ m_divmmc->automap_rom3_instant_on_w(BIT(m_nr_bb_divmmc_ep_1, 7));
+ return do_m1(0x3d00 + offset);
+ }
+ else
+ return m_program.read_byte(0x3d00 + offset);
+ }));
+ map(0x04c6, 0x04c6).lr8(NAME([this](offs_t offset)
+ {
+ if (!machine().side_effects_disabled())
+ {
+ m_divmmc->automap_rom3_delayed_on_w(BIT(m_nr_bb_divmmc_ep_1, 2));
+ return do_m1(0x04c6 + offset);
+ }
+ else
+ return m_program.read_byte(0x04c6 + offset);
+ }));
+ map(0x0562, 0x0562).lr8(NAME([this](offs_t offset)
+ {
+ if (!machine().side_effects_disabled())
+ {
+ m_divmmc->automap_rom3_delayed_on_w(BIT(m_nr_bb_divmmc_ep_1, 3));
+ return do_m1(0x0562 + offset);
+ }
+ else
+ return m_program.read_byte(0x0562 + offset);
+ }));
+ map(0x04d7, 0x04d7).lr8(NAME([this](offs_t offset)
+ {
+ if (!machine().side_effects_disabled())
+ {
+ m_divmmc->automap_rom3_delayed_on_w(BIT(m_nr_bb_divmmc_ep_1, 4));
+ return do_m1(0x04d7 + offset);
+ }
+ else
+ return m_program.read_byte(0x04d7 + offset);
+ }));
+ map(0x056a, 0x056a).lr8(NAME([this](offs_t offset)
+ {
+ if (!machine().side_effects_disabled())
+ {
+ m_divmmc->automap_rom3_delayed_on_w(BIT(m_nr_bb_divmmc_ep_1, 5));
+ return do_m1(0x056a + offset);
+ }
+ else
+ return m_program.read_byte(0x056a + offset);
+ }));
+ map(0x0066, 0x0066).lr8(NAME([this](offs_t offset)
+ {
+ if (!machine().side_effects_disabled())
+ {
+ m_mf->cpu_m1_n_w(0);
+ m_mf->cpu_a_0066_w(1);
+ m_mf->cpu_mreq_n_w(0);
+ m_mf->clock_w();
+
+ m_divmmc->automap_nmi_instant_on_w(BIT(m_nr_bb_divmmc_ep_1, 1));
+ m_divmmc->automap_nmi_delayed_on_w(BIT(m_nr_bb_divmmc_ep_1, 0));
+ u8 data = do_m1(0x0066 + offset);
+
+ m_mf->cpu_a_0066_w(0);
+ m_mf->cpu_m1_n_w(1);
+ m_mf->clock_w();
+
+ return data;
+ }
+ else
+ return m_program.read_byte(0x0066 + offset);
+ }));
+}
+
+void specnext_state::map_mem(address_map &map)
+{
+ using views_link = std::reference_wrapper<memory_view>;
+ views_link views[] = { m_view0, m_view1, m_view2, m_view3, m_view4, m_view5, m_view6, m_view7 };
+
+ for (auto i = 0; i < 8; i++)
+ {
+ map(0x0000 + i * 0x2000, 0x1fff + i * 0x2000).bankrw(m_bank_ram[i]);
+ map(0x0000 + i * 0x2000, 0x1fff + i * 0x2000).view(views[i].get());
+ views[i].get()[0](0x0000 + i * 0x2000, 0x1fff + i * 0x2000).nopw();
+ }
+ views[0].get()[1](0x0000, 0x1fff).bankr(m_bank_boot_rom);
+ views[1].get()[1](0x2000, 0x3fff).bankr(m_bank_boot_rom);
+}
+
+void specnext_state::map_io(address_map &map)
+{
+ map.unmap_value_low();
+ map(0x0000, 0xffff).unmaprw();
+
+ map(0x0000, 0x0000).select(0xfffe).rw(FUNC(specnext_state::spectrum_ula_r), FUNC(specnext_state::spectrum_ula_w));
+ map(0x00ff, 0x00ff).mirror(0xff00).rw(FUNC(specnext_state::port_ff_r), FUNC(specnext_state::port_ff_w));
+
+ map(0x001f, 0x001f).select(0xff00).lrw8(NAME([this](offs_t offset)
+ {
+ return mf_port_r(offset | 0x1f);
+ }), NAME([this](offs_t offset, u8 data) {
+ mf_port_w(offset | 0x1f, data);
+ }));
+ map(0x003f, 0x003f).select(0xff00).lrw8(NAME([this](offs_t offset)
+ {
+ return mf_port_r(offset | 0x3f);
+ }), NAME([this](offs_t offset, u8 data) {
+ mf_port_w(offset | 0x3f, data);
+ }));
+ map(0x009f, 0x009f).select(0xff00).lrw8(NAME([this](offs_t offset)
+ {
+ return mf_port_r(offset | 0x8f);
+ }), NAME([this](offs_t offset, u8 data) {
+ mf_port_w(offset | 0x8f, data);
+ }));
+ map(0x00bf, 0x00bf).select(0xff00).lrw8(NAME([this](offs_t offset)
+ {
+ return mf_port_r(offset | 0xbf);
+ }), NAME([this](offs_t offset, u8 data) {
+ mf_port_w(offset | 0xbf, data);
+ }));
+
+ map(0x0001, 0x0001).mirror(0xfff4).lr8(NAME([this]() { // #bff5
+ return m_nr_08_psg_turbosound_en ? m_ay_select : 0;
+ }));
+ map(0xc005, 0xc005).mirror(0x3ff8).lr8(NAME([this]() { // #fffd
+ return m_ay[m_nr_08_psg_turbosound_en ? m_ay_select : 0]->data_r();
+ })).w(FUNC(specnext_state::turbosound_address_w));
+ map(0x8005, 0x8005).mirror(0x3ff8).lw8(NAME([this](u8 data) { // #bffd
+ m_ay[m_nr_08_psg_turbosound_en ? m_ay_select : 0]->data_w(data);
+ }));
+
+ map(0x0001, 0x0001).select(0x7ffc).lw8(NAME([this](offs_t offset, u8 data) {
+ const bool p3_timing_hw_en = (m_nr_03_machine_timing & 3) == 0b11;
+ if (port_7ffd_io_en() && (BIT(offset, 14) || !p3_timing_hw_en) && !port_7ffd_locked())
+ {
+ port_7ffd_reg_w(data);
+ memory_change(0x7ffd, data);
+ }
+ }));
+ map(0xd001, 0xd001).mirror(0x0ffc).lw8(NAME([this](u8 data) {
+ if (port_dffd_io_en() && (!port_7ffd_locked() || nr_8f_mapping_mode_profi()))
+ {
+ m_port_dffd_data = data;
+ memory_change(0xdffd, data);
+ }
+ }));
+ map(0x1001, 0x1001).mirror(0x0ffc).lw8(NAME([this](u8 data) {
+ if (port_1ffd_io_en() && !port_7ffd_locked())
+ {
+ m_port_1ffd_data = data;
+ memory_change(0x1ffd, data);
+ }
+ }));
+ map(0xe0f7, 0xe0f7).mirror(0x0f00).lw8(NAME([this](u8 data) {
+ if(port_eff7_io_en())
+ {
+ m_port_eff7_data = data;
+ memory_change(0xeff7, data);
+ }
+ }));
+
+ map(0x2001, 0x2001).mirror(0x0ffc).lr8(NAME([]() {
+ return /*m_nr_d8_io_trap_fdc_en ? ... :*/ 0x00;
+ }));
+ map(0x3001, 0x3001).mirror(0x0ffc).lrw8(NAME([]() {
+ return /*m_nr_d8_io_trap_fdc_en ? ... :*/ 0x00;
+ }), NAME([this](u8 data) {
+ if (m_nr_d8_io_trap_fdc_en)
+ ;
+ }));
+
+
+ map(0x103b, 0x103b).lr8(NAME([this]() {
+ return port_i2c_io_en() ? (0xfe | m_i2c_scl_data) : 0x00;
+ })).w(FUNC(specnext_state::i2c_scl_w));
+ map(0x113b, 0x113b).lrw8(NAME([this]() {
+ return port_i2c_io_en() ? (0xfe | (m_i2cmem->read_sda() & 1)) : 0x00;
+ }), NAME([this](u8 data) {
+ if(port_i2c_io_en())
+ m_i2cmem->write_sda(data & 1);
+ }));
+ map(0x183b, 0x183b).select(0x0700).lrw8(NAME([this](offs_t offset) {
+ u8 chanel = offset >> 8;
+ return port_ctc_io_en() && (chanel < 4) ? m_ctc->read(chanel) : 0x00;
+ }), NAME([this](offs_t offset, u8 data) {
+ u8 chanel = offset >> 8;
+ if(port_ctc_io_en() && (chanel < 4))
+ m_ctc->write(chanel, data);
+ }));
+ map(0x123b, 0x123b).lrw8(NAME([this]() {
+ return (m_port_123b_layer2_map_segment << 6) | (0b00 << 4) | (m_port_123b_layer2_map_shadow << 3) | (m_port_123b_layer2_map_rd_en << 2) | (m_port_123b_layer2_en << 1) | m_port_123b_layer2_map_wr_en;
+ }), NAME([this](u8 data) {
+ if (BIT(~data, 4))
+ {
+ port_123b_layer2_en_w(BIT(data, 1));
+ m_port_123b_layer2_map_wr_en = BIT(data, 0);
+ m_port_123b_layer2_map_rd_en = BIT(data, 2);
+ m_port_123b_layer2_map_shadow = BIT(data, 3);
+ m_port_123b_layer2_map_segment = BIT(data, 6, 2);
+ }
+ else
+ m_port_123b_layer2_offset = BIT(data, 0, 3);
+
+ bank_update(0, 6);
+ }));
+ map(0x243b, 0x243b).lrw8(NAME([this]() { return m_nr_register; })
+ , NAME([this](u8 data) { m_nr_register = data; }));
+ map(0x253b, 0x253b).lrw8(NAME([this]() { return m_next_regs.read_byte(m_nr_register); })
+ , NAME([this](u8 data) { m_next_regs.write_byte(m_nr_register, data); }));
+ map(0x303b, 0x303b).lrw8(NAME([this]() {
+ return port_sprite_io_en() ? m_sprites->mirror_num_r() : 0x00;
+ }), NAME([this](u8 data) {
+ if (port_sprite_io_en())
+ m_sprites->io_w(0x303b, data);
+ }));
+ map(0xbf3b, 0xbf3b).lw8(NAME([this](u8 data) {
+ if (port_ulap_io_en())
+ {
+ m_port_bf3b_ulap_mode = BIT(data, 6, 2);
+ if (BIT(data, 6, 2) == 0b00)
+ m_port_bf3b_ulap_index = BIT(data, 0, 6);
+ }
+ })); // ULA+ Register
+ map(0xff3b, 0xff3b).lrw8(NAME([this]() {
+ u8 port_ff3b_dat = 0x00;
+ if (port_ulap_io_en())
+ {
+ port_ff3b_dat = m_port_bf3b_ulap_mode == 0b00
+ ? bitswap<8>(nr_palette_dat(), 5, 4, 3, 8, 7, 6, 2, 1)
+ : m_port_ff3b_ulap_en;
+ }
+ return port_ff3b_dat;
+ }), NAME([this](u8 data) {
+ if (port_ulap_io_en())
+ {
+ if (m_port_bf3b_ulap_mode == 0b01)
+ port_ff3b_ulap_en_w(BIT(data, 0));
+ }
+ })); // ULA+ Data
+ map(0x0057, 0x0057).mirror(0xff00).lw8(NAME([this](u8 data) {
+ if (port_sprite_io_en())
+ m_sprites->io_w(0x0057, data);
+ }));
+ map(0x005b, 0x005b).mirror(0xff00).lw8(NAME([this](u8 data) {
+ if (port_sprite_io_en())
+ m_sprites->io_w(0x005b, data);
+ }));
+ map(0x00e7, 0x00e7).mirror(0xff00).w(FUNC(specnext_state::port_e7_reg_w));
+ map(0x00e3, 0x00e3).mirror(0xff00).lrw8(NAME([this]() { return port_divmmc_io_en() ? m_port_e3_reg & ~0x30 : 0x00; })
+ , NAME([this](u8 data) { if (port_divmmc_io_en()) { port_e3_reg_w((m_port_e3_reg & 0x40) | data); }}));
+ map(0x00eb, 0x00eb).mirror(0xff00).rw(FUNC(specnext_state::spi_data_r), FUNC(specnext_state::spi_data_w));
+
+ map(0x000b, 0x000b).mirror(0xff00).lrw8(NAME([this]() { return dma_r(1); }), NAME([this](u8 data) { dma_w(1, data); }));
+ map(0x006b, 0x006b).mirror(0xff00).lrw8(NAME([this]() { return dma_r(0); }), NAME([this](u8 data) { dma_w(0, data); }));
+
+ map(0x0bdf, 0x0bdf).mirror(0xf000).lr8(NAME([this]() -> u8 { return m_io_mouse[0]->read(); })); // #fbdf
+ map(0x0fdf, 0x0fdf).mirror(0xf000).lr8(NAME([this]() -> u8 { return ~m_io_mouse[1]->read(); })); // #ffdf
+ map(0x0adf, 0x0adf).mirror(0xf000).lr8(NAME([this]() -> u8 { return 0x80 | (m_io_mouse[2]->read() & 0x07); })); // #fadf
+
+ // TODO resolve conflicts mf+joy+DAC: 1f, 3f
+ //map(0x001f, 0x001f).mirror(0xff00).lr8(NAME([]() -> u8 { return 0x00; /* Joy1,2*/ })).lw8(NAME([this](u8 data) {
+ // if (m_nr_08_dac_en)
+ // m_dac[0]->data_w(data);
+ //}));
+ map(0x00f1, 0x00f1).mirror(0xff00).lw8(NAME([this](u8 data) {
+ if (m_nr_08_dac_en)
+ m_dac[0]->data_w(data);
+ }));
+ //map(0x003f, 0x003f).mirror(0xff00).lw8(NAME([this](u8 data) {
+ // if (m_nr_08_dac_en)
+ // m_dac[0]->data_w(data);
+ //}));
+ map(0x000f, 0x000f).mirror(0xff00).lw8(NAME([this](u8 data) {
+ if (m_nr_08_dac_en)
+ m_dac[1]->data_w(data);
+ }));
+ map(0x00f3, 0x00f3).mirror(0xff00).lw8(NAME([this](u8 data) {
+ if (m_nr_08_dac_en)
+ m_dac[1]->data_w(data);
+ }));
+ map(0x00df, 0x00df).mirror(0xff00).lw8(NAME([this](u8 data) {
+ if (m_nr_08_dac_en && port_dac_mono_AD_df_io_en())
+ {
+ m_dac[0]->data_w(data);
+ m_dac[3]->data_w(data);
+ }
+ }));
+ map(0x00fb, 0x00fb).mirror(0xff00).lw8(NAME([this](u8 data) {
+ if (m_nr_08_dac_en && port_dac_mono_AD_fb_io_en())
+ {
+ m_dac[0]->data_w(data);
+ m_dac[3]->data_w(data);
+ }
+ }));
+ map(0x00b3, 0x00b3).mirror(0xff00).lw8(NAME([this](u8 data) {
+ if (m_nr_08_dac_en && port_dac_mono_BC_b3_io_en())
+ {
+ m_dac[1]->data_w(data);
+ m_dac[2]->data_w(data);
+ }
+ }));
+ map(0x004f, 0x004f).mirror(0xff00).lw8(NAME([this](u8 data) {
+ if (m_nr_08_dac_en)
+ m_dac[2]->data_w(data);
+ }));
+ map(0x00f9, 0x00f9).mirror(0xff00).lw8(NAME([this](u8 data) {
+ if (m_nr_08_dac_en)
+ m_dac[2]->data_w(data);
+ }));
+ map(0x005f, 0x005f).mirror(0xff00).lw8(NAME([this](u8 data) {
+ if (m_nr_08_dac_en)
+ m_dac[3]->data_w(data);
+ }));
+}
+
+void specnext_state::map_regs(address_map &map)
+{
+ map(0x00, 0xff).rw(FUNC(specnext_state::reg_r), FUNC(specnext_state::reg_w));
+}
+
+INPUT_PORTS_START(specnext)
+ PORT_INCLUDE(spec_plus)
+
+ PORT_START("ISSUE")
+ PORT_CONFNAME(0x03, 0x02, "Hardware Version" )
+ PORT_CONFSETTING(0x00, "Issue 1 (TBBLUE)" )
+ PORT_CONFSETTING(0x01, "Issue 2 (KS 1)" )
+ PORT_CONFSETTING(0x02, "Issue 4 (KS 2)" )
+ PORT_BIT(0xfc, IP_ACTIVE_HIGH, IPT_UNUSED)
+
+ PORT_START("mouse_input1")
+ PORT_BIT(0xff, 0, IPT_MOUSE_X) PORT_SENSITIVITY(40)
+
+ PORT_START("mouse_input2")
+ PORT_BIT(0xff, 0, IPT_MOUSE_Y) PORT_SENSITIVITY(40)
+
+ PORT_START("mouse_input3")
+ PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_BUTTON4) PORT_NAME("Left mouse button") PORT_CODE(MOUSECODE_BUTTON2)
+ PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_BUTTON5) PORT_NAME("Right mouse button") PORT_CODE(MOUSECODE_BUTTON1)
+ PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_BUTTON6) PORT_NAME("Middle mouse button") PORT_CODE(MOUSECODE_BUTTON3)
+
+ PORT_MODIFY("NMI")
+ PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_OTHER) PORT_NAME("NMI MF") PORT_CODE(KEYCODE_F12) PORT_CHANGED_MEMBER(DEVICE_SELF, specnext_state, on_mf_nmi, 0)
+ PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_OTHER) PORT_NAME("NMI DivMMC") PORT_CODE(KEYCODE_F11) PORT_CHANGED_MEMBER(DEVICE_SELF, specnext_state, on_divmmc_nmi, 0)
+INPUT_PORTS_END
+
+void specnext_state::machine_start()
+{
+ spectrum_128_state::machine_start();
+
+ m_irq_line_timer = timer_alloc(FUNC(specnext_state::line_irq_on), this);
+ m_spi_clock = timer_alloc(FUNC(specnext_state::spi_clock), this);
+
+ m_regs_map->space(AS_PROGRAM).specific(m_next_regs);
+ m_maincpu->space(AS_PROGRAM).specific(m_program);
+ m_maincpu->space(AS_IO).specific(m_io);
+
+ for (auto i = 0; i < 8; i++)
+ m_bank_ram[i]->configure_entries(0, m_ram->size() / 0x2000, m_ram->pointer(), 0x2000);
+ m_bank_boot_rom->configure_entry(0, memregion("maincpu")->base());
+
+ const u8 *ram = m_ram->pointer() + 0x40000;
+ m_ula->set_host_ram_ptr(ram);
+ m_tiles->set_host_ram_ptr(ram);
+ m_layer2->set_host_ram_ptr(ram);
+ m_lores->set_host_ram_ptr(ram);
+
+ m_nr_02_hard_reset = 1;
+
+ // Save
+ save_pointer(NAME(m_page_shadow), 8);
+ save_item(NAME(m_bootrom_en));
+ save_item(NAME(m_port_ff_data));
+ save_item(NAME(m_port_1ffd_special_old));
+ save_item(NAME(m_port_1ffd_data));
+ //save_item(NAME(m_port_7ffd_data));
+ save_item(NAME(m_port_dffd_data));
+ save_item(NAME(m_port_eff7_data));
+ save_item(NAME(m_port_e7_reg));
+ save_item(NAME(m_nr_register));
+ save_item(NAME(m_port_e3_reg));
+ save_item(NAME(m_divmmc_delayed_check));
+ save_item(NAME(m_global_transparent));
+ save_item(NAME(m_sram_rom));
+ save_item(NAME(m_sram_rom3));
+ save_item(NAME(m_sram_alt_128_n));
+ save_pointer(NAME(m_mmu), 8);
+ save_item(NAME(m_nr_02_bus_reset));
+ save_item(NAME(m_nr_02_generate_mf_nmi));
+ save_item(NAME(m_nr_02_generate_divmmc_nmi));
+ save_item(NAME(m_nr_02_hard_reset));
+ save_item(NAME(m_nr_02_reset_type));
+ save_item(NAME(m_nr_03_machine_type));
+ save_item(NAME(m_nr_03_user_dt_lock));
+ save_item(NAME(m_nr_03_machine_timing));
+ save_item(NAME(m_nr_03_config_mode));
+ save_item(NAME(m_nr_04_romram_bank));
+ save_item(NAME(m_nr_05_joy1));
+ save_item(NAME(m_nr_05_joy0));
+ save_item(NAME(m_nr_06_psg_mode));
+ save_item(NAME(m_nr_06_ps2_mode));
+ save_item(NAME(m_nr_06_button_m1_nmi_en));
+ save_item(NAME(m_nr_06_button_drive_nmi_en));
+ save_item(NAME(m_nr_06_hotkey_5060_en));
+ save_item(NAME(m_nr_06_internal_speaker_beep));
+ save_item(NAME(m_nr_06_hotkey_cpu_speed_en));
+ save_item(NAME(m_nr_07_cpu_speed));
+ save_item(NAME(m_nr_08_keyboard_issue2));
+ save_item(NAME(m_nr_08_psg_turbosound_en));
+ save_item(NAME(m_nr_08_port_ff_rd_en));
+ save_item(NAME(m_nr_08_dac_en));
+ save_item(NAME(m_nr_08_internal_speaker_en));
+ save_item(NAME(m_nr_08_psg_stereo_mode));
+ save_item(NAME(m_nr_08_contention_disable));
+ save_item(NAME(m_nr_09_hdmi_audio_en));
+ save_item(NAME(m_nr_09_sprite_tie));
+ save_item(NAME(m_nr_09_psg_mono));
+ save_item(NAME(m_nr_0a_mouse_dpi));
+ save_item(NAME(m_nr_0a_mouse_button_reverse));
+ save_item(NAME(m_nr_0a_divmmc_automap_en));
+ save_item(NAME(m_nr_0a_mf_type));
+ save_item(NAME(m_nr_0b_joy_iomode_0));
+ save_item(NAME(m_nr_0b_joy_iomode));
+ save_item(NAME(m_nr_0b_joy_iomode_en));
+ save_item(NAME(m_nr_10_coreid));
+ save_item(NAME(m_nr_11_video_timing));
+ save_item(NAME(m_nr_10_flashboot));
+ save_item(NAME(m_nr_12_layer2_active_bank));
+ save_item(NAME(m_nr_13_layer2_shadow_bank));
+ save_item(NAME(m_nr_14_global_transparent_rgb));
+ save_item(NAME(m_nr_15_sprite_en));
+ save_item(NAME(m_nr_15_sprite_over_border_en));
+ save_item(NAME(m_nr_15_layer_priority));
+ save_item(NAME(m_nr_15_sprite_border_clip_en));
+ save_item(NAME(m_nr_15_sprite_priority));
+ save_item(NAME(m_nr_15_lores_en));
+ save_item(NAME(m_nr_16_layer2_scrollx));
+ save_item(NAME(m_nr_17_layer2_scrolly));
+ save_item(NAME(m_nr_18_layer2_clip_x1));
+ save_item(NAME(m_nr_18_layer2_clip_x2));
+ save_item(NAME(m_nr_18_layer2_clip_y1));
+ save_item(NAME(m_nr_18_layer2_clip_y2));
+ save_item(NAME(m_nr_18_layer2_clip_idx));
+ save_item(NAME(m_nr_19_sprite_clip_x1));
+ save_item(NAME(m_nr_19_sprite_clip_x2));
+ save_item(NAME(m_nr_19_sprite_clip_y1));
+ save_item(NAME(m_nr_19_sprite_clip_y2));
+ save_item(NAME(m_nr_19_sprite_clip_idx));
+ save_item(NAME(m_nr_1a_ula_clip_x1));
+ save_item(NAME(m_nr_1a_ula_clip_x2));
+ save_item(NAME(m_nr_1a_ula_clip_y1));
+ save_item(NAME(m_nr_1a_ula_clip_y2));
+ save_item(NAME(m_nr_1a_ula_clip_idx));
+ save_item(NAME(m_nr_1b_tm_clip_x1));
+ save_item(NAME(m_nr_1b_tm_clip_x2));
+ save_item(NAME(m_nr_1b_tm_clip_y1));
+ save_item(NAME(m_nr_1b_tm_clip_y2));
+ save_item(NAME(m_nr_1b_tm_clip_idx));
+ save_item(NAME(m_nr_22_line_interrupt_en));
+ save_item(NAME(m_nr_23_line_interrupt));
+ save_item(NAME(m_nr_26_ula_scrollx));
+ save_item(NAME(m_nr_27_ula_scrolly));
+ save_item(NAME(m_nr_2d_i2s_sample));
+ save_item(NAME(m_nr_30_tm_scrollx));
+ save_item(NAME(m_nr_31_tm_scrolly));
+ save_item(NAME(m_nr_32_lores_scrollx));
+ save_item(NAME(m_nr_33_lores_scrolly));
+ save_item(NAME(m_nr_palette_idx));
+ save_item(NAME(m_nr_palette_sub_idx));
+ save_item(NAME(m_nr_42_ulanext_format));
+ save_item(NAME(m_nr_43_palette_autoinc_disable));
+ save_item(NAME(m_nr_43_palette_write_select));
+ save_item(NAME(m_nr_43_active_sprite_palette));
+ save_item(NAME(m_nr_43_active_layer2_palette));
+ save_item(NAME(m_nr_43_active_ula_palette));
+ save_item(NAME(m_nr_43_ulanext_en));
+ save_item(NAME(m_nr_stored_palette_value));
+ save_item(NAME(m_nr_4a_fallback_rgb));
+ save_item(NAME(m_nr_4b_sprite_transparent_index));
+ save_item(NAME(m_nr_4c_tm_transparent_index));
+ save_item(NAME(m_nr_copper_addr));
+ save_item(NAME(m_nr_copper_data_stored));
+ save_item(NAME(m_nr_62_copper_mode));
+ save_item(NAME(m_nr_64_copper_offset));
+ save_item(NAME(m_nr_68_ula_en));
+ save_item(NAME(m_nr_68_blend_mode));
+ save_item(NAME(m_nr_68_cancel_extended_keys));
+ save_item(NAME(m_nr_68_ula_fine_scroll_x));
+ save_item(NAME(m_nr_68_ula_stencil_mode));
+ save_item(NAME(m_nr_6a_lores_radastan));
+ save_item(NAME(m_nr_6a_lores_radastan_xor));
+ save_item(NAME(m_nr_6a_lores_palette_offset));
+ save_item(NAME(m_nr_6b_tm_en));
+ save_item(NAME(m_nr_6b_tm_control));
+ save_item(NAME(m_nr_6c_tm_default_attr));
+ save_item(NAME(m_nr_6e_tilemap_base));
+ save_item(NAME(m_nr_6e_tilemap_base_7));
+ save_item(NAME(m_nr_6f_tilemap_tiles));
+ save_item(NAME(m_nr_6f_tilemap_tiles_7));
+ save_item(NAME(m_nr_70_layer2_resolution));
+ save_item(NAME(m_nr_70_layer2_palette_offset));
+ save_item(NAME(m_nr_71_layer2_scrollx_msb));
+ save_item(NAME(m_nr_7f_user_register_0));
+ save_item(NAME(m_nr_80_expbus));
+ save_item(NAME(m_nr_81_expbus_speed));
+ save_item(NAME(m_nr_81_expbus_clken));
+ save_item(NAME(m_nr_81_expbus_nmi_debounce_disable));
+ save_item(NAME(m_nr_81_expbus_ula_override));
+ save_item(NAME(m_nr_82_internal_port_enable));
+ save_item(NAME(m_nr_83_internal_port_enable));
+ save_item(NAME(m_nr_84_internal_port_enable));
+ save_item(NAME(m_nr_85_internal_port_enable));
+ save_item(NAME(m_nr_85_internal_port_reset_type));
+ save_item(NAME(m_nr_86_bus_port_enable));
+ save_item(NAME(m_nr_87_bus_port_enable));
+ save_item(NAME(m_nr_88_bus_port_enable));
+ save_item(NAME(m_nr_89_bus_port_enable));
+ save_item(NAME(m_nr_89_bus_port_reset_type));
+ save_item(NAME(m_nr_8a_bus_port_propagate));
+ save_item(NAME(m_nr_8c_altrom));
+ save_item(NAME(m_nr_8f_mapping_mode));
+ save_item(NAME(m_nr_90_pi_gpio_o_en));
+ save_item(NAME(m_nr_91_pi_gpio_o_en));
+ save_item(NAME(m_nr_92_pi_gpio_o_en));
+ save_item(NAME(m_nr_93_pi_gpio_o_en));
+ save_item(NAME(m_nr_98_pi_gpio_o));
+ save_item(NAME(m_nr_99_pi_gpio_o));
+ save_item(NAME(m_nr_9a_pi_gpio_o));
+ save_item(NAME(m_nr_9b_pi_gpio_o));
+ save_item(NAME(m_nr_a0_pi_peripheral_en));
+ save_item(NAME(m_nr_a2_pi_i2s_ctl));
+ save_item(NAME(m_nr_a8_esp_gpio0_en));
+ save_item(NAME(m_nr_a9_esp_gpio0));
+ save_item(NAME(m_nr_b8_divmmc_ep_0));
+ save_item(NAME(m_nr_b9_divmmc_ep_valid_0));
+ save_item(NAME(m_nr_ba_divmmc_ep_timing_0));
+ save_item(NAME(m_nr_bb_divmmc_ep_1));
+ save_item(NAME(m_nr_c0_im2_vector));
+ save_item(NAME(m_nr_c0_stackless_nmi));
+ save_item(NAME(m_nr_c0_int_mode_pulse_0_im2_1));
+ save_item(NAME(m_nr_c2_retn_address_lsb));
+ save_item(NAME(m_nr_c3_retn_address_msb));
+ save_item(NAME(m_nr_c4_int_en_0_expbus));
+ save_item(NAME(m_nr_c6_int_en_2_654));
+ save_item(NAME(m_nr_c6_int_en_2_210));
+ save_item(NAME(m_nr_cc_dma_int_en_0_7));
+ save_item(NAME(m_nr_cc_dma_int_en_0_10));
+ save_item(NAME(m_nr_cd_dma_int_en_1));
+ save_item(NAME(m_nr_ce_dma_int_en_2_654));
+ save_item(NAME(m_nr_ce_dma_int_en_2_210));
+ save_item(NAME(m_nr_d8_io_trap_fdc_en));
+ save_item(NAME(m_nr_d9_iotrap_write));
+ save_item(NAME(m_nr_da_iotrap_cause));
+ save_item(NAME(m_nr_f0_select));
+ save_item(NAME(m_nr_f0_xdna_en));
+ save_item(NAME(m_nr_f0_xadc_en));
+ save_item(NAME(m_nr_f0_xdev_cmd));
+ save_item(NAME(m_nr_f0_xadc_eoc));
+ save_item(NAME(m_nr_f0_xadc_eos));
+ save_item(NAME(m_nr_f8_xadc_dwe));
+ save_item(NAME(m_nr_f8_xadc_daddr));
+ save_item(NAME(m_nr_f8_xadc_den));
+ save_item(NAME(m_nr_f9_xadc_d0));
+ save_item(NAME(m_nr_fa_xadc_d1));
+ save_item(NAME(m_pulse_int_n));
+ save_item(NAME(m_nr_09_scanlines));
+ save_item(NAME(m_eff_nr_03_machine_timing));
+ save_item(NAME(m_eff_nr_05_5060));
+ save_item(NAME(m_eff_nr_05_scandouble_en));
+ save_item(NAME(m_eff_nr_08_contention_disable));
+ save_item(NAME(m_eff_nr_09_scanlines));
+ save_item(NAME(m_port_123b_layer2_en));
+ save_item(NAME(m_port_123b_layer2_map_wr_en));
+ save_item(NAME(m_port_123b_layer2_map_rd_en));
+ save_item(NAME(m_port_123b_layer2_map_shadow));
+ save_item(NAME(m_port_123b_layer2_map_segment));
+ save_item(NAME(m_port_123b_layer2_offset));
+ save_item(NAME(m_port_bf3b_ulap_mode));
+ save_item(NAME(m_port_bf3b_ulap_index));
+ save_item(NAME(m_port_ff3b_ulap_en));
+ save_item(NAME(m_ay_select));
+ save_item(NAME(m_spi_clock_cycles));
+ save_item(NAME(m_spi_clock_state));
+ save_item(NAME(m_spi_mosi_dat));
+ save_item(NAME(m_spi_miso_dat));
+ save_item(NAME(m_i2c_scl_data));
+ save_item(NAME(m_irq_mask));
+}
+
+void specnext_state::reset_hard()
+{
+ m_nr_02_hard_reset = 0;
+ m_bootrom_en = 1;
+
+ m_dma->dma_mode_w(0);
+ // nmi_mf = 0;
+ // nmi_divmmc = 0;
+ // nmi_expbus = 0;
+ m_nr_80_expbus = 0;
+ // expbus_eff_en = 0;
+ // expbus_eff_disable_io = 0;
+ // expbus_eff_disable_mem = 0;
+ // expbus_eff_clken = 0;
+ m_nr_8c_altrom = 0;
+ // bus_iorq_ula = 0;
+ // port_253b_rd_qq = 0;
+ // sram_req_d = 1;
+ // sram_wait_n = 1;
+ // m_port_103b_dat = 0xff;
+ // m_port_113b_dat = 0xff;
+ port_e7_reg_w(0xff);
+ // joy_iomode_pin7 = 1;
+ // port_fe_border = 0;
+ m_nr_8f_mapping_mode = 0;
+ // m_port_123b_dat = 0;
+ m_nr_02_bus_reset = 0;
+ m_nr_03_machine_timing = 0b011;
+ m_nr_03_user_dt_lock = 0;
+ m_nr_03_config_mode = 1;
+ m_nr_03_machine_type = 0b011;
+ m_nr_04_romram_bank = 0;
+ m_nr_05_joy0 = 0b001;
+ m_nr_05_joy1 = 0b000;
+ m_nr_06_hotkey_cpu_speed_en = 1;
+ m_nr_06_hotkey_5060_en = 1;
+ m_nr_06_button_drive_nmi_en = 0;
+ m_nr_06_button_m1_nmi_en = 0;
+ m_nr_06_ps2_mode = 0;
+ m_nr_06_psg_mode = 0b00;
+ nr_07_cpu_speed_w(0b00);
+ m_nr_06_internal_speaker_beep = 0;
+ m_nr_08_contention_disable = 0;
+ m_nr_08_psg_stereo_mode = 0;
+ m_nr_08_internal_speaker_en = 1;
+ m_nr_08_dac_en = 0;
+ m_nr_08_port_ff_rd_en = 0;
+ m_nr_08_psg_turbosound_en = 0;
+ m_nr_08_keyboard_issue2 = 0;
+ m_nr_09_psg_mono = 0;
+ m_nr_09_hdmi_audio_en = 1;
+ m_nr_09_sprite_tie = 0;
+ nr_0a_mf_type_w(0b00);
+ m_nr_0a_divmmc_automap_en = 0;
+ m_nr_0a_mouse_button_reverse = 0;
+ m_nr_0a_mouse_dpi = 0b01;
+ m_nr_10_flashboot = 0;
+ m_nr_10_coreid = 0b00001;
+ m_nr_7f_user_register_0 = 0xff;
+ m_nr_81_expbus_ula_override = 0;
+ m_nr_81_expbus_nmi_debounce_disable = 0;
+ m_nr_81_expbus_clken = 0;
+ m_nr_81_expbus_speed = 0b00;
+ m_nr_82_internal_port_enable = 0xff;
+ m_nr_83_internal_port_enable = 0xff;
+ m_nr_84_internal_port_enable = 0xff;
+ m_nr_85_internal_port_enable = 0x0f;
+ m_nr_85_internal_port_reset_type = 1;
+ m_nr_86_bus_port_enable = 0xff;
+ m_nr_87_bus_port_enable = 0xff;
+ m_nr_88_bus_port_enable = 0xff;
+ m_nr_89_bus_port_enable = 0x0f;
+ m_nr_89_bus_port_reset_type = 1;
+ m_nr_8a_bus_port_propagate = 0x3f;
+ m_nr_90_pi_gpio_o_en = 0;
+ m_nr_91_pi_gpio_o_en = 0;
+ m_nr_92_pi_gpio_o_en = 0;
+ m_nr_93_pi_gpio_o_en = 0;
+ m_nr_a8_esp_gpio0_en = 0;
+ m_nr_a9_esp_gpio0 = 1;
+ m_nr_f0_select = 1;
+ m_nr_f0_xdna_en = 0;
+ m_nr_f0_xadc_en = 0;
+ m_nr_f0_xdev_cmd = 0x00;
+ m_nr_f0_xadc_eoc = 0;
+ m_nr_f0_xadc_eos = 0;
+ m_nr_f8_xadc_dwe = 0;
+ m_nr_f8_xadc_daddr = 0x00;
+ m_nr_f8_xadc_den = 0;
+ m_nr_f9_xadc_d0 = 0;
+ m_nr_fa_xadc_d1 = 0;
+ //m_nr_05_5060 = 0;
+ //m_nr_05_scandouble_en = 1;
+ m_nr_09_scanlines = 0b00;
+ m_nr_02_reset_type = 0b100;
+ // nr_keymap_sel = 0;
+ // nr_keymap_addr = 0;
+ // hotkeys_1 = 0;
+ // hotkeys_0 = 0;
+ // video_timing_change_d = 0;
+ m_eff_nr_03_machine_timing = 0b011;
+ m_eff_nr_08_contention_disable = 0;
+ // xdna_load = 0;
+ // xdna_shift = 0;
+ // xadc_reset = 0;
+ // xadc_convst = 0;
+ m_eff_nr_05_5060 = 0;
+ m_eff_nr_05_scandouble_en = 0;
+ m_eff_nr_09_scanlines = 0;
+ m_nr_2d_i2s_sample = 0b00;
+
+ // m_port_00_data = 0;
+ m_nr_0a_divmmc_automap_en = 1;
+}
+
+void specnext_state::machine_reset()
+{
+ // TODO prevent from soft reset in config mode?
+ spectrum_128_state::machine_reset();
+
+ m_irq_line_timer->reset();
+
+ if (m_nr_02_hard_reset)
+ reset_hard();
+
+ m_spi_clock->reset();
+ m_spi_clock_cycles = 0;
+ m_spi_clock_state = false;
+
+ m_port_ff_data = 0;
+ m_divmmc_delayed_check = 0;
+
+ m_dma->dma_mode_w(0);
+ //z80_retn_seen_28_d = 0;
+ //im2_dma_delay = 0;
+ m_pulse_int_n = 1;
+ m_nr_c2_retn_address_lsb = 0x00;
+ m_nr_c3_retn_address_msb = 0x00;
+ //z80_stackless_retn_en = 0;
+ //nmi_mf = 0;
+ //nmi_divmmc = 0;
+ //nmi_expbus = 0;
+ //nmi_state = S_NMI_IDLE;
+ m_nr_80_expbus = (m_nr_80_expbus << 4) | (m_nr_80_expbus & 0x0f);
+ m_nr_8c_altrom = (m_nr_8c_altrom << 4) | (m_nr_8c_altrom & 0x0f);
+ //port_253b_rd_qq = 0b00;
+ //sram_req_d = 0;
+ m_i2c_scl_data = 1;
+ port_e7_reg_w(0xff);
+ //joy_iomode_pin7 = 1;
+
+ port_7ffd_reg_w(0x00);
+ m_port_1ffd_data = 0;
+ m_port_1ffd_special_old = 0;
+ m_port_dffd_data = 0;
+ m_port_eff7_data = 0;
+/* TODO don't use inherited
+ port_fe_reg = 0x00;
+ port_ff_reg = 0x00;
+ port_7ffd_reg = 0x00;
+ port_dffd_reg = 0x00;
+ port_dffd_reg_6 = 0;
+ port_1ffd_reg = 0x00;
+ port_1ffd_special_old = 0;
+ port_eff7_reg_2 = 0;
+ port_eff7_reg_3 = 0;
+*/
+ m_nr_02_generate_mf_nmi = 0;
+ m_nr_02_generate_divmmc_nmi = 0;
+ m_nr_da_iotrap_cause = 0x00;
+ m_nr_d9_iotrap_write = 0x00;
+ port_123b_layer2_en_w(0);
+ m_port_123b_layer2_map_wr_en = 0;
+ m_port_123b_layer2_map_rd_en = 0;
+ m_port_123b_layer2_map_shadow = 0;
+ m_port_123b_layer2_map_segment = 0x00;
+ m_port_123b_layer2_offset = 0x00;
+ port_e3_reg_w(0x00);
+ m_port_bf3b_ulap_mode = 0x00;
+ m_port_bf3b_ulap_index = 0x00;
+ port_ff3b_ulap_en_w(0);
+ m_nr_register = 0x24;
+ //copper_requester_d = 0;
+ //copper_req = 0;
+ //copper_nr_reg = 0x00;
+ //copper_nr_dat = 0x00;
+ //cpu_requester_d = 0;
+ //cpu_req = 0;
+ //cpu_nr_reg = 0x00;
+ //cpu_nr_dat = 0x00;
+ nr_07_cpu_speed_w(0b00);
+ m_eff_nr_08_contention_disable = 0;
+
+ //expbus_eff_en = expbus_en;
+ //expbus_eff_disable_io = expbus_disable_io;
+ //expbus_eff_disable_mem = expbus_disable_mem;
+ //expbus_eff_clken = expbus_clken;
+
+
+ m_nr_06_hotkey_cpu_speed_en = 1;
+ m_nr_06_hotkey_5060_en = 1;
+
+ m_nr_08_contention_disable = 0;
+
+ m_nr_09_sprite_tie = 0;
+
+ m_nr_0b_joy_iomode_en = 0;
+ m_nr_0b_joy_iomode = 0b00;
+ m_nr_0b_joy_iomode_0 = 1;
+
+ nr_12_layer2_active_bank_w(0b0001000);
+ nr_13_layer2_shadow_bank_w(0b0001011);
+
+ nr_14_global_transparent_rgb_w(0xe3);
+
+ m_nr_15_lores_en = 0;
+ nr_15_sprite_priority_w(0);
+ nr_15_sprite_border_clip_en_w(0);
+ m_nr_15_layer_priority = 0x00;
+ nr_15_sprite_over_border_en_w(0);
+ m_nr_15_sprite_en = 0;
+
+ nr_16_layer2_scrollx_w(0x00);
+ nr_17_layer2_scrolly_w(0x00);
+ nr_18_layer2_clip_x1_w(0x00);
+ nr_18_layer2_clip_x2_w(0xff);
+ nr_18_layer2_clip_y1_w(0x00);
+ nr_18_layer2_clip_y2_w(0xbf);
+ m_nr_18_layer2_clip_idx = 0b00;
+
+ nr_19_sprite_clip_x1_w(0x00);
+ nr_19_sprite_clip_x2_w(0xff);
+ nr_19_sprite_clip_y1_w(0x00);
+ nr_19_sprite_clip_y2_w(0xbf);
+ m_nr_19_sprite_clip_idx = 0b00;
+
+ nr_1a_ula_clip_x1_w(0x00);
+ nr_1a_ula_clip_x2_w(0xff);
+ nr_1a_ula_clip_y1_w(0x00);
+ nr_1a_ula_clip_y2_w(0xbf);
+ m_nr_1a_ula_clip_idx = 0b00;
+
+ nr_1b_tm_clip_x1_w(0x00);
+ nr_1b_tm_clip_x2_w(0x9f);
+ nr_1b_tm_clip_y1_w(0x00);
+ nr_1b_tm_clip_y2_w(0xff);
+ m_nr_1b_tm_clip_idx = 0b00;
+
+ m_nr_22_line_interrupt_en = 0;
+ m_nr_23_line_interrupt = 0x00;
+ nr_26_ula_scrollx_w(0x00);
+ nr_27_ula_scrolly_w(0x00);
+ nr_30_tm_scrollx_w(0x00);
+ nr_31_tm_scrolly_w(0x00);
+ nr_32_lores_scrollx_w(0x00);
+ nr_33_lores_scrolly_w(0x00);
+
+ m_nr_palette_idx = 0x00;
+ m_nr_palette_sub_idx = 0;
+
+ nr_42_ulanext_format_w(0x07);
+
+ m_nr_43_palette_autoinc_disable = 0;
+ m_nr_43_palette_write_select = 0b000;
+ nr_43_active_sprite_palette_w(0);
+ nr_43_active_layer2_palette_w(0);
+ nr_43_active_ula_palette_w(0);
+ nr_43_ulanext_en_w(0);
+
+ m_nr_stored_palette_value = 0x00;
+
+ reg_w(0x4a, 0xe3); // m_nr_4a_fallback_rgb = 0xe3;
+ nr_4b_sprite_transparent_index_w(0xe3);
+ nr_4c_tm_transparent_index_w(0xf);
+
+ nr_62_copper_mode_w(0b00);
+ m_nr_copper_addr = 0x00;
+ m_nr_copper_data_stored = 0x00;
+
+ m_nr_64_copper_offset = 0x00;
+
+ m_nr_68_ula_en = 1;
+ m_nr_68_blend_mode = 0b00;
+ m_nr_68_cancel_extended_keys = 0;
+ nr_68_ula_fine_scroll_x_w(0);
+ m_nr_68_ula_stencil_mode = 0;
+
+ nr_6a_lores_radastan_w(0);
+ nr_6a_lores_palette_offset_w(0x00);
+ nr_6a_lores_radastan_xor_w(0);
+
+ m_nr_6b_tm_en = 0;
+ nr_6b_tm_control_w(0x00);
+ nr_6c_tm_default_attr_w(0x00);
+ nr_6e_tilemap_base_w(0, 0b101100);
+ nr_6f_tilemap_tiles_w(0, 0b001100);
+
+ nr_70_layer2_resolution_w(0b00);
+ nr_70_layer2_palette_offset_w(0x00);
+
+ nr_71_layer2_scrollx_msb_w(0);
+
+ if (m_nr_85_internal_port_reset_type)
+ {
+ m_nr_82_internal_port_enable = 0xff;
+ m_nr_83_internal_port_enable = 0xff;
+ m_nr_84_internal_port_enable = 0xff;
+ m_nr_85_internal_port_enable = 0xf;
+ }
+
+ if (!m_nr_89_bus_port_reset_type)
+ {
+ m_nr_86_bus_port_enable = 0xff;
+ m_nr_87_bus_port_enable = 0xff;
+ m_nr_88_bus_port_enable = 0xff;
+ m_nr_89_bus_port_enable = 0xf;
+ }
+
+ m_nr_98_pi_gpio_o = 0xff;
+ m_nr_99_pi_gpio_o = 0x01;
+ m_nr_9a_pi_gpio_o = 0x00;
+ m_nr_9b_pi_gpio_o = 0x00;
+
+ m_nr_90_pi_gpio_o_en = 0x00;
+ m_nr_91_pi_gpio_o_en = 0x00;
+ m_nr_92_pi_gpio_o_en = 0x00;
+ m_nr_93_pi_gpio_o_en = 0x00;
+
+ m_nr_a0_pi_peripheral_en = 0x00;
+ m_nr_a2_pi_i2s_ctl = 0x00;
+
+ m_nr_a8_esp_gpio0_en = 0;
+ m_nr_a9_esp_gpio0 = 1;
+
+ m_nr_b8_divmmc_ep_0 = 0x83;
+ m_nr_b9_divmmc_ep_valid_0 = 0x01;
+ m_nr_ba_divmmc_ep_timing_0 = 0x00;
+ m_nr_bb_divmmc_ep_1 = 0xcd;
+
+ nr_c0_im2_vector_w(0x00);
+ m_nr_c0_stackless_nmi = 0;
+ m_maincpu->nmi_stackless_w(m_nr_c0_stackless_nmi);
+ m_nr_c0_int_mode_pulse_0_im2_1 = 0;
+
+ m_nr_c4_int_en_0_expbus = 1;
+
+ m_nr_c6_int_en_2_654 = 0x00;
+ m_nr_c6_int_en_2_210 = 0x00;
+
+ m_nr_cc_dma_int_en_0_7 = 0;
+ m_nr_cc_dma_int_en_0_10 = 0x00;
+ m_nr_cd_dma_int_en_1 = 0x00;
+ m_nr_ce_dma_int_en_2_654 = 0x00;
+ m_nr_ce_dma_int_en_2_210 = 0x00;
+
+ m_nr_d8_io_trap_fdc_en = 0;
+
+ if (m_nr_03_config_mode)
+ m_bootrom_en = 1;
+
+ mmu_x2_w(0, 0xff);
+ mmu_x2_w(2, 0x0a);
+ mmu_x2_w(4, 0x04);
+ mmu_x2_w(6, 0x00);
+
+ m_ay_select = 0;
+ m_irq_mask = 0;
+}
+
+static const gfx_layout bootrom_charlayout =
+{
+ 8, 8, // 8 x 8 characters */
+ 96, // 96 characters */
+ 1, // 1 bits per pixel */
+ { 0 }, // no bitplanes
+ { STEP8(0, 1) }, // x offsets
+ { STEP8(0, 8) }, // y offsets
+ 8 * 8 // every char takes 8 bytes
+};
+
+static GFXDECODE_START(gfx_tbblue)
+ GFXDECODE_ENTRY("maincpu", 0x1584, bootrom_charlayout, 0xf7, 1)
+GFXDECODE_END
+
+void specnext_state::video_start()
+{
+ spectrum_128_state::video_start();
+ m_contention_pattern = {}; // No contention for now
+
+ address_space &prg = m_maincpu->space(AS_PROGRAM);
+ prg.install_write_tap(0x0000, 0xbfff, "shadow_w", [this](offs_t offset, u8 &data, u8 mem_mask)
+ {
+ u8 bank8 = offset >> 13;
+ if (~m_page_shadow[bank8])
+ {
+ u8 *to = m_ram->pointer() + (m_page_shadow[bank8] << 13);
+ to[offset & 0x1fff] = data;
+ }
+ });
+}
+
+void specnext_state::tbblue(machine_config &config)
+{
+ spectrum_128(config);
+ config.device_remove("exp");
+ // m_ram->set_default_size("1M").set_extra_options("2M");
+ m_ram->set_default_size("2M").set_default_value(0);
+
+ Z80N(config.replace(), m_maincpu, 28_MHz_XTAL / 8);
+ m_maincpu->set_daisy_config(z80_daisy_chain);
+ m_maincpu->set_m1_map(&specnext_state::map_fetch);
+ m_maincpu->set_memory_map(&specnext_state::map_mem);
+ m_maincpu->set_io_map(&specnext_state::map_io);
+ m_maincpu->set_vblank_int("screen", FUNC(specnext_state::specnext_interrupt));
+ m_maincpu->set_irq_acknowledge_callback(FUNC(specnext_state::irq_callback));
+ m_maincpu->out_nextreg_cb().set(FUNC(specnext_state::reg_w));
+ m_maincpu->in_nextreg_cb().set(FUNC(specnext_state::reg_r));
+ m_maincpu->out_retn_seen_cb().set(FUNC(specnext_state::leave_nmi));
+ m_maincpu->nomreq_cb().set_nop();
+
+ SPECNEXT_CTC(config, m_ctc, 28_MHz_XTAL / 8);
+ m_ctc->intr_callback().set_inputline(m_maincpu, INPUT_LINE_IRQ0);
+
+ SPECNEXT_DMA(config, m_dma, 28_MHz_XTAL / 8);
+ m_dma->out_busreq_callback().set_inputline(m_maincpu, Z80_INPUT_LINE_BUSRQ);
+ m_dma->in_mreq_callback().set([this](offs_t offset) { return m_program.read_byte(offset); });
+ m_dma->out_mreq_callback().set([this](offs_t offset, u8 data) { m_program.write_byte(offset, data); });
+ m_dma->in_iorq_callback().set([this](offs_t offset) { return m_io.read_byte(offset); });
+ m_dma->out_iorq_callback().set([this](offs_t offset, u8 data) { m_io.write_byte(offset, data); });
+
+ ADDRESS_MAP_BANK(config, m_regs_map).set_map(&specnext_state::map_regs).set_options(ENDIANNESS_LITTLE, 8, 8, 0);
+
+ I2C_24C01(config, m_i2cmem).set_address(0xd0); // RTC + DEFAULT_ALL_0; confitm size
+
+ SPI_SDCARD(config, m_sdcard, 0);
+ m_sdcard->spi_miso_callback().set(FUNC(specnext_state::spi_miso_w));
+
+ SPEAKER(config, "lspeaker").front_left();
+ SPEAKER(config, "rspeaker").front_right();
+
+ DAC_8BIT_R2R(config, m_dac[0], 0).add_route(ALL_OUTPUTS, "lspeaker", 0.75);
+ DAC_8BIT_R2R(config, m_dac[1], 0).add_route(ALL_OUTPUTS, "lspeaker", 0.75);
+ DAC_8BIT_R2R(config, m_dac[2], 0).add_route(ALL_OUTPUTS, "rspeaker", 0.75);
+ DAC_8BIT_R2R(config, m_dac[3], 0).add_route(ALL_OUTPUTS, "rspeaker", 0.75);
+
+ config.device_remove("ay8912");
+ for (auto i = 0; i < 3; ++i)
+ {
+ YM2149(config, m_ay[i], 14_MHz_XTAL / 8)
+ .add_route(0, "lspeaker", 0.50)
+ .add_route(1, "lspeaker", 0.25)
+ .add_route(1, "rspeaker", 0.25)
+ .add_route(2, "rspeaker", 0.50);
+ }
+
+ SPECNEXT_MULTIFACE(config, m_mf, 0);
+ SPECNEXT_DIVMMC(config, m_divmmc, 0);
+
+ zxbus_device &zxbus(ZXBUS(config, "zxbus", 0));
+ zxbus.set_iospace("maincpu", AS_IO);
+ ZXBUS_SLOT(config, "zxbus:1", 0, "zxbus", zxbus_cards, nullptr);
+
+ const rectangle scr_full = { SCR_320x256.left() - 16, SCR_320x256.right() + 16, SCR_320x256.top() - 8, SCR_320x256.bottom() + 8 };
+ m_screen->set_raw(28_MHz_XTAL / 2, CYCLES_HORIZ, CYCLES_VERT, scr_full);
+ m_screen->set_screen_update(FUNC(specnext_state::screen_update));
+ m_screen->set_no_palette();
+
+ PALETTE(config.replace(), m_palette, palette_device::BLACK, 512 * 4 + 1); // ulatm, l2s, +1 == fallback
+ subdevice<gfxdecode_device>("gfxdecode")->set_info(gfx_tbblue);
+
+ const u16 left = SCR_256x192.left();
+ const u16 top = SCR_256x192.top();
+ SCREEN_ULA_NEXT (config, m_ula, 0).set_raster_offset(left, top).set_palette(m_palette->device().tag(), 0x000, 0x100);
+ SPECNEXT_LORES (config, m_lores, 0).set_raster_offset(left, top).set_palette(m_palette->device().tag(), 0x000, 0x100);
+ SPECNEXT_TILES (config, m_tiles, 0).set_raster_offset(left, top).set_palette(m_palette->device().tag(), 0x200, 0x300);
+ SPECNEXT_LAYER2 (config, m_layer2, 0).set_raster_offset(left, top).set_palette(m_palette->device().tag(), 0x400, 0x500);
+ SPECNEXT_SPRITES(config, m_sprites, 0).set_raster_offset(left, top).set_palette(m_palette->device().tag(), 0x600, 0x700);
+
+ SPECNEXT_COPPER(config, m_copper, 28_MHz_XTAL);
+ m_copper->out_nextreg_cb().set(FUNC(specnext_state::reg_w));
+ m_copper->set_in_until_pos_cb(FUNC(specnext_state::cooper_until_pos_r));
+
+ config.device_remove("snapshot");
+}
+
+ROM_START(tbblue)
+ ROM_REGION(0x4000, "maincpu", ROMREGION_ERASEFF)
+ ROM_DEFAULT_BIOS("boot")
+
+ ROM_SYSTEM_BIOS(0, "boot", "BootROM")
+ ROMX_LOAD( "bootrom.fa55357d.bin", 0x0000, 0x2000, CRC(ccbd55ba) SHA1(8b3c2a301f486904d1c74929b94845a7731bf230), ROM_BIOS(0))
+
+ ROM_SYSTEM_BIOS(1, "bootab", "BootROM - AntiBrick")
+ ROMX_LOAD( "bootrom-ab.cfffa702.bin", 0x0000, 0x2000, CRC(1d16e9d4) SHA1(6f9c8771e5a9ef5a6b52a31b2e65f0698f0f5cfa), ROM_BIOS(1))
+ROM_END
+
+} // Anonymous namespace
+
+/* YEAR NAME PARENT COMPAT MACHINE INPUT CLASS INIT COMPANY FULLNAME FLAGS */
+COMP( 2017, tbblue, spec128, 0, tbblue, specnext, specnext_state, empty_init, "SpecNext Ltd., Victor Trucco, Fabio Belavenuto", "ZX Spectrum Next: TBBlue", MACHINE_IMPERFECT_GRAPHICS | MACHINE_SUPPORTS_SAVE )
diff --git a/src/mame/sinclair/specnext_copper.cpp b/src/mame/sinclair/specnext_copper.cpp
index acbdc0feb4d..87699f2c2b1 100644
--- a/src/mame/sinclair/specnext_copper.cpp
+++ b/src/mame/sinclair/specnext_copper.cpp
@@ -1,4 +1,5 @@
// license:BSD-3-Clause
+// copyright-holders:Andrei I. Holub
/**********************************************************************
Spectrum Next Copper
Refs:
diff --git a/src/mame/sinclair/specnext_copper.h b/src/mame/sinclair/specnext_copper.h
index f62ee548789..75c175fd0df 100644
--- a/src/mame/sinclair/specnext_copper.h
+++ b/src/mame/sinclair/specnext_copper.h
@@ -23,8 +23,8 @@ public:
void copper_en_w(u8 data);
protected:
- virtual void device_start() override;
- virtual void device_reset() override;
+ virtual void device_start() override ATTR_COLD;
+ virtual void device_reset() override ATTR_COLD;
TIMER_CALLBACK_MEMBER(timer_callback);
TIMER_CALLBACK_MEMBER(frame_timer_callback);
diff --git a/src/mame/sinclair/specnext_ctc.h b/src/mame/sinclair/specnext_ctc.h
index 27cdb0399ff..fa8f03f347f 100644
--- a/src/mame/sinclair/specnext_ctc.h
+++ b/src/mame/sinclair/specnext_ctc.h
@@ -19,5 +19,4 @@ protected:
};
DECLARE_DEVICE_TYPE(SPECNEXT_CTC, specnext_ctc_device)
-
#endif // MAME_SINCLAIR_SPECNEXT_CTC_H
diff --git a/src/mame/sinclair/specnext_dma.h b/src/mame/sinclair/specnext_dma.h
index c1c9891e402..0be5a20533b 100644
--- a/src/mame/sinclair/specnext_dma.h
+++ b/src/mame/sinclair/specnext_dma.h
@@ -17,8 +17,8 @@ public:
virtual void write(u8 data) override;
protected:
- virtual void device_start() override;
- virtual void device_reset() override;
+ virtual void device_start() override ATTR_COLD;
+ virtual void device_reset() override ATTR_COLD;
virtual int is_ready() override;
virtual void do_write() override;
diff --git a/src/mame/sinclair/specnext_layer2.cpp b/src/mame/sinclair/specnext_layer2.cpp
new file mode 100644
index 00000000000..3796130e646
--- /dev/null
+++ b/src/mame/sinclair/specnext_layer2.cpp
@@ -0,0 +1,183 @@
+// license:BSD-3-Clause
+// copyright-holders:Andrei I. Holub
+/**********************************************************************
+ Spectrum Next Layer2
+**********************************************************************/
+
+#include "emu.h"
+#include "specnext_layer2.h"
+
+#include "screen.h"
+
+specnext_layer2_device::specnext_layer2_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock)
+ : device_t(mconfig, SPECNEXT_LAYER2, tag, owner, clock)
+ , device_gfx_interface(mconfig, *this)
+{
+}
+
+specnext_layer2_device &specnext_layer2_device::set_palette(const char *tag, u16 base_offset, u16 alt_offset)
+{
+ device_gfx_interface::set_palette(tag);
+ m_palette_base_offset = base_offset,
+ m_palette_alt_offset = alt_offset;
+
+ return *this;
+}
+
+static const u16 layer2_info[3][5] =
+{
+ //width height border x-inc y-inc
+ { 256, 192, 0, 1, 256 },
+ { 320, 256, 32, 256, 1 },
+};
+
+static rgb_t blend(u32 &target, const rgb_t pen, u8 mixer)
+{
+ switch (mixer)
+ {
+ case 0:
+ target = pen;
+ break;
+ case 1:
+ target = rgb_t(target) + pen;
+ break;
+ default:
+ {
+ const u8 five = pal3bit(5);
+ const rgb_t t = rgb_t(target);
+ target = rgb_t(0, rgb_t::clamp(t.r() + pen.r() - five)
+ , rgb_t::clamp(t.g() + pen.g() - five)
+ , rgb_t::clamp(t.b() + pen.b() - five));
+ }
+ }
+
+ return target;
+}
+
+void specnext_layer2_device::draw_mix(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect, u8 mixer)
+{
+ draw(screen, bitmap, cliprect, 0, 1, mixer + 1);
+}
+
+void specnext_layer2_device::draw(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect, u8 pcode, u8 priority_mask, u8 mixer)
+{
+ if (!m_layer2_en)
+ return;
+
+ const u16 (&info)[5] = layer2_info[std::min<u8>(m_resolution, 1)];
+ const u8 scale = (m_resolution >= 2) ? 0 : 1; // hires
+
+ u16 offset_h = m_offset_h - (info[2] << 1);
+ u16 offset_v = m_offset_v - info[2];
+ rectangle clip = rectangle{ m_clip_x1 << 1, (std::min<u16>(m_clip_x2, info[0] - 1) << 1) | 1, m_clip_y1, std::min<u8>(m_clip_y2, info[1] - 1) };
+ if (info[2])
+ clip.setx(clip.left() << 1, (clip.right() << 1) | 1);
+
+ clip.offset(offset_h, offset_v);
+ clip &= cliprect;
+ //clip.setx(clip.left() + (clip.left() & 1), clip.right() | 1);
+ clip.setx(clip.left() & ~1, clip.right() | 1);
+
+ if (clip.empty())
+ return;
+
+ const rgb_t gt0 = rgbexpand<3,3,3>((m_global_transparent << 1) | 0, 6, 3, 0);
+ const rgb_t gt1 = rgbexpand<3,3,3>((m_global_transparent << 1) | 1, 6, 3, 0);
+ const u16 pen_base = (m_layer2_palette_select ? m_palette_alt_offset : m_palette_base_offset) | (m_palette_offset << 4);
+ const u16 x_min = (((clip.left() - offset_h) >> 1) + info[0] + m_scroll_x) % info[0];
+ for (u16 vpos = clip.top(); vpos <= clip.bottom(); vpos++)
+ {
+ const u16 y = (vpos - offset_v + info[1] + m_scroll_y) % info[1];
+ u16 x = x_min ;
+ const u8 *scr = m_host_ram_ptr + (m_layer2_active_bank << 14) + (y * info[4]) + (x * info[3]);
+ u32 *pix = &(bitmap.pix(vpos, clip.left()));
+ u8 *prio = &(screen.priority().pix(vpos, clip.left()));
+ for (u16 hpos = clip.left(); hpos <= clip.right(); hpos += 2, pix += 2, prio += 2)
+ {
+ if (scale)
+ {
+ const u16 idx = pen_base + (*scr);
+ const rgb_t pen = palette().pen_color(idx);
+ if ((pen != gt0) && (pen != gt1))
+ {
+ const bool prio_color = m_pen_priority[idx];
+ if (prio_color || mixer || ((*(prio) & priority_mask) == 0))
+ {
+ blend(*pix, pen, (*(prio) == priority_mask) ? mixer : 0);
+ *(prio) |= prio_color ? 8 : pcode;
+ }
+ if (prio_color || mixer || ((*(prio + 1) & priority_mask) == 0))
+ {
+ blend(*(pix + 1), pen, (*(prio + 1) == priority_mask) ? mixer : 0);
+ *(prio + 1) |= prio_color ? 8 : pcode;
+ }
+ }
+ }
+ else
+ {
+ u16 idx = pen_base + (*scr >> 4);
+ rgb_t pen = palette().pen_color(idx);
+ if ((pen != gt0) && (pen != gt1))
+ {
+ const bool prio_color = m_pen_priority[idx];
+ if (prio_color || mixer || ((*(prio) & priority_mask) == 0))
+ {
+ blend(*pix, pen, (*(prio) == priority_mask) ? mixer : 0);
+ *(prio) |= prio_color ? 8 : pcode;
+ }
+ }
+
+ idx = pen_base + (*scr & 0x0f);
+ pen = palette().pen_color(idx);
+ if ((pen != gt0) && (pen != gt1))
+ {
+ const bool prio_color = m_pen_priority[idx];
+ if (prio_color || mixer || ((*(prio + 1) & priority_mask) == 0))
+ {
+ blend(*(pix + 1), pen, (*(prio + 1) == priority_mask) ? mixer : 0);
+ *(prio + 1) |= prio_color ? 8 : pcode;
+ }
+ }
+ }
+
+ ++x %= info[0];
+ if (x == 0)
+ scr = m_host_ram_ptr + (m_layer2_active_bank << 14) + (y * info[4]);
+ else
+ scr += info[3];
+ }
+ }
+}
+
+void specnext_layer2_device::device_add_mconfig(machine_config &config)
+{
+ m_offset_h = 0;
+ m_offset_v = 0;
+}
+
+void specnext_layer2_device::device_start()
+{
+ save_item(NAME(m_global_transparent));
+ save_item(NAME(m_layer2_palette_select));
+ save_pointer(NAME(m_pen_priority), 512 * 4);
+
+ save_item(NAME(m_layer2_en));
+ save_item(NAME(m_resolution));
+ save_item(NAME(m_palette_offset));
+ save_item(NAME(m_layer2_active_bank));
+
+ save_item(NAME(m_scroll_x));
+ save_item(NAME(m_scroll_y));
+ save_item(NAME(m_clip_x1));
+ save_item(NAME(m_clip_x2));
+ save_item(NAME(m_clip_y1));
+ save_item(NAME(m_clip_y2));
+}
+
+void specnext_layer2_device::device_reset()
+{
+ memset(m_pen_priority, 0, 512 * 4);
+}
+
+// device type definition
+DEFINE_DEVICE_TYPE(SPECNEXT_LAYER2, specnext_layer2_device, "layer2", "Spectrum Next Layer2")
diff --git a/src/mame/sinclair/specnext_layer2.h b/src/mame/sinclair/specnext_layer2.h
new file mode 100644
index 00000000000..b53a9da5139
--- /dev/null
+++ b/src/mame/sinclair/specnext_layer2.h
@@ -0,0 +1,66 @@
+// license:BSD-3-Clause
+// copyright-holders:Andrei I. Holub
+#ifndef MAME_SINCLAIR_SPECNEXT_LAYER2_H
+#define MAME_SINCLAIR_SPECNEXT_LAYER2_H
+
+#pragma once
+
+class specnext_layer2_device : public device_t, public device_gfx_interface
+{
+
+public:
+ specnext_layer2_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock);
+
+ specnext_layer2_device &set_raster_offset(u16 offset_h, u16 offset_v) { m_offset_h = offset_h; m_offset_v = offset_v; return *this; }
+ specnext_layer2_device &set_host_ram_ptr(const u8 *host_ram_ptr) { m_host_ram_ptr = host_ram_ptr; return *this; }
+ specnext_layer2_device &set_palette(const char *tag, u16 base_offset, u16 alt_offset);
+
+ void set_global_transparent(u8 global_transparent) { m_global_transparent = global_transparent; }
+ void layer2_palette_select_w(bool layer2_palette_select) { m_layer2_palette_select = layer2_palette_select; }
+ void pen_priority_w(u16 pen, bool priority) { m_pen_priority[pen] = priority; }
+
+ void layer2_en_w(bool layer2_en) { m_layer2_en = layer2_en; }
+ void resolution_w(u8 resolution) { m_resolution = resolution & 0x03; }
+ void palette_offset_w(u8 palette_offset) { m_palette_offset = palette_offset & 0x0f; }
+ void layer2_active_bank_w(u8 layer2_active_bank) { m_layer2_active_bank = layer2_active_bank & 0x7f; }
+
+ void scroll_x_w(u16 scroll_x) { m_scroll_x = scroll_x & 0x1ff; }
+ void scroll_y_w(u8 scroll_y) { m_scroll_y = scroll_y; }
+ void clip_x1_w(u8 clip_x1) { m_clip_x1 = clip_x1; }
+ void clip_x2_w(u8 clip_x2) { m_clip_x2 = clip_x2; }
+ void clip_y1_w(u8 clip_y1) { m_clip_y1 = clip_y1; }
+ void clip_y2_w(u8 clip_y2) { m_clip_y2 = clip_y2; }
+
+ void draw(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect, u8 pcode = 0, u8 priority_mask = 0, u8 mixer = 0);
+ void draw_mix(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect, u8 mixer);
+
+protected:
+ virtual void device_add_mconfig(machine_config &config) override ATTR_COLD;
+ virtual void device_start() override ATTR_COLD;
+ virtual void device_reset() override ATTR_COLD;
+
+private:
+ u16 m_offset_h, m_offset_v;
+ const u8 *m_host_ram_ptr;
+ u8 m_global_transparent;
+ u16 m_palette_base_offset;
+ u16 m_palette_alt_offset;
+ bool m_layer2_palette_select;
+ bool m_pen_priority[512 * 4];
+
+ bool m_layer2_en;
+ u8 m_resolution; // u2: 00 = 256x192, 01 = 320x256, 1X = 640x256x4
+ u8 m_palette_offset; // u4
+ u8 m_layer2_active_bank; // u7
+
+ u16 m_scroll_x; // u9
+ u8 m_scroll_y;
+ u8 m_clip_x1;
+ u8 m_clip_x2;
+ u8 m_clip_y1;
+ u8 m_clip_y2;
+};
+
+
+DECLARE_DEVICE_TYPE(SPECNEXT_LAYER2, specnext_layer2_device)
+#endif // MAME_SINCLAIR_SPECNEXT_LAYER2_H
diff --git a/src/mame/sinclair/specnext_lores.cpp b/src/mame/sinclair/specnext_lores.cpp
new file mode 100644
index 00000000000..4ae4467703d
--- /dev/null
+++ b/src/mame/sinclair/specnext_lores.cpp
@@ -0,0 +1,111 @@
+// license:BSD-3-Clause
+// copyright-holders:Andrei I. Holub
+/**********************************************************************
+ Spectrum Next Lores (Radastan)
+
+ LoRes video mode
+ 128 x 96, 8-bit colour
+ $4000-$57FF for top half
+ $6000-$77FF for bottom half
+
+ LoRes radastan (original mode from zx uno <http://zxuno.speccy.org/>)
+ 128 x 96, 4-bit colour
+ $4000 - $57ff if timex dfile 0 active
+ $6000 - $77ff if timex dfile 1 active
+
+**********************************************************************/
+
+#include "emu.h"
+#include "specnext_lores.h"
+
+#include "screen.h"
+
+specnext_lores_device::specnext_lores_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock)
+ : device_t(mconfig, SPECNEXT_LORES, tag, owner, clock)
+ , device_gfx_interface(mconfig, *this)
+{
+}
+
+specnext_lores_device &specnext_lores_device::set_palette(const char *tag, u16 base_offset, u16 alt_offset)
+{
+ device_gfx_interface::set_palette(tag);
+ m_palette_base_offset = base_offset,
+ m_palette_alt_offset = alt_offset;
+
+ return *this;
+}
+
+void specnext_lores_device::draw(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect, u8 pcode)
+{
+ rectangle clip = { m_clip_x1 << 1, (m_clip_x2 << 1) | 1, m_clip_y1, m_clip_y2 };
+ clip.offset(m_offset_h, m_offset_v);
+ clip &= cliprect;
+ clip.setx(clip.left() & ~3, clip.right() | 3);
+
+ if (clip.empty())
+ return;
+
+ const rgb_t gt0 = rgbexpand<3,3,3>((m_global_transparent << 1) | 0, 6, 3, 0);
+ const rgb_t gt1 = rgbexpand<3,3,3>((m_global_transparent << 1) | 1, 6, 3, 0);
+ const u16 pen_base = (m_lores_palette_select ? m_palette_alt_offset : m_palette_base_offset) | (m_lores_palette_offset << 4);
+ const u8 *screen_location = m_host_ram_ptr + (5 << 14);
+
+ const u16 x_min = ((((clip.left() - m_offset_h) >> 1) + m_scroll_x) % SCREEN_AREA.width()) >> 1;
+ for (u16 vpos = clip.top(); vpos <= clip.bottom(); ++vpos)
+ {
+ const u16 y = ((vpos - m_offset_v + m_scroll_y) % SCREEN_AREA.height()) >> 1;
+ u16 x = x_min ;
+ const u8 *scr = &screen_location[x + ((y < 48) ? 128 * y : (128 * (y - 48) + 0x2000))];
+
+ u32 *pix = &(bitmap.pix(vpos, clip.left()));
+ u8 *prio = &(screen.priority().pix(vpos, clip.left()));
+ for (u16 hpos = clip.left(); hpos <= clip.right(); hpos += 4, pix += 4, prio += 4)
+ {
+ const rgb_t pen = palette().pen_color(pen_base + *scr);
+ if ((pen != gt0) && (pen != gt1))
+ {
+ *(pix + 0) = pen;
+ *(pix + 1) = pen;
+ *(pix + 2) = pen;
+ *(pix + 3) = pen;
+ *(prio + 0) |= pcode;
+ *(prio + 1) |= pcode;
+ *(prio + 2) |= pcode;
+ *(prio + 3) |= pcode;
+ }
+
+ ++x %= SCREEN_AREA.width() >> 1;
+ if (x == 0)
+ scr = &screen_location[(y < 48) ? 128 * y : (128 * (y - 48) + 0x2000)];
+ else
+ ++scr;
+ }
+ }
+}
+
+void specnext_lores_device::device_add_mconfig(machine_config &config)
+{
+ m_offset_h = 0;
+ m_offset_v = 0;
+}
+
+void specnext_lores_device::device_start()
+{
+ save_item(NAME(m_global_transparent));
+ save_item(NAME(m_lores_palette_select));
+
+ save_item(NAME(m_mode));
+ save_item(NAME(m_dfile));
+ save_item(NAME(m_ulap_en));
+
+ save_item(NAME(m_lores_palette_offset));
+ save_item(NAME(m_clip_x1));
+ save_item(NAME(m_clip_x2));
+ save_item(NAME(m_clip_y1));
+ save_item(NAME(m_clip_y2));
+ save_item(NAME(m_scroll_x));
+ save_item(NAME(m_scroll_y));
+}
+
+// device type definition
+DEFINE_DEVICE_TYPE(SPECNEXT_LORES, specnext_lores_device, "lores", "Spectrum Next Lores")
diff --git a/src/mame/sinclair/specnext_lores.h b/src/mame/sinclair/specnext_lores.h
new file mode 100644
index 00000000000..8fe3999537e
--- /dev/null
+++ b/src/mame/sinclair/specnext_lores.h
@@ -0,0 +1,65 @@
+// license:BSD-3-Clause
+// copyright-holders:Andrei I. Holub
+#ifndef MAME_SINCLAIR_SPECNEXT_LORES_H
+#define MAME_SINCLAIR_SPECNEXT_LORES_H
+
+#pragma once
+
+class specnext_lores_device : public device_t, public device_gfx_interface
+{
+
+public:
+ specnext_lores_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock);
+
+ specnext_lores_device &set_raster_offset(u16 offset_h, u16 offset_v) { m_offset_h = offset_h; m_offset_v = offset_v; return *this; }
+ specnext_lores_device &set_host_ram_ptr(const u8 *host_ram_ptr) { m_host_ram_ptr = host_ram_ptr; return *this; }
+ specnext_lores_device &set_palette(const char *tag, u16 base_offset, u16 alt_offset);
+
+ void set_global_transparent(u8 global_transparent) { m_global_transparent = global_transparent; }
+ void lores_palette_select_w(bool lores_palette_select) { m_lores_palette_select = lores_palette_select; }
+
+ void mode_w(bool mode) { m_mode = mode; } // 0 = lores, 1 = radastan
+ void dfile_w(bool dfile) { m_dfile = dfile; } // timex display file to use for radastan
+ void ulap_en_w(bool ulap_en) { m_ulap_en = ulap_en; } // translate radastan pixel to ula+ palette
+
+ void lores_palette_offset_w(u8 lores_palette_offset) { m_lores_palette_offset = lores_palette_offset & 0x0f; }
+ void scroll_x_w(u8 scroll_x) { m_scroll_x = scroll_x; }
+ void scroll_y_w(u8 scroll_y) { m_scroll_y = scroll_y; }
+ void clip_x1_w(u8 clip_x1) { m_clip_x1 = clip_x1; }
+ void clip_x2_w(u8 clip_x2) { m_clip_x2 = clip_x2; }
+ void clip_y1_w(u8 clip_y1) { m_clip_y1 = clip_y1; }
+ void clip_y2_w(u8 clip_y2) { m_clip_y2 = clip_y2; }
+
+ void draw(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect, u8 pcode = 0);
+
+protected:
+ static constexpr rectangle SCREEN_AREA = { 0, 255, 0, 191 };
+
+ virtual void device_add_mconfig(machine_config &config) override ATTR_COLD;
+ virtual void device_start() override ATTR_COLD;
+
+private:
+ u16 m_offset_h, m_offset_v;
+ const u8 *m_host_ram_ptr;
+ u8 m_global_transparent;
+ u16 m_palette_base_offset;
+ u16 m_palette_alt_offset;
+ bool m_lores_palette_select;
+
+ bool m_mode;
+ bool m_dfile;
+ bool m_ulap_en;
+
+ u8 m_lores_palette_offset; // u4
+ u8 m_clip_x1;
+ u8 m_clip_x2;
+ u8 m_clip_y1;
+ u8 m_clip_y2;
+ u8 m_scroll_x;
+ u8 m_scroll_y;
+
+};
+
+
+DECLARE_DEVICE_TYPE(SPECNEXT_LORES, specnext_lores_device)
+#endif // MAME_SINCLAIR_SPECNEXT_LORES_H
diff --git a/src/mame/sinclair/specnext_multiface.h b/src/mame/sinclair/specnext_multiface.h
index f01d117b4e1..31a1a0daa6d 100644
--- a/src/mame/sinclair/specnext_multiface.h
+++ b/src/mame/sinclair/specnext_multiface.h
@@ -33,8 +33,8 @@ public:
void clock_w(); // called on active clock edge
protected:
- virtual void device_start() override;
- virtual void device_reset() override;
+ virtual void device_start() override ATTR_COLD;
+ virtual void device_reset() override ATTR_COLD;
private:
// in
diff --git a/src/mame/sinclair/specnext_sprites.cpp b/src/mame/sinclair/specnext_sprites.cpp
new file mode 100644
index 00000000000..fc646919429
--- /dev/null
+++ b/src/mame/sinclair/specnext_sprites.cpp
@@ -0,0 +1,322 @@
+// license:BSD-3-Clause
+// copyright-holders:Andrei I. Holub
+/**********************************************************************
+ Spectrum Next Sprites
+**********************************************************************/
+
+#include "emu.h"
+#include "specnext_sprites.h"
+
+#include "screen.h"
+
+
+static const gfx_layout gfx_16x16x8 =
+{
+ 16, 16, RGN_FRAC(1, 1), 8,
+ { STEP8(0, 1) },
+ { STEP16(0, 8) },
+ { STEP16(0, 8 * 16) },
+ 8 * 16 * 16
+};
+
+static const gfx_layout gfx_16x16x4 =
+{
+ 16, 16, RGN_FRAC(1, 1), 4,
+ { STEP4(0, 1) },
+ { STEP16(0, 4) },
+ { STEP16(0, 4 * 16) },
+ 4 * 16 * 16
+};
+
+static const gfx_layout gfx_16x16x8_r =
+{
+ 16, 16, RGN_FRAC(1, 1), 8,
+ { STEP8(0, 1) },
+ { STEP16(8 * 16 * 15, -8 * 16) },
+ { STEP16(0, 8) },
+ 8 * 16 * 16
+};
+
+static const gfx_layout gfx_16x16x4_r =
+{
+ 16, 16, RGN_FRAC(1, 1), 4,
+ { STEP4(0, 1) },
+ { STEP16(4 * 16 * 15, -4 * 16) },
+ { STEP16(0, 4) },
+ 4 * 16 * 16
+};
+
+static GFXDECODE_START( gfx_sprites )
+ GFXDECODE_DEVICE_RAM( "pattern_ram", 0, gfx_16x16x8, 0, 256 )
+ GFXDECODE_DEVICE_RAM( "pattern_ram", 0, gfx_16x16x4, 0, 16 )
+ GFXDECODE_DEVICE_RAM( "pattern_ram", 0, gfx_16x16x8_r, 0, 256 )
+ GFXDECODE_DEVICE_RAM( "pattern_ram", 0, gfx_16x16x4_r, 0, 16 )
+GFXDECODE_END
+
+specnext_sprites_device::specnext_sprites_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock)
+ : device_t(mconfig, SPECNEXT_SPRITES, tag, owner, clock)
+ , device_gfx_interface(mconfig, *this, gfx_sprites)
+ , m_sprite_pattern_ram(*this, "pattern_ram", 64 * 256, ENDIANNESS_LITTLE)
+ , m_sprite_attr_ram(*this, "attr_ram", 128 * 8, ENDIANNESS_LITTLE)
+{
+}
+
+specnext_sprites_device &specnext_sprites_device::set_palette(const char *tag, u16 base_offset, u16 alt_offset)
+{
+ device_gfx_interface::set_palette(tag);
+ m_palette_base_offset = base_offset,
+ m_palette_alt_offset = alt_offset;
+ update_config();
+
+ return *this;
+}
+
+void specnext_sprites_device::draw(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect, u32 pmask)
+{
+ if (m_sprites_cache.empty()) update_sprites_cache();
+ const rectangle clipped = m_clip_window & cliprect;
+
+ for (auto i = 0; i < m_sprites_cache.size(); i++ )
+ {
+ const u8 x = m_zero_on_top ? i : (m_sprites_cache.size() - 1) - i;
+ const sprite_data &spr = m_sprites_cache[x];
+
+ gfx((spr.rotate << 1) | spr.h)->prio_zoom_transpen(bitmap, clipped
+ , spr.pattern >> (1 - spr.h), spr.paloff
+ , spr.xmirror, spr.ymirror
+ , ((spr.x & 0x1ff) << 1) + m_offset_h, (spr.y & 0x1ff) + m_offset_v
+ , 0x20000 << spr.xscale, 0x10000 << spr.yscale
+ , screen.priority(), pmask
+ , m_transp_colour & (spr.h ? 0x0f : 0xff));
+ }
+}
+
+/* The cache only stores visible sprites.
+ Sprites becomes visible only it's anchor(+v), or relative(+v) to visible anchor.
+*/
+void specnext_sprites_device::update_sprites_cache()
+{
+ m_sprites_cache.clear();
+ m_sprites_cache.reserve(128);
+
+ sprite_data *anchor;
+ bool anchor_vis = false;
+ u8 *sprite_attr = m_sprite_attr_ram;
+ for (auto i = 0; i < 128; i++, sprite_attr += 8)
+ {
+ const bool spr_relative = BIT(sprite_attr[3], 6) && ((sprite_attr[4] >> 6) == 0b01);
+ bool is_visible = BIT(sprite_attr[3], 7);
+
+ if (spr_relative)
+ is_visible &= anchor_vis; // skip relative unless visible anchor is found
+ else
+ anchor_vis = is_visible;
+
+ if (is_visible)
+ {
+ u8 spr_cur_attr[5] = {};
+ sprite_data spr_cur;
+ u8 anchor_pattern = 0;
+ if (!spr_relative) // Anchor
+ memcpy(spr_cur_attr, sprite_attr, 5);
+ else
+ {
+ const bool anchor_rotate = anchor->rel_type ? anchor->rotate : 0;
+ const bool anchor_xmirror = anchor->rel_type ? anchor->xmirror : 0;
+ const bool anchor_ymirror = anchor->rel_type ? anchor->ymirror : 0;
+ const u8 anchor_xscale = anchor->rel_type ? anchor->xscale : 0b00;
+ const u8 anchor_yscale = anchor->rel_type ? anchor->yscale : 0b00;
+
+ const u8 spr_rel_x0 = anchor_rotate ? sprite_attr[1] : sprite_attr[0];
+ const u8 spr_rel_y0 = anchor_rotate ? sprite_attr[0] : sprite_attr[1];;
+ const u8 spr_rel_x1 = (anchor_rotate xor anchor_xmirror) ? (~spr_rel_x0 + 1) : spr_rel_x0;
+ const u8 spr_rel_y1 = anchor_ymirror ? (~spr_rel_y0 + 1) : spr_rel_y0;
+ const u16 spr_rel_x2 = (((BIT(spr_rel_x1, 7) << 8) | spr_rel_x1) << anchor_xscale) & 0x1ff;
+ const u16 spr_rel_y2 = (((BIT(spr_rel_y1, 7) << 8) | spr_rel_y1) << anchor_yscale) & 0x1ff;
+ const u16 spr_rel_x3 = (anchor->x + spr_rel_x2) & 0x1ff;
+ const u16 spr_rel_y3 = (anchor->y + spr_rel_y2) & 0x1ff;
+
+ const u8 spr_rel_paloff = BIT(sprite_attr[2], 0) ? anchor->paloff + BIT(sprite_attr[2], 4, 4) : BIT(sprite_attr[2], 4, 4);
+
+ const bool spr_rel_xm = anchor_rotate ? BIT(sprite_attr[2], 2) xor BIT(sprite_attr[2], 1) : BIT(sprite_attr[2], 3);
+ const bool spr_rel_ym = anchor_rotate ? BIT(sprite_attr[2], 3) xor BIT(sprite_attr[2], 1) : BIT(sprite_attr[2], 2);
+
+ spr_cur_attr[0] = BIT(spr_rel_x3, 0, 8);
+ spr_cur_attr[1] = BIT(spr_rel_y3, 0, 8);
+ spr_cur_attr[2] = anchor->rel_type
+ ? (spr_rel_paloff << 4) | ((anchor_xmirror xor spr_rel_xm) << 3) | ((anchor_ymirror xor spr_rel_ym) << 2) | ((anchor_rotate xor BIT(sprite_attr[2], 1)) << 1) | BIT(spr_rel_x3, 8)
+ : (spr_rel_paloff << 4) | (BIT(sprite_attr[2], 1, 3) << 1) | BIT(spr_rel_x3, 8);
+ spr_cur_attr[3] = (is_visible << 7) | (0b1 << 6) | BIT(sprite_attr[3], 0, TOTAL_PATTERN_BITS);
+ spr_cur_attr[4] = anchor->rel_type
+ ? (anchor->h << 7) | (BIT(sprite_attr[4], 5) << 6) | (anchor_xscale << 3) | (anchor_yscale << 1) | BIT(spr_rel_y3, 8)
+ : (anchor->h << 7) | (BIT(sprite_attr[4], 5) << 6) | (BIT(sprite_attr[4], 1, 4) << 1) | BIT(spr_rel_y3, 8);
+
+ anchor_pattern = anchor->pattern;
+ }
+
+ bool y8 = BIT(sprite_attr[3], 6) ? BIT(spr_cur_attr[4], 0) : 0;
+ spr_cur.y = (y8 << 8) | spr_cur_attr[1];
+ spr_cur.x = (BIT(spr_cur_attr[2], 0) << 8) | spr_cur_attr[0];
+ spr_cur.rotate = BIT(spr_cur_attr[2], 1);
+ spr_cur.ymirror = BIT(spr_cur_attr[2], 2);
+ spr_cur.xmirror = BIT(spr_cur_attr[2], 3);
+ spr_cur.paloff = BIT(spr_cur_attr[2], 4, 4);
+
+ spr_cur.h = BIT(spr_cur_attr[4], 7) && BIT(sprite_attr[3], 6);
+ bool spr_cur_n6 = BIT(spr_cur_attr[4], 6) && spr_cur.h;
+ spr_cur.pattern = (BIT(spr_cur_attr[3], 0, TOTAL_PATTERN_BITS) << 1) | spr_cur_n6;
+ if (spr_relative && BIT(sprite_attr[4], 0))
+ spr_cur.pattern = (spr_cur.pattern + anchor_pattern) & 0x7f;
+
+ spr_cur.yscale = BIT(spr_cur_attr[4], 1, 2);
+ spr_cur.xscale = BIT(spr_cur_attr[4], 3, 2);
+ spr_cur.rel_type = BIT(sprite_attr[4], 5) && BIT(sprite_attr[3], 6);
+
+ m_sprites_cache.push_back(spr_cur);
+ if (!spr_relative)
+ anchor = &m_sprites_cache.back();
+ }
+ }
+}
+
+void specnext_sprites_device::update_config()
+{
+ if (gfx(0) == nullptr) return;
+
+ for (auto i = 0; i < 4; ++i)
+ {
+ gfx(i)->set_granularity(16); // Change granularity to 16 for 8bpp
+ gfx(i)->set_colorbase(m_sprite_palette_select ? m_palette_alt_offset : m_palette_base_offset);
+ }
+
+ m_clip_window = SCREEN_AREA; // over + !clip
+ if (!m_over_border)
+ m_clip_window = rectangle { m_clip_x1 + OVER_BORDER, m_clip_x2 + OVER_BORDER, m_clip_y1 + OVER_BORDER, m_clip_y2 + OVER_BORDER };
+ else if (m_border_clip_en)
+ m_clip_window = rectangle { m_clip_x1 << 1, (m_clip_x2 << 1) | 1, m_clip_y1, m_clip_y2 };
+ m_clip_window.setx(m_clip_window.left() << 1, (m_clip_window.right() << 1) | 1);
+ m_clip_window.offset(m_offset_h, m_offset_v);
+}
+
+void specnext_sprites_device::io_w(offs_t addr, u8 data)
+{
+ bool attr_num_change = 0;
+ if (addr == 0x5b)
+ {
+ // Ugh
+ gfx(0)->mark_all_dirty();
+ gfx(1)->mark_all_dirty();
+ gfx(2)->mark_all_dirty();
+ gfx(3)->mark_all_dirty();
+
+ m_sprite_pattern_ram[m_pattern_index] = data;
+ ++m_pattern_index &= 0x3fff;
+ }
+ else if (addr == 0x303b)
+ {
+ m_pattern_index = (BIT(data, 0, TOTAL_PATTERN_BITS) << 8) | (data & 0x80);
+ m_attr_index = BIT(data, 0, TOTAL_SPRITES_BITS) << 3;
+ attr_num_change = 1;
+ }
+ else if (addr == 0x57)
+ {
+ m_sprites_cache.clear();
+ m_sprite_attr_ram[m_attr_index] = data;
+
+ const bool index_inc_attr_by_8 = BIT(m_attr_index, 2) || ((BIT(m_attr_index, 0, 3) == 0b011) && (BIT(data, 6) == 0));
+ const u16 index_inc_in_s = index_inc_attr_by_8
+ ? BIT(m_attr_index, 3, TOTAL_SPRITES_BITS)
+ : m_attr_index;
+ const u16 index_inc_out_s = index_inc_in_s + 1;
+
+ if (index_inc_attr_by_8 == 0)
+ m_attr_index = BIT(index_inc_out_s, 0, TOTAL_SPRITES_BITS + 3);
+ else
+ {
+ m_attr_index = BIT(index_inc_out_s, 0, TOTAL_SPRITES_BITS) << 3;
+ attr_num_change = 1;
+ }
+ }
+
+ if (attr_num_change && m_mirror_tie)
+ {
+ m_mirror_sprite_q &= ~0xff;
+ m_mirror_sprite_q |= BIT(m_attr_index, 3, TOTAL_SPRITES_BITS);
+ m_mirror_sprite_q |= m_pattern_index & 0x80;
+ }
+}
+
+void specnext_sprites_device::mirror_data_w(u8 mirror_data)
+{
+ if (m_mirror_index <= 0b100)
+ {
+ m_sprites_cache.clear();
+ m_sprite_attr_ram[(m_mirror_sprite_q << 3) | m_mirror_index] = mirror_data;
+ }
+
+ bool mirror_num_change = 0;
+ if (m_mirror_index == 0b111)
+ {
+ m_mirror_sprite_q = mirror_data;
+ mirror_num_change = 1;
+ }
+ else if (m_mirror_inc)
+ {
+ m_mirror_sprite_q &= ~0xff;
+ m_mirror_sprite_q |= (BIT(m_mirror_sprite_q, 0, TOTAL_SPRITES_BITS) + 1) & 0x7f;
+ m_mirror_sprite_q |= m_pattern_index & 0x80;
+ mirror_num_change = 1;
+ }
+
+ if (mirror_num_change && m_mirror_tie)
+ {
+ m_pattern_index = (BIT(m_mirror_sprite_q, 0, TOTAL_PATTERN_BITS) << 8) | (m_mirror_sprite_q & 0x80);
+ m_attr_index = BIT(m_mirror_sprite_q, 0, TOTAL_SPRITES_BITS) << 3;
+ }
+}
+
+void specnext_sprites_device::device_add_mconfig(machine_config &config)
+{
+ m_offset_h = 0;
+ m_offset_v = 0;
+ m_clip_window = SCREEN_AREA;
+}
+
+void specnext_sprites_device::device_start()
+{
+ save_item(NAME(m_sprite_palette_select));
+
+ save_item(NAME(m_zero_on_top));
+ save_item(NAME(m_border_clip_en));
+ save_item(NAME(m_over_border));
+ save_item(NAME(m_transp_colour));
+ save_item(NAME(m_clip_x1));
+ save_item(NAME(m_clip_x2));
+ save_item(NAME(m_clip_y1));
+ save_item(NAME(m_clip_y2));
+
+ save_item(NAME(m_mirror_tie));
+ save_item(NAME(m_mirror_index));
+ save_item(NAME(m_mirror_inc));
+
+ save_item(NAME(m_attr_index));
+ save_item(NAME(m_pattern_index));
+ save_item(NAME(m_mirror_sprite_q));
+
+ update_config();
+}
+
+void specnext_sprites_device::device_reset()
+{
+ m_attr_index = 0;
+ m_pattern_index = 0;
+ m_mirror_sprite_q = 0;
+
+ memset(m_sprite_attr_ram, 0, 128 * 8);
+ m_sprites_cache.clear();
+ update_config();
+}
+
+
+// device type definition
+DEFINE_DEVICE_TYPE(SPECNEXT_SPRITES, specnext_sprites_device, "sprites", "Spectrum Next Sprites")
diff --git a/src/mame/sinclair/specnext_sprites.h b/src/mame/sinclair/specnext_sprites.h
new file mode 100644
index 00000000000..2d3ec3eac0b
--- /dev/null
+++ b/src/mame/sinclair/specnext_sprites.h
@@ -0,0 +1,96 @@
+// license:BSD-3-Clause
+// copyright-holders:Andrei I. Holub
+#ifndef MAME_SINCLAIR_SPECNEXT_SPRITES_H
+#define MAME_SINCLAIR_SPECNEXT_SPRITES_H
+
+#pragma once
+
+class specnext_sprites_device : public device_t, public device_gfx_interface
+{
+
+public:
+ specnext_sprites_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock);
+
+ specnext_sprites_device &set_raster_offset(u16 offset_h, u16 offset_v) { m_offset_h = offset_h - (OVER_BORDER << 1); m_offset_v = offset_v - OVER_BORDER; return *this; }
+ specnext_sprites_device &set_palette(const char *tag, u16 base_offset, u16 alt_offset);
+
+ void update_sprites_cache();
+ void draw(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect, u32 pmask);
+
+ void sprite_palette_select_w(bool sprite_palette_select) { m_sprite_palette_select = sprite_palette_select; update_config(); }
+
+ void zero_on_top_w(bool zero_on_top) { m_zero_on_top = zero_on_top; }
+ void border_clip_en_w(bool border_clip_en) { m_border_clip_en = border_clip_en; update_config(); }
+ void over_border_w(bool over_border) { m_over_border = over_border; update_config(); }
+ void transp_colour_w(u8 transp_colour) { m_transp_colour = transp_colour; }
+ void clip_x1_w(u8 clip_x1) { m_clip_x1 = clip_x1; update_config(); }
+ void clip_x2_w(u8 clip_x2) { m_clip_x2 = clip_x2; update_config(); }
+ void clip_y1_w(u8 clip_y1) { m_clip_y1 = clip_y1; update_config(); }
+ void clip_y2_w(u8 clip_y2) { m_clip_y2 = clip_y2; update_config(); }
+
+ void mirror_tie_w(bool mirror_tie) { m_mirror_tie = mirror_tie; }
+ void mirror_index_w(u8 mirror_index) { m_mirror_index = mirror_index & 0x07; }
+ void io_w(offs_t addr, u8 data);
+ void mirror_data_w(u8 mirror_data);
+ void mirror_inc_w(bool mirror_inc) { m_mirror_inc = mirror_inc; }
+ u8 mirror_num_r() { return BIT(m_mirror_sprite_q, 0, TOTAL_SPRITES_BITS); }
+
+protected:
+ static constexpr u8 OVER_BORDER = 32;
+ static constexpr u8 TOTAL_SPRITES_BITS = 7;
+ static constexpr u8 TOTAL_PATTERN_BITS = 6;
+ static constexpr rectangle SCREEN_AREA = { 0, 319, 0, 255 };
+
+ virtual void device_add_mconfig(machine_config &config) override ATTR_COLD;
+ virtual void device_start() override ATTR_COLD;
+ virtual void device_reset() override ATTR_COLD;
+
+ memory_share_creator<u8> m_sprite_pattern_ram;
+ memory_share_creator<u8> m_sprite_attr_ram;
+
+ void update_config();
+
+private:
+ struct sprite_data
+ {
+ bool rel_type;
+ bool h;
+ u16 x; // u9
+ u16 y; // u9
+ u8 pattern; // u7
+ u8 paloff; // u4
+ bool rotate;
+ bool xmirror;
+ bool ymirror;
+ u8 xscale; // u2
+ u8 yscale; // u2
+ };
+
+ std::vector<sprite_data> m_sprites_cache;
+ u16 m_offset_h, m_offset_v;
+ rectangle m_clip_window;
+ u16 m_palette_base_offset;
+ u16 m_palette_alt_offset;
+ bool m_sprite_palette_select;
+
+ bool m_zero_on_top;
+ bool m_border_clip_en;
+ bool m_over_border;
+ u8 m_transp_colour;
+ u8 m_clip_x1;
+ u8 m_clip_x2;
+ u8 m_clip_y1;
+ u8 m_clip_y2;
+
+ bool m_mirror_tie;
+ u8 m_mirror_index; // attributes 0-4, sprite number if 7
+ bool m_mirror_inc;
+
+ u16 m_attr_index;
+ u16 m_pattern_index;
+ u8 m_mirror_sprite_q;
+};
+
+
+DECLARE_DEVICE_TYPE(SPECNEXT_SPRITES, specnext_sprites_device)
+#endif // MAME_SINCLAIR_SPECNEXT_SPRITES_H
diff --git a/src/mame/sinclair/specnext_tiles.cpp b/src/mame/sinclair/specnext_tiles.cpp
new file mode 100644
index 00000000000..9aced473d37
--- /dev/null
+++ b/src/mame/sinclair/specnext_tiles.cpp
@@ -0,0 +1,157 @@
+// license:BSD-3-Clause
+// copyright-holders:Andrei I. Holub
+/**********************************************************************
+ Spectrum Next Tiles
+**********************************************************************/
+
+#include "emu.h"
+#include "specnext_tiles.h"
+
+#include "screen.h"
+
+
+static const gfx_layout gfx_8x8x4 =
+{
+ 8, 8, 256 * 2, 4,
+ { STEP4(0, 1) },
+ { STEP8(0, 4) },
+ { STEP8(0, 4 * 8) },
+ 4 * 8 * 8
+};
+
+static const gfx_layout gfx_8x8x4_r =
+{
+ 8, 8, 256 * 2, 4,
+ { STEP4(0, 1) },
+ { STEP8(4 * 8 * 7, -4 * 8) },
+ { STEP8(0, 4) },
+ 4 * 8 * 8
+};
+
+static const gfx_layout gfx_text =
+{
+ 8, 8, 256 * 2, 1,
+ { 0 },
+ { STEP8(0, 1) },
+ { STEP8(0, 8) },
+ 8 * 8
+};
+
+static GFXDECODE_START( gfx_tiles )
+ GFXDECODE_SCALE( nullptr, 0, gfx_8x8x4, 0, 16, 2, 1 ) // 40x32
+ GFXDECODE_ENTRY( nullptr, 0, gfx_8x8x4, 0, 16 ) // 80x32
+ GFXDECODE_SCALE( nullptr, 0, gfx_8x8x4_r, 0, 16, 2, 1 ) // 40x32 rotated
+ GFXDECODE_ENTRY( nullptr, 0, gfx_8x8x4_r, 0, 16 ) // 80x32 rotated
+ GFXDECODE_SCALE( nullptr, 0, gfx_text, 0, 128, 2, 1 ) // 40x32
+ GFXDECODE_ENTRY( nullptr, 0, gfx_text, 0, 128 ) // 80x32
+GFXDECODE_END
+
+specnext_tiles_device::specnext_tiles_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock)
+ : device_t(mconfig, SPECNEXT_TILES, tag, owner, clock)
+ , device_gfx_interface(mconfig, *this, gfx_tiles)
+{
+}
+
+specnext_tiles_device &specnext_tiles_device::set_palette(const char *tag, u16 base_offset, u16 alt_offset)
+{
+ device_gfx_interface::set_palette(tag);
+ m_palette_base_offset = base_offset,
+ m_palette_alt_offset = alt_offset;
+ tilemap_update();
+
+ return *this;
+}
+
+TILE_GET_INFO_MEMBER(specnext_tiles_device::get_tile_info)
+{
+ const bool attr_in_map = BIT(~m_control, 5);
+ const u8 *data = &m_tiles_info[tile_index << attr_in_map];
+ u8 attr = attr_in_map ? *(data + attr_in_map) : m_default_flags;
+ u16 code = *data;
+
+ u32 category;
+ if (BIT(m_control, 1))
+ {
+ code |= BIT(attr, 0) << 8;
+ category = BIT(m_control, 0) ? 1 : 2;
+ }
+ else
+ {
+ category = BIT(attr, 0) ? 1 : 2;
+ }
+ tileinfo.category = category;
+
+ if (BIT(m_control, 3)) // textmode
+ tileinfo.set(4 | BIT(m_control, 6), code, BIT(attr, 1, 7), 0);
+ else
+ tileinfo.set((BIT(attr, 1) << 1) | BIT(m_control, 6), code, BIT(attr, 4, 4), (TILE_FLIPY * BIT(attr, 2) | (TILE_FLIPX * BIT(attr, 3))));
+}
+
+void specnext_tiles_device::tilemap_update()
+{
+ if (gfx(0) == nullptr) return;
+
+ const u8 *tiles_offset = m_host_ram_ptr + ((BIT(m_tm_tile_base, 6) ? 7 : 5) << 14) + ((m_tm_tile_base & 0x3f) << 8);
+ for (auto i = 0; i < 6; ++i)
+ {
+ gfx(i)->set_source(tiles_offset);
+ gfx(i)->set_granularity(i < 4 ? 16 : 2);
+ }
+ for (auto i = 0; i < 2; ++i)
+ {
+ m_tilemap[i]->set_palette_offset(BIT(m_control, 4) ? m_palette_alt_offset : m_palette_base_offset);
+ m_tilemap[i]->set_transparent_pen(m_transp_colour);
+ m_tilemap[i]->set_scrollx(-m_offset_h + (m_tm_scroll_x << 1));
+ m_tilemap[i]->set_scrolly(-m_offset_v + m_tm_scroll_y);
+ m_tilemap[i]->mark_mapping_dirty();
+ m_tilemap[i]->mark_all_dirty();
+ }
+
+ m_tiles_info = m_host_ram_ptr + ((BIT(m_tm_map_base, 6) ? 7 : 5) << 14) + ((m_tm_map_base & 0x3f) << 8);
+}
+
+void specnext_tiles_device::draw(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect, u32 flags, u8 pcode)
+{
+ rectangle clip = rectangle{ m_clip_x1 << 1, (m_clip_x2 << 1) | 1, m_clip_y1, m_clip_y2 };
+ clip &= SCREEN_AREA;
+ clip.setx(clip.left() << 1, (clip.right() << 1) | 1);
+ clip.offset(m_offset_h, m_offset_v);
+ clip &= cliprect;
+
+ if (!clip.empty())
+ m_tilemap[BIT(m_control, 6)]->draw(screen, bitmap, clip, flags, pcode);
+}
+
+void specnext_tiles_device::device_add_mconfig(machine_config &config)
+{
+ m_offset_h = 0;
+ m_offset_v = 0;
+}
+
+void specnext_tiles_device::device_start()
+{
+ m_tilemap[0] = &machine().tilemap().create(*this
+ , tilemap_get_info_delegate(*this, FUNC(specnext_tiles_device::get_tile_info))
+ , TILEMAP_SCAN_ROWS, 16, 8, 40, 32);
+ m_tilemap[1] = &machine().tilemap().create(*this
+ , tilemap_get_info_delegate(*this, FUNC(specnext_tiles_device::get_tile_info))
+ , TILEMAP_SCAN_ROWS, 8, 8, 80, 32);
+
+ save_item(NAME(m_tm_palette_select));
+ save_item(NAME(m_control));
+ save_item(NAME(m_default_flags));
+ save_item(NAME(m_transp_colour));
+ save_item(NAME(m_tm_map_base));
+ save_item(NAME(m_tm_tile_base));
+ save_item(NAME(m_tm_scroll_x));
+ save_item(NAME(m_tm_scroll_y));
+ save_item(NAME(m_clip_x1));
+ save_item(NAME(m_clip_x2));
+ save_item(NAME(m_clip_y1));
+ save_item(NAME(m_clip_y2));
+
+ tilemap_update();
+}
+
+// device type definition
+DEFINE_DEVICE_TYPE(SPECNEXT_TILES, specnext_tiles_device, "tiles", "Spectrum Next Tiles")
diff --git a/src/mame/sinclair/specnext_tiles.h b/src/mame/sinclair/specnext_tiles.h
new file mode 100644
index 00000000000..d656ae0433a
--- /dev/null
+++ b/src/mame/sinclair/specnext_tiles.h
@@ -0,0 +1,76 @@
+// license:BSD-3-Clause
+// copyright-holders:Andrei I. Holub
+#ifndef MAME_SINCLAIR_SPECNEXT_TILES_H
+#define MAME_SINCLAIR_SPECNEXT_TILES_H
+
+#pragma once
+
+#include "tilemap.h"
+
+class specnext_tiles_device : public device_t, public device_gfx_interface
+{
+
+public:
+ specnext_tiles_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock);
+
+ specnext_tiles_device &set_raster_offset(u16 offset_h, u16 offset_v) { m_offset_h = offset_h - (OVER_BORDER << 1); m_offset_v = offset_v - OVER_BORDER; return *this; }
+ specnext_tiles_device &set_host_ram_ptr(const u8 *host_ram_ptr) { m_host_ram_ptr = host_ram_ptr; return *this; }
+ specnext_tiles_device &set_palette(const char *tag, u16 base_offset, u16 alt_offset);
+ void tilemap_update();
+
+ void control_w(u8 control) { m_control = control; tilemap_update(); }
+ void default_flags_w(u8 default_flags) { m_default_flags = default_flags; m_tilemap[0]->mark_all_dirty(); m_tilemap[1]->mark_all_dirty(); }
+ void transp_colour_w(u8 transp_colour) { m_transp_colour = transp_colour; tilemap_update(); }
+
+ void tm_map_base_w(u8 tm_map_base) { m_tm_map_base = tm_map_base; m_tilemap[0]->mark_all_dirty(); m_tilemap[1]->mark_all_dirty(); }
+ void tm_tile_base_w(u8 tm_tile_base) { m_tm_tile_base = tm_tile_base; tilemap_update(); }
+
+ void tm_scroll_x_w(u16 tm_scroll_x) { m_tm_scroll_x = tm_scroll_x; tilemap_update(); }
+ void tm_scroll_y_w(u8 tm_scroll_y) { m_tm_scroll_y = tm_scroll_y; tilemap_update(); }
+
+ void clip_x1_w(u8 clip_x1) { m_clip_x1 = clip_x1; }
+ void clip_x2_w(u8 clip_x2) { m_clip_x2 = clip_x2; }
+ void clip_y1_w(u8 clip_y1) { m_clip_y1 = clip_y1; }
+ void clip_y2_w(u8 clip_y2) { m_clip_y2 = clip_y2; }
+
+ void draw(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect, u32 flags, u8 pcode = 0xff);
+
+protected:
+ static constexpr u8 OVER_BORDER = 32;
+ static constexpr rectangle SCREEN_AREA = { 0, 319, 0, 255 };
+
+ virtual void device_add_mconfig(machine_config &config) override ATTR_COLD;
+ virtual void device_start() override ATTR_COLD;
+
+ TILE_GET_INFO_MEMBER(get_tile_info);
+
+ const u8 *m_host_ram_ptr;
+ const u8 *m_tiles_info;
+ tilemap_t *m_tilemap[2];
+
+private:
+ u16 m_palette_base_offset;
+ u16 m_palette_alt_offset;
+ bool m_tm_palette_select;
+
+ u16 m_offset_h, m_offset_v;
+
+ u8 m_control; // u7
+ u8 m_default_flags;
+ u8 m_transp_colour;
+
+ u8 m_tm_map_base;
+ u8 m_tm_tile_base;
+
+ u16 m_tm_scroll_x;
+ u8 m_tm_scroll_y;
+
+ u8 m_clip_x1;
+ u8 m_clip_x2;
+ u8 m_clip_y1;
+ u8 m_clip_y2;
+};
+
+
+DECLARE_DEVICE_TYPE(SPECNEXT_TILES, specnext_tiles_device)
+#endif // MAME_SINCLAIR_SPECNEXT_TILES_H
diff --git a/src/osd/modules/debugger/debuggdbstub.cpp b/src/osd/modules/debugger/debuggdbstub.cpp
index e5092c71548..3c053abd327 100644
--- a/src/osd/modules/debugger/debuggdbstub.cpp
+++ b/src/osd/modules/debugger/debuggdbstub.cpp
@@ -478,6 +478,7 @@ static const std::map<std::string, const gdb_register_map &> gdb_register_maps =
{ "m68020pmmu", gdb_register_map_m68020pmmu },
{ "m68000", gdb_register_map_m68000 },
{ "z80", gdb_register_map_z80 },
+ { "z80n", gdb_register_map_z80 },
{ "z84c015", gdb_register_map_z80 },
{ "m6502", gdb_register_map_m6502 },
{ "m6507", gdb_register_map_m6502 },