From 8e82c9f767c4a9872f8dad2eba09840a1e44febe Mon Sep 17 00:00:00 2001 From: holub Date: Wed, 12 Jun 2024 16:12:26 -0400 Subject: New WORKING - sinclair/specnext.cpp: ZX Spectrum Next (known as tbblue) (#11918) New Working Machines ----------------------- ZX Spectrum Next: TBBlue [holub] --- scripts/src/cpu.lua | 28 +- src/devices/cpu/z80/z80.cpp | 91 +- src/devices/cpu/z80/z80.inc | 90 + src/devices/cpu/z80/z80.lst | 188 ++ src/devices/cpu/z80/z80dasm.cpp | 20 +- src/devices/cpu/z80/z80dasm.h | 7 + src/devices/cpu/z80/z80n.cpp | 43 + src/devices/cpu/z80/z80n.h | 44 + src/devices/cpu/z80/z80ndasm.cpp | 76 + src/devices/cpu/z80/z80ndasm.h | 23 + src/mame/mame.lst | 3 + src/mame/sinclair/screen_ula.cpp | 4 +- src/mame/sinclair/specnext.cpp | 3531 +++++++++++++++++++++++++++++ src/mame/sinclair/specnext_copper.cpp | 1 + src/mame/sinclair/specnext_copper.h | 4 +- src/mame/sinclair/specnext_ctc.h | 1 - src/mame/sinclair/specnext_dma.h | 4 +- src/mame/sinclair/specnext_layer2.cpp | 183 ++ src/mame/sinclair/specnext_layer2.h | 66 + src/mame/sinclair/specnext_lores.cpp | 111 + src/mame/sinclair/specnext_lores.h | 65 + src/mame/sinclair/specnext_multiface.h | 4 +- src/mame/sinclair/specnext_sprites.cpp | 322 +++ src/mame/sinclair/specnext_sprites.h | 96 + src/mame/sinclair/specnext_tiles.cpp | 157 ++ src/mame/sinclair/specnext_tiles.h | 76 + src/osd/modules/debugger/debuggdbstub.cpp | 1 + 27 files changed, 5132 insertions(+), 107 deletions(-) create mode 100644 src/devices/cpu/z80/z80.inc create mode 100644 src/devices/cpu/z80/z80n.cpp create mode 100644 src/devices/cpu/z80/z80n.h create mode 100644 src/devices/cpu/z80/z80ndasm.cpp create mode 100644 src/devices/cpu/z80/z80ndasm.h create mode 100644 src/mame/sinclair/specnext.cpp create mode 100644 src/mame/sinclair/specnext_layer2.cpp create mode 100644 src/mame/sinclair/specnext_layer2.h create mode 100644 src/mame/sinclair/specnext_lores.cpp create mode 100644 src/mame/sinclair/specnext_lores.h create mode 100644 src/mame/sinclair/specnext_sprites.cpp create mode 100644 src/mame/sinclair/specnext_sprites.h create mode 100644 src/mame/sinclair/specnext_tiles.cpp create mode 100644 src/mame/sinclair/specnext_tiles.h 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 z80_device::SZ = std::make_unique(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 z80n_device::create_disassembler() +{ + return std::make_unique(); +} + +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 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 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 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 m_copper; + required_device m_ctc; + required_device m_dma; + required_device m_i2cmem; + required_device m_sdcard; + required_device_array m_ay; + required_device_array m_dac; + required_device m_palette; + required_device m_regs_map; + required_device m_mf; + required_device m_divmmc; + required_device m_ula; + required_device m_tiles; + required_device m_layer2; + required_device m_lores; + required_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; + 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; + 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")->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(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(m_clip_x2, info[0] - 1) << 1) | 1, m_clip_y1, std::min(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 ) + 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 m_sprite_pattern_ram; + memory_share_creator 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 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 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 }, -- cgit v1.2.3