diff --git a/index.html b/index.html
index a48ac908..bf8271c6 100644
--- a/index.html
+++ b/index.html
@@ -213,7 +213,7 @@ canvas.pixelated {
-
+
diff --git a/src/cpu/z80.coffee b/src/cpu/z80.coffee
index 29450709..7b8bb81c 100644
--- a/src/cpu/z80.coffee
+++ b/src/cpu/z80.coffee
@@ -2314,6 +2314,10 @@ window.buildZ80 = (opts) ->
~48T window, to support retriggered interrupts and interrupt blocking via
chains of EI or DD/FD prefixes */
}
+ self.nonMaskableInterrupt = function() {
+ iff1 = 1;
+ self.requestInterrupt(0x66);
+ }
var z80Interrupt = function() {
if (iff1) {
if (halted) {
diff --git a/src/cpu/z80.js b/src/cpu/z80.js
index 6cefc455..740c1618 100644
--- a/src/cpu/z80.js
+++ b/src/cpu/z80.js
@@ -1666,7 +1666,7 @@ of the host processor, as typed arrays are native-endian
The indirection on 'eval' causes most browsers to evaluate it in the global
scope, giving a significant speed boost
*/
- defineZ80JS = "window.Z80 = function(opts) {\n var self = {};\n\n " + setUpStateJS + "\n\n self.requestInterrupt = function(dataBus) {\n interruptPending = true;\n interruptDataBus = dataBus & 0xffff;\n /* TODO: use event scheduling to keep the interrupt line active for a fixed\n ~48T window, to support retriggered interrupts and interrupt blocking via\n chains of EI or DD/FD prefixes */\n }\n var z80Interrupt = function() {\n if (iff1) {\n if (halted) {\n /* move PC on from the HALT opcode */\n regPairs[" + rpPC + "]++;\n halted = false;\n }\n\n iff1 = iff2 = 0;\n\n /* push current PC in readiness for call to interrupt handler */\n regPairs[" + rpSP + "]--; WRITEMEM(regPairs[" + rpSP + "], regPairs[" + rpPC + "] >> 8);\n regPairs[" + rpSP + "]--; WRITEMEM(regPairs[" + rpSP + "], regPairs[" + rpPC + "] & 0xff);\n\n /* TODO: R register */\n\n switch (im) {\n case 0:\n regPairs[" + rpPC + "] = interruptDataBus; // assume always RST\n tstates += 6;\n break;\n case 1:\n regPairs[" + rpPC + "] = 0x0038;\n tstates += 7;\n break;\n case 2:\n inttemp = (regs[" + rI + "] << 8) | (interruptDataBus & 0xff);\n l = READMEM(inttemp);\n inttemp = (inttemp+1) & 0xffff;\n h = READMEM(inttemp);\n console.log(hex(interruptDataBus), hex(inttemp), hex(l), hex(h));\n regPairs[" + rpPC + "] = (h<<8) | l;\n tstates += 7;\n break;\n }\n }\n };\n\n self.runFrame = function(frameLength) {\n var lastOpcodePrefix, offset, opcode;\n\n while (tstates < frameLength || opcodePrefix) {\n if (interruptible && interruptPending) {\n z80Interrupt();\n interruptPending = false;\n }\n interruptible = true; /* unless overridden by opcode */\n lastOpcodePrefix = opcodePrefix;\n opcodePrefix = '';\n switch (lastOpcodePrefix) {\n case '':\n CONTEND_READ(regPairs[" + rpPC + "], 4);\n opcode = memory.read(regPairs[" + rpPC + "]); regPairs[" + rpPC + "]++;\n regs[" + rR + "] = ((regs[" + rR + "] + 1) & 0x7f) | (regs[" + rR + "] & 0x80);\n " + (opcodeSwitch(OPCODE_RUN_STRINGS, null, opts.traps)) + "\n break;\n case 'CB':\n CONTEND_READ(regPairs[" + rpPC + "], 4);\n opcode = memory.read(regPairs[" + rpPC + "]); regPairs[" + rpPC + "]++;\n regs[" + rR + "] = ((regs[" + rR + "] + 1) & 0x7f) | (regs[" + rR + "] & 0x80);\n " + (opcodeSwitch(OPCODE_RUN_STRINGS_CB)) + "\n break;\n case 'DD':\n CONTEND_READ(regPairs[" + rpPC + "], 4);\n opcode = memory.read(regPairs[" + rpPC + "]); regPairs[" + rpPC + "]++;\n regs[" + rR + "] = ((regs[" + rR + "] + 1) & 0x7f) | (regs[" + rR + "] & 0x80);\n " + (opcodeSwitch(OPCODE_RUN_STRINGS_DD, OPCODE_RUN_STRINGS)) + "\n break;\n case 'DDCB':\n offset = READMEM(regPairs[" + rpPC + "]); regPairs[" + rpPC + "]++;\n if (offset & 0x80) offset -= 0x100;\n CONTEND_READ(regPairs[" + rpPC + "], 3);\n opcode = memory.read(regPairs[" + rpPC + "]);\n CONTEND_READ_NO_MREQ(regPairs[" + rpPC + "], 1);\n CONTEND_READ_NO_MREQ(regPairs[" + rpPC + "], 1);\n regPairs[" + rpPC + "]++;\n " + (opcodeSwitch(OPCODE_RUN_STRINGS_DDCB)) + "\n break;\n case 'ED':\n CONTEND_READ(regPairs[" + rpPC + "], 4);\n opcode = memory.read(regPairs[" + rpPC + "]); regPairs[" + rpPC + "]++;\n regs[" + rR + "] = ((regs[" + rR + "] + 1) & 0x7f) | (regs[" + rR + "] & 0x80);\n " + (opcodeSwitch(OPCODE_RUN_STRINGS_ED)) + "\n break;\n case 'FD':\n CONTEND_READ(regPairs[" + rpPC + "], 4);\n opcode = memory.read(regPairs[" + rpPC + "]); regPairs[" + rpPC + "]++;\n regs[" + rR + "] = ((regs[" + rR + "] + 1) & 0x7f) | (regs[" + rR + "] & 0x80);\n " + (opcodeSwitch(OPCODE_RUN_STRINGS_FD, OPCODE_RUN_STRINGS)) + "\n break;\n case 'FDCB':\n offset = READMEM(regPairs[" + rpPC + "]); regPairs[" + rpPC + "]++;\n if (offset & 0x80) offset -= 0x100;\n CONTEND_READ(regPairs[" + rpPC + "], 3);\n opcode = memory.read(regPairs[" + rpPC + "]);\n CONTEND_READ_NO_MREQ(regPairs[" + rpPC + "], 1);\n CONTEND_READ_NO_MREQ(regPairs[" + rpPC + "], 1);\n regPairs[" + rpPC + "]++;\n " + (opcodeSwitch(OPCODE_RUN_STRINGS_FDCB)) + "\n break;\n default:\n throw(\"Unknown opcode prefix: \" + lastOpcodePrefix);\n }\n }\n while (display.nextEventTime != null && display.nextEventTime <= tstates) display.doEvent();\n };\n\n self.reset = function() {\n regPairs[" + rpPC + "] = regPairs[" + rpIR + "] = 0;\n iff1 = 0; iff2 = 0; im = 0; halted = false;\n };\n\n self.loadState = function(snapRegs) {\n regPairs[" + rpAF + "] = snapRegs['AF'];\n regPairs[" + rpBC + "] = snapRegs['BC'];\n regPairs[" + rpDE + "] = snapRegs['DE'];\n regPairs[" + rpHL + "] = snapRegs['HL'];\n regPairs[" + rpAF_ + "] = snapRegs['AF_'];\n regPairs[" + rpBC_ + "] = snapRegs['BC_'];\n regPairs[" + rpDE_ + "] = snapRegs['DE_'];\n regPairs[" + rpHL_ + "] = snapRegs['HL_'];\n regPairs[" + rpIX + "] = snapRegs['IX'];\n regPairs[" + rpIY + "] = snapRegs['IY'];\n regPairs[" + rpSP + "] = snapRegs['SP'];\n regPairs[" + rpPC + "] = snapRegs['PC'];\n regPairs[" + rpIR + "] = snapRegs['IR'];\n iff1 = snapRegs['iff1'];\n iff2 = snapRegs['iff2'];\n im = snapRegs['im'];\n halted = snapRegs['halted'];\n tstates = snapRegs['tstates'];\n interruptPending = snapRegs['intp'];\n interruptDataBus = snapRegs['intd'];\n };\n\n self.saveState = function() {\n return {\n AF: regPairs[" + rpAF + "],\n BC: regPairs[" + rpBC + "],\n DE: regPairs[" + rpDE + "],\n HL: regPairs[" + rpHL + "],\n AF_: regPairs[" + rpAF_ + "],\n BC_: regPairs[" + rpBC_ + "],\n DE_: regPairs[" + rpDE_ + "],\n HL_: regPairs[" + rpHL_ + "],\n IX: regPairs[" + rpIX + "],\n IY: regPairs[" + rpIY + "],\n SP: regPairs[" + rpSP + "],\n PC: regPairs[" + rpPC + "],\n IR: regPairs[" + rpIR + "],\n iff1: iff1,\n iff2: iff2,\n im: im,\n halted: halted,\n tstates: tstates,\n intp: interruptPending,\n intd: interruptDataBus,\n };\n };\n\n /* Register / flag accessors (used for tape trapping and test harness) */\n self.getAF = function() {\n return regPairs[" + rpAF + "];\n }\n self.getBC = function() {\n return regPairs[" + rpBC + "];\n }\n self.getDE = function() {\n return regPairs[" + rpDE + "];\n }\n self.getHL = function() {\n return regPairs[" + rpHL + "];\n }\n self.getAF_ = function() {\n return regPairs[" + rpAF_ + "];\n }\n self.getBC_ = function() {\n return regPairs[" + rpBC_ + "];\n }\n self.getDE_ = function() {\n return regPairs[" + rpDE_ + "];\n }\n self.getHL_ = function() {\n return regPairs[" + rpHL_ + "];\n }\n self.getIX = function() {\n return regPairs[" + rpIX + "];\n }\n self.getIY = function() {\n return regPairs[" + rpIY + "];\n }\n self.getI = function() {\n return regs[" + rI + "];\n }\n self.getR = function() {\n return regs[" + rR + "];\n }\n self.getSP = function() {\n return regPairs[" + rpSP + "];\n }\n self.getPC = function() {\n return regPairs[" + rpPC + "];\n }\n self.getIFF1 = function() {\n return iff1;\n }\n self.getIFF2 = function() {\n return iff2;\n }\n self.getIM = function() {\n return im;\n }\n self.getHalted = function() {\n return halted;\n }\n\n self.setAF = function(val) {\n regPairs[" + rpAF + "] = val;\n }\n self.setBC = function(val) {\n regPairs[" + rpBC + "] = val;\n }\n self.setDE = function(val) {\n regPairs[" + rpDE + "] = val;\n }\n self.setHL = function(val) {\n regPairs[" + rpHL + "] = val;\n }\n self.setAF_ = function(val) {\n regPairs[" + rpAF_ + "] = val;\n }\n self.setBC_ = function(val) {\n regPairs[" + rpBC_ + "] = val;\n }\n self.setDE_ = function(val) {\n regPairs[" + rpDE_ + "] = val;\n }\n self.setHL_ = function(val) {\n regPairs[" + rpHL_ + "] = val;\n }\n self.setIX = function(val) {\n regPairs[" + rpIX + "] = val;\n }\n self.setIY = function(val) {\n regPairs[" + rpIY + "] = val;\n }\n self.setI = function(val) {\n regs[" + rI + "] = val;\n }\n self.setR = function(val) {\n regs[" + rR + "] = val;\n }\n self.setSP = function(val) {\n regPairs[" + rpSP + "] = val;\n }\n self.setPC = function(val) {\n regPairs[" + rpPC + "] = val;\n }\n self.setIFF1 = function(val) {\n iff1 = val;\n }\n self.setIFF2 = function(val) {\n iff2 = val;\n }\n self.setIM = function(val) {\n im = val;\n }\n self.setHalted = function(val) {\n halted = val;\n }\n\n self.getTstates = function() {\n return tstates;\n }\n self.setTstates = function(val) {\n tstates = val;\n }\n\n self.getCarry_ = function() {\n return regs[" + rF_ + "] & " + FLAG_C + ";\n };\n self.setCarry = function(val) {\n if (val) {\n regs[" + rF + "] |= " + FLAG_C + ";\n } else {\n regs[" + rF + "] &= " + (~FLAG_C) + ";\n }\n };\n self.getA_ = function() {\n return regs[" + rA_ + "];\n };\n\n return self;\n};";
+ defineZ80JS = "window.Z80 = function(opts) {\n var self = {};\n\n " + setUpStateJS + "\n\n self.requestInterrupt = function(dataBus) {\n interruptPending = true;\n interruptDataBus = dataBus & 0xffff;\n /* TODO: use event scheduling to keep the interrupt line active for a fixed\n ~48T window, to support retriggered interrupts and interrupt blocking via\n chains of EI or DD/FD prefixes */\n }\n self.nonMaskableInterrupt = function() {\n iff1 = 1;\n self.requestInterrupt(0x66);\n }\n var z80Interrupt = function() {\n if (iff1) {\n if (halted) {\n /* move PC on from the HALT opcode */\n regPairs[" + rpPC + "]++;\n halted = false;\n }\n\n iff1 = iff2 = 0;\n\n /* push current PC in readiness for call to interrupt handler */\n regPairs[" + rpSP + "]--; WRITEMEM(regPairs[" + rpSP + "], regPairs[" + rpPC + "] >> 8);\n regPairs[" + rpSP + "]--; WRITEMEM(regPairs[" + rpSP + "], regPairs[" + rpPC + "] & 0xff);\n\n /* TODO: R register */\n\n switch (im) {\n case 0:\n regPairs[" + rpPC + "] = interruptDataBus; // assume always RST\n tstates += 6;\n break;\n case 1:\n regPairs[" + rpPC + "] = 0x0038;\n tstates += 7;\n break;\n case 2:\n inttemp = (regs[" + rI + "] << 8) | (interruptDataBus & 0xff);\n l = READMEM(inttemp);\n inttemp = (inttemp+1) & 0xffff;\n h = READMEM(inttemp);\n console.log(hex(interruptDataBus), hex(inttemp), hex(l), hex(h));\n regPairs[" + rpPC + "] = (h<<8) | l;\n tstates += 7;\n break;\n }\n }\n };\n\n self.runFrame = function(frameLength) {\n var lastOpcodePrefix, offset, opcode;\n\n while (tstates < frameLength || opcodePrefix) {\n if (interruptible && interruptPending) {\n z80Interrupt();\n interruptPending = false;\n }\n interruptible = true; /* unless overridden by opcode */\n lastOpcodePrefix = opcodePrefix;\n opcodePrefix = '';\n switch (lastOpcodePrefix) {\n case '':\n CONTEND_READ(regPairs[" + rpPC + "], 4);\n opcode = memory.read(regPairs[" + rpPC + "]); regPairs[" + rpPC + "]++;\n regs[" + rR + "] = ((regs[" + rR + "] + 1) & 0x7f) | (regs[" + rR + "] & 0x80);\n " + (opcodeSwitch(OPCODE_RUN_STRINGS, null, opts.traps)) + "\n break;\n case 'CB':\n CONTEND_READ(regPairs[" + rpPC + "], 4);\n opcode = memory.read(regPairs[" + rpPC + "]); regPairs[" + rpPC + "]++;\n regs[" + rR + "] = ((regs[" + rR + "] + 1) & 0x7f) | (regs[" + rR + "] & 0x80);\n " + (opcodeSwitch(OPCODE_RUN_STRINGS_CB)) + "\n break;\n case 'DD':\n CONTEND_READ(regPairs[" + rpPC + "], 4);\n opcode = memory.read(regPairs[" + rpPC + "]); regPairs[" + rpPC + "]++;\n regs[" + rR + "] = ((regs[" + rR + "] + 1) & 0x7f) | (regs[" + rR + "] & 0x80);\n " + (opcodeSwitch(OPCODE_RUN_STRINGS_DD, OPCODE_RUN_STRINGS)) + "\n break;\n case 'DDCB':\n offset = READMEM(regPairs[" + rpPC + "]); regPairs[" + rpPC + "]++;\n if (offset & 0x80) offset -= 0x100;\n CONTEND_READ(regPairs[" + rpPC + "], 3);\n opcode = memory.read(regPairs[" + rpPC + "]);\n CONTEND_READ_NO_MREQ(regPairs[" + rpPC + "], 1);\n CONTEND_READ_NO_MREQ(regPairs[" + rpPC + "], 1);\n regPairs[" + rpPC + "]++;\n " + (opcodeSwitch(OPCODE_RUN_STRINGS_DDCB)) + "\n break;\n case 'ED':\n CONTEND_READ(regPairs[" + rpPC + "], 4);\n opcode = memory.read(regPairs[" + rpPC + "]); regPairs[" + rpPC + "]++;\n regs[" + rR + "] = ((regs[" + rR + "] + 1) & 0x7f) | (regs[" + rR + "] & 0x80);\n " + (opcodeSwitch(OPCODE_RUN_STRINGS_ED)) + "\n break;\n case 'FD':\n CONTEND_READ(regPairs[" + rpPC + "], 4);\n opcode = memory.read(regPairs[" + rpPC + "]); regPairs[" + rpPC + "]++;\n regs[" + rR + "] = ((regs[" + rR + "] + 1) & 0x7f) | (regs[" + rR + "] & 0x80);\n " + (opcodeSwitch(OPCODE_RUN_STRINGS_FD, OPCODE_RUN_STRINGS)) + "\n break;\n case 'FDCB':\n offset = READMEM(regPairs[" + rpPC + "]); regPairs[" + rpPC + "]++;\n if (offset & 0x80) offset -= 0x100;\n CONTEND_READ(regPairs[" + rpPC + "], 3);\n opcode = memory.read(regPairs[" + rpPC + "]);\n CONTEND_READ_NO_MREQ(regPairs[" + rpPC + "], 1);\n CONTEND_READ_NO_MREQ(regPairs[" + rpPC + "], 1);\n regPairs[" + rpPC + "]++;\n " + (opcodeSwitch(OPCODE_RUN_STRINGS_FDCB)) + "\n break;\n default:\n throw(\"Unknown opcode prefix: \" + lastOpcodePrefix);\n }\n }\n while (display.nextEventTime != null && display.nextEventTime <= tstates) display.doEvent();\n };\n\n self.reset = function() {\n regPairs[" + rpPC + "] = regPairs[" + rpIR + "] = 0;\n iff1 = 0; iff2 = 0; im = 0; halted = false;\n };\n\n self.loadState = function(snapRegs) {\n regPairs[" + rpAF + "] = snapRegs['AF'];\n regPairs[" + rpBC + "] = snapRegs['BC'];\n regPairs[" + rpDE + "] = snapRegs['DE'];\n regPairs[" + rpHL + "] = snapRegs['HL'];\n regPairs[" + rpAF_ + "] = snapRegs['AF_'];\n regPairs[" + rpBC_ + "] = snapRegs['BC_'];\n regPairs[" + rpDE_ + "] = snapRegs['DE_'];\n regPairs[" + rpHL_ + "] = snapRegs['HL_'];\n regPairs[" + rpIX + "] = snapRegs['IX'];\n regPairs[" + rpIY + "] = snapRegs['IY'];\n regPairs[" + rpSP + "] = snapRegs['SP'];\n regPairs[" + rpPC + "] = snapRegs['PC'];\n regPairs[" + rpIR + "] = snapRegs['IR'];\n iff1 = snapRegs['iff1'];\n iff2 = snapRegs['iff2'];\n im = snapRegs['im'];\n halted = snapRegs['halted'];\n tstates = snapRegs['tstates'];\n interruptPending = snapRegs['intp'];\n interruptDataBus = snapRegs['intd'];\n };\n\n self.saveState = function() {\n return {\n AF: regPairs[" + rpAF + "],\n BC: regPairs[" + rpBC + "],\n DE: regPairs[" + rpDE + "],\n HL: regPairs[" + rpHL + "],\n AF_: regPairs[" + rpAF_ + "],\n BC_: regPairs[" + rpBC_ + "],\n DE_: regPairs[" + rpDE_ + "],\n HL_: regPairs[" + rpHL_ + "],\n IX: regPairs[" + rpIX + "],\n IY: regPairs[" + rpIY + "],\n SP: regPairs[" + rpSP + "],\n PC: regPairs[" + rpPC + "],\n IR: regPairs[" + rpIR + "],\n iff1: iff1,\n iff2: iff2,\n im: im,\n halted: halted,\n tstates: tstates,\n intp: interruptPending,\n intd: interruptDataBus,\n };\n };\n\n /* Register / flag accessors (used for tape trapping and test harness) */\n self.getAF = function() {\n return regPairs[" + rpAF + "];\n }\n self.getBC = function() {\n return regPairs[" + rpBC + "];\n }\n self.getDE = function() {\n return regPairs[" + rpDE + "];\n }\n self.getHL = function() {\n return regPairs[" + rpHL + "];\n }\n self.getAF_ = function() {\n return regPairs[" + rpAF_ + "];\n }\n self.getBC_ = function() {\n return regPairs[" + rpBC_ + "];\n }\n self.getDE_ = function() {\n return regPairs[" + rpDE_ + "];\n }\n self.getHL_ = function() {\n return regPairs[" + rpHL_ + "];\n }\n self.getIX = function() {\n return regPairs[" + rpIX + "];\n }\n self.getIY = function() {\n return regPairs[" + rpIY + "];\n }\n self.getI = function() {\n return regs[" + rI + "];\n }\n self.getR = function() {\n return regs[" + rR + "];\n }\n self.getSP = function() {\n return regPairs[" + rpSP + "];\n }\n self.getPC = function() {\n return regPairs[" + rpPC + "];\n }\n self.getIFF1 = function() {\n return iff1;\n }\n self.getIFF2 = function() {\n return iff2;\n }\n self.getIM = function() {\n return im;\n }\n self.getHalted = function() {\n return halted;\n }\n\n self.setAF = function(val) {\n regPairs[" + rpAF + "] = val;\n }\n self.setBC = function(val) {\n regPairs[" + rpBC + "] = val;\n }\n self.setDE = function(val) {\n regPairs[" + rpDE + "] = val;\n }\n self.setHL = function(val) {\n regPairs[" + rpHL + "] = val;\n }\n self.setAF_ = function(val) {\n regPairs[" + rpAF_ + "] = val;\n }\n self.setBC_ = function(val) {\n regPairs[" + rpBC_ + "] = val;\n }\n self.setDE_ = function(val) {\n regPairs[" + rpDE_ + "] = val;\n }\n self.setHL_ = function(val) {\n regPairs[" + rpHL_ + "] = val;\n }\n self.setIX = function(val) {\n regPairs[" + rpIX + "] = val;\n }\n self.setIY = function(val) {\n regPairs[" + rpIY + "] = val;\n }\n self.setI = function(val) {\n regs[" + rI + "] = val;\n }\n self.setR = function(val) {\n regs[" + rR + "] = val;\n }\n self.setSP = function(val) {\n regPairs[" + rpSP + "] = val;\n }\n self.setPC = function(val) {\n regPairs[" + rpPC + "] = val;\n }\n self.setIFF1 = function(val) {\n iff1 = val;\n }\n self.setIFF2 = function(val) {\n iff2 = val;\n }\n self.setIM = function(val) {\n im = val;\n }\n self.setHalted = function(val) {\n halted = val;\n }\n\n self.getTstates = function() {\n return tstates;\n }\n self.setTstates = function(val) {\n tstates = val;\n }\n\n self.getCarry_ = function() {\n return regs[" + rF_ + "] & " + FLAG_C + ";\n };\n self.setCarry = function(val) {\n if (val) {\n regs[" + rF + "] |= " + FLAG_C + ";\n } else {\n regs[" + rF + "] &= " + (~FLAG_C) + ";\n }\n };\n self.getA_ = function() {\n return regs[" + rA_ + "];\n };\n\n return self;\n};";
defineZ80JS = defineZ80JS.replace(/READMEM\((.*?)\)/g, '(CONTEND_READ($1, 3), memory.read($1))');
defineZ80JS = defineZ80JS.replace(/WRITEMEM\((.*?),(.*?)\)/g, "CONTEND_WRITE($1, 3);\nwhile (display.nextEventTime != null && display.nextEventTime < tstates) display.doEvent();\nmemory.write($1,$2);");
if (opts.applyContention) {
diff --git a/src/emu.js b/src/emu.js
index 3a790b89..7e486441 100644
--- a/src/emu.js
+++ b/src/emu.js
@@ -16,9 +16,7 @@ function __createCanvas(mainElement, width, height) {
// TODO
var fsElement = document.createElement('div');
fsElement.style.position = "relative";
- fsElement.style.padding = "20px";
- if (height > width)
- fsElement.style.margin = "20%"; // TODO
+ fsElement.style.padding = "5%";
fsElement.style.overflow = "hidden";
fsElement.style.background = "black";
@@ -43,7 +41,10 @@ var RasterVideo = function(mainElement, width, height, options) {
this.create = function() {
self.canvas = canvas = __createCanvas(mainElement, width, height);
if (options && options.rotate) {
+ // TODO: aspect ratio?
canvas.style.transform = "rotate("+options.rotate+"deg)";
+ if (canvas.width > canvas.height)
+ canvas.style.paddingTop = canvas.style.paddingBottom = "10%";
}
ctx = canvas.getContext('2d');
imageData = ctx.createImageData(width, height);
@@ -68,9 +69,12 @@ var RasterVideo = function(mainElement, width, height, options) {
return datau32;
}
- this.updateFrame = function() {
+ this.updateFrame = function(sx, sy, dx, dy, width, height) {
imageData.data.set(buf8);
- ctx.putImageData(imageData, 0, 0);
+ if (width && height)
+ ctx.putImageData(imageData, sx, sy, dx, dy, width, height);
+ else
+ ctx.putImageData(imageData, 0, 0);
}
/*
diff --git a/src/platform/galaxian.js b/src/platform/galaxian.js
new file mode 100644
index 00000000..fcf4c0bd
--- /dev/null
+++ b/src/platform/galaxian.js
@@ -0,0 +1,381 @@
+
+"use strict";
+var GALAXIAN_PRESETS = [
+];
+
+// TODO: global???
+window.buildZ80({
+ applyContention: false
+});
+
+var GalaxianPlatform = function(mainElement) {
+ var self = this;
+ this.__proto__ = new BaseZ80Platform();
+
+ var cpu, ram, vram, oram, membus, iobus, rom, palette, outlatches;
+ var video, audio, timer, pixels, displayPCs;
+ var inputs = [0xe,0x8,0x0];
+ var interruptEnabled = 0;
+ var starsEnabled = 0;
+ var watchdog_counter;
+ var frameCounter = 0;
+
+ var XTAL = 18432000.0;
+ var scanlinesPerFrame = 264;
+ var cpuFrequency = XTAL/6; // 3.072 MHz
+ var hsyncFrequency = XTAL/3/192/2; // 16 kHz
+ var vsyncFrequency = hsyncFrequency/132/2; // 60.606060 Hz
+ var vblankDuration = 1/vsyncFrequency * (20/132); // 2500 us
+ var cpuCyclesPerLine = cpuFrequency/hsyncFrequency;
+ var INITIAL_WATCHDOG = 256;
+ var showOffscreenObjects = false;
+ var stars = [];
+ for (var i=0; i<256; i++)
+ stars[i] = noise();
+
+ function drawScanline(pixels, sl) {
+ if (sl < 16 && !showOffscreenObjects) return; // offscreen
+ if (sl >= 240 && !showOffscreenObjects) return; // offscreen
+ // draw tiles
+ var pixofs = sl*264;
+ var outi = pixofs; // starting output pixel in frame buffer
+ for (var xx=0; xx<32; xx++) {
+ var xofs = xx;
+ var scroll = oram.mem[xofs*2]; // even entries control scroll position
+ var attrib = oram.mem[xofs*2+1]; // odd entries control the color base
+ var sl2 = (sl + scroll) & 0xff;
+ var vramofs = (sl2>>3)<<5; // offset in VRAM
+ var yy = sl2 & 7; // y offset within tile
+ var tile = vram.mem[vramofs+xofs];
+ var color0 = (attrib & 7) << 2;
+ var addr = 0x2800+(tile<<3)+yy;
+ var data1 = rom[addr];
+ var data2 = rom[addr+0x800];
+ for (var i=0; i<8; i++) {
+ var bm = 128>>i;
+ var color = color0 + ((data1&bm)?1:0) + ((data2&bm)?2:0);
+ pixels[outi] = palette[color];
+ outi++;
+ }
+ }
+ // draw sprites
+ for (var sprnum=7; sprnum>=0; sprnum--) {
+ var base = (sprnum<<2) + 0x40;
+ var base0 = oram.mem[base];
+ var sy = 240 - (base0 - (sprnum<3)); // the first three sprites match against y-1
+ var yy = (sl - sy);
+ if (yy >= 0 && yy < 16) {
+ var sx = oram.mem[base+3] + 1; // +1 pixel offset from tiles
+ if (sx == 0 && !showOffscreenObjects)
+ continue; // drawn off-buffer
+ var code = oram.mem[base+1];
+ var flipx = code & 0x40; // TODO
+ var flipy = code & 0x80; // TODO
+ code &= 0x3f;
+ var color0 = oram.mem[base+2] << 2;
+ var addr = 0x2800+(code<<5)+(yy<8?yy:yy+8);
+ outi = pixofs + sx; //<< 1
+ var data1 = rom[addr];
+ var data2 = rom[addr+0x800];
+ for (var i=0; i<8; i++) {
+ var bm = 128>>i;
+ var color = ((data1&bm)?1:0) + ((data2&bm)?2:0);
+ if (color)
+ pixels[outi+i] = palette[color0 + color];
+ }
+ var data1 = rom[addr+8];
+ var data2 = rom[addr+0x808];
+ for (var i=0; i<8; i++) {
+ var bm = 128>>i;
+ var color = ((data1&bm)?1:0) + ((data2&bm)?2:0);
+ if (color)
+ pixels[outi+i+8] = palette[color0 + color];
+ }
+ }
+ }
+ // draw bullets/shells
+ var shell = 0xff;
+ var missile = 0xff;
+ for (var which=0; which<8; which++) {
+ var sy = oram.mem[0x60 + (which<<2)+1];
+ if (((sy + sl - (which<3))&0xff) == 0xff) {
+ if (which != 7)
+ shell = which;
+ else
+ missile = which;
+ }
+ }
+ for (var which of [shell,missile]) {
+ if (which != 0xff) {
+ var sx = 255 - oram.mem[0x60 + (which<<2)+3];
+ var outi = pixofs+sx;
+ var col = which == 7 ? 0xffffff00 : 0xffffffff;
+ pixels[outi++] = col;
+ pixels[outi++] = col;
+ pixels[outi++] = col;
+ pixels[outi++] = col;
+ }
+ }
+ // draw stars
+ if (starsEnabled) {
+ var starx = ((frameCounter + stars[sl & 0xff]) & 0xff);
+ if ((starx + sl) & 0x10) {
+ var outi = pixofs + starx;
+ if ((pixels[outi] & 0xffffff) == 0) {
+ pixels[outi] = palette[sl & 0x1f];
+ }
+ }
+ }
+ }
+
+ var KEYCODE_MAP = {
+ 32:{i:0,b:4}, // space bar (P1)
+ 37:{i:0,b:2}, // left arrow (P1)
+ 39:{i:0,b:3}, // right arrow (P1)
+ 0x53:{i:1,b:4}, // S (P2)
+ 0x41:{i:1,b:2}, // A (P2)
+ 0x44:{i:1,b:3}, // D (P2)
+ 53:{i:0,b:0}, // 5
+ 49:{i:1,b:0}, // 1
+ 50:{i:1,b:1}, // 2
+ }
+
+ this.getPresets = function() {
+ return GALAXIAN_PRESETS;
+ }
+
+ this.start = function() {
+ ram = new RAM(0x400);
+ vram = new RAM(0x400);
+ oram = new RAM(0x100);
+ outlatches = new RAM(0x8);
+ membus = {
+ read: function(address) {
+ if (address < 0x4000) {
+ return (rom ? rom[address] : 0) & 0xff;
+ } else if (address < 0x4800) {
+ address &= 0x3ff;
+ return ram.mem[address] & 0xff;
+ } else if (address >= 0x5000 && address < 0x5800) {
+ address &= 0x3ff;
+ return vram.mem[address] & 0xff;
+ } else if (address >= 0x5800 && address < 0x6000) {
+ address &= 0xff;
+ return oram.mem[address] & 0xff;
+ } else if (address >= 0x6000 && address < 0x6800) {
+ address &= 0x7;
+ switch (address) {
+ case 0:
+ return inputs[0];
+ }
+ } else if (address >= 0x6800 && address < 0x7000) {
+ return inputs[1];
+ } else if (address >= 0x7000 && address < 0x7800) {
+ address &= 0x7;
+ switch (address) {
+ case 0:
+ return inputs[2];
+ }
+ } else if (address >= 0x7800 && address < 0x8000) {
+ watchdog_counter = INITIAL_WATCHDOG;
+ } else {
+ console.log("read", hex(address));
+ return 0;
+ }
+ },
+ write: function(address, value) {
+ //console.log("write", hex(address,4), hex(value,2));
+ if (address >= 0x4000 && address < 0x4800) {
+ address &= 0x3ff;
+ ram.mem[address] = value;
+ } else if (address >= 0x5000 && address < 0x5800) {
+ address &= 0x3ff;
+ vram.mem[address] = value;
+ } else if (address >= 0x5800 && address < 0x6000) {
+ address &= 0xff;
+ oram.mem[address] = value;
+ } else if (address >= 0x6000 && address < 0x6800) {
+ address &= 0x7;
+ outlatches.mem[address] = value;
+ } else if (address >= 0x6800 && address < 0x7000) {
+ address &= 0x7;
+ // TODO: sound
+ } else if (address >= 0x7000 && address < 0x7800) {
+ address &= 0x7;
+ switch (address) {
+ case 1:
+ interruptEnabled = value;
+ break;
+ case 4:
+ starsEnabled = value;
+ break;
+ }
+ } else if (address >= 0x7800 && address < 0x8000) {
+ // TODO: sound
+ } else {
+ console.log("write", hex(address), hex(value));
+ }
+ },
+ isContended: function() { return false; },
+ };
+ iobus = {
+ read: function(addr) {
+ console.log('IO read', hex(addr,4));
+ return 0;
+ },
+ write: function(addr, val) {
+ console.log('IO write', hex(addr,4), hex(val,2));
+ }
+ };
+ cpu = window.Z80({
+ display: {},
+ memory: membus,
+ ioBus: iobus
+ });
+ video = new RasterVideo(mainElement,264,264,{rotate:90});
+ audio = new SampleAudio(cpuFrequency);
+ video.create();
+ var idata = video.getFrameData();
+ video.setKeyboardEvents(function(key,code,flags) {
+ var o = KEYCODE_MAP[key];
+ if (o) {
+ if (flags & 1) {
+ inputs[o.i] |= (1<= 0) ;
+ var arr = new Uint8Array(new ArrayBuffer(l));
+ for (var i=0; i 0)
+ return;
worker.postMessage({code:text, platform:platform_id,
tool:platform.getToolForFilename(current_preset_id)});
}
@@ -328,6 +331,10 @@ function arrayCompare(a,b) {
}
worker.onmessage = function(e) {
+ if (pendingWorkerMessages > 1) {
+ setCode(editor.getValue());
+ }
+ pendingWorkerMessages = 0;
// errors?
var toolbar = $("#controls_top");
function addErrorMarker(line, msg) {