diff --git a/index.html b/index.html
index dd403a1d..bd8fe70a 100644
--- a/index.html
+++ b/index.html
@@ -320,6 +320,7 @@ function require(modname) {
+
diff --git a/presets/nes/ex1.asm b/presets/nes/ex1.asm
index 2da8c6bc..90fc13d9 100644
--- a/presets/nes/ex1.asm
+++ b/presets/nes/ex1.asm
@@ -1,4 +1,4 @@
-
+
include "nesdefs.asm"
;;;;; VARIABLES
@@ -26,9 +26,9 @@ Start:
sta PPU_ADDR ; PPU addr = $0000
sta PPU_SCROLL
sta PPU_SCROLL ; scroll = $0000
- lda #$90
+ lda #CTRL_NMI
sta PPU_CTRL ; enable NMI
- lda #$1e
+ lda #MASK_SPR|MASK_BG|MASK_SPR_CLIP|MASK_BG_CLIP
sta PPU_MASK ; enable rendering
.endless
jmp .endless ; endless loop
@@ -38,15 +38,15 @@ FillVRAM: subroutine
txa
ldy #$20
sty PPU_ADDR
- sta PPU_ADDR
- ldy #$10
+ sta PPU_ADDR ; PPU addr = $2000
+ ldy #$10 ; $10 (16) 256-byte pages
.loop:
- sta PPU_DATA
- adc #7
+ sta PPU_DATA ; write to VRAM
+ adc #7 ; add 7 to make randomish pattern
inx
bne .loop
- dey
- bne .loop
+ dey ; next page
+ bne .loop ; out of pages?
rts
; set palette colors
@@ -54,14 +54,14 @@ SetPalette: subroutine
ldy #$00
lda #$3f
sta PPU_ADDR
- sty PPU_ADDR
- ldx #32
+ sty PPU_ADDR ; PPU addr = 0x3f00
+ ldx #32 ; 32 palette colors
.loop:
- lda Palette,y
- sta PPU_DATA
+ lda Palette,y ; load from ROM
+ sta PPU_DATA ; store to palette
iny
dex
- bne .loop
+ bne .loop ; loop until 32 colors stored
rts
@@ -77,14 +77,9 @@ NMIHandler:
; update scroll position (must be done after VRAM updates)
inc ScrollPos
lda ScrollPos
- sta PPU_SCROLL
+ sta PPU_SCROLL ; X scroll position
lda #0
- sta PPU_SCROLL
-; TODO: write high bits to PPUCTRL
- lda ScrollPos
- and #0
- ora #$90 ; enable NMI
- sta PPU_CTRL
+ sta PPU_SCROLL ; Y scroll position
; reload registers
pla ; reload A
rti
diff --git a/src/audio.ts b/src/audio.ts
index 6ca18bc7..999be8d0 100644
--- a/src/audio.ts
+++ b/src/audio.ts
@@ -19,7 +19,7 @@ export class AY38910_Audio {
master : MasterAudio;
psg = new PsgDeviceChannel();
curreg = 0;
-
+
constructor(master : MasterAudio) {
this.master = master;
this.psg.setMode(PsgDeviceChannel.MODE_SIGNED);
@@ -41,6 +41,21 @@ export class AY38910_Audio {
}
}
+export class SN76489_Audio {
+ master : MasterAudio;
+ psg = new PsgDeviceChannel();
+
+ constructor(master : MasterAudio) {
+ this.master = master;
+ this.psg.setMode(PsgDeviceChannel.MODE_SIGNED);
+ this.psg.setDevice(PsgDeviceChannel.DEVICE_SN76489);
+ master.master.addChannel(this.psg);
+ }
+ setData(val : number) {
+ this.psg.writeRegisterSN(0, val & 0xff);
+ }
+}
+
// https://en.wikipedia.org/wiki/POKEY
// https://user.xmission.com/~trevin/atari/pokey_regs.html
// http://krap.pl/mirrorz/atari/homepage.ntlworld.com/kryten_droid/Atari/800XL/atari_hw/pokey.htm
diff --git a/src/platform/coleco.ts b/src/platform/coleco.ts
index ba89b58b..e2387fa6 100644
--- a/src/platform/coleco.ts
+++ b/src/platform/coleco.ts
@@ -1,7 +1,10 @@
"use strict";
-import { Platform, BaseMAMEPlatform, getToolForFilename_z80 } from "../baseplatform";
-import { PLATFORMS } from "../emu";
+import { Platform, BaseMAMEPlatform, BaseZ80Platform, getToolForFilename_z80 } from "../baseplatform";
+import { PLATFORMS, RAM, newAddressDecoder, padBytes, noise, setKeyboardFromMap, AnimationTimer, RasterVideo, Keys, makeKeycodeMap } from "../emu";
+import { hex, lzgmini, stringToByteArray } from "../util";
+import { MasterAudio, SN76489_Audio } from "../audio";
+import { TMS9918A } from "../video/tms9918a";
// http://www.colecovision.eu/ColecoVision/development/tutorial1.shtml
// http://www.colecovision.eu/ColecoVision/development/libcv.shtml
@@ -31,6 +34,228 @@ var ColecoVision_PRESETS = [
{id:'platform.c', name:'Platform Game'},
];
+var COLECOVISION_KEYCODE_MAP = makeKeycodeMap([
+ [Keys.VK_UP, 0, 0x1],
+ [Keys.VK_DOWN, 0, 0x4],
+ [Keys.VK_LEFT, 0, 0x8],
+ [Keys.VK_RIGHT, 0, 0x2],
+ [Keys.VK_SPACE, 0, 0x40],
+ [Keys.VK_CONTROL, 1, 0x40],
+
+ [Keys.VK_W, 2, 0x1],
+ [Keys.VK_S, 2, 0x4],
+ [Keys.VK_A, 2, 0x8],
+ [Keys.VK_D, 2, 0x2],
+ [Keys.VK_Z, 2, 0x40],
+ [Keys.VK_X, 3, 0x40],
+]);
+
+/// standard emulator
+
+const _ColecoVisionPlatform = function(mainElement) {
+
+ const cpuFrequency = 3579545; // MHz
+ const canvasWidth = 304;
+ const numTotalScanlines = 262;
+ const numVisibleScanlines = 240;
+ const cpuCyclesPerLine = Math.round(cpuFrequency / 60 / numTotalScanlines);
+
+ var cpu, ram, membus, iobus, rom, bios;
+ var video, vdp, timer;
+ var audio, psg;
+ var inputs = new Uint8Array(4);
+ var keypadMode = false;
+
+ class ColecoVisionPlatform extends BaseZ80Platform implements Platform {
+
+ getPresets() { return ColecoVision_PRESETS; }
+ getToolForFilename = getToolForFilename_z80;
+ getDefaultExtension() { return ".c"; };
+
+ start() {
+ ram = new RAM(1024);
+ bios = new lzgmini().decode(stringToByteArray(atob(COLECO_BIOS_LZG)));
+ membus = {
+ read: newAddressDecoder([
+ [0x0000, 0x1fff, 0x1fff, function(a) { return bios ? bios[a] : 0; }],
+ [0x6000, 0x7fff, 0x3ff, function(a) { return ram.mem[a]; }],
+ [0x8000, 0xffff, 0x7fff, function(a) { return rom ? rom[a] : 0; }],
+ ]),
+ write: newAddressDecoder([
+ [0x6000, 0x7fff, 0x3ff, function(a,v) { ram.mem[a] = v; }],
+ ]),
+ isContended: function() { return false; },
+ };
+ iobus = {
+ read: function(addr) {
+ addr &= 0xff;
+ //console.log('IO read', hex(addr,4));
+ switch (addr) {
+ case 0xfc: return inputs[keypadMode?1:0] ^ 0xff;
+ case 0xff: return inputs[keypadMode?3:2] ^ 0xff;
+ }
+ if (addr >= 0xa0 && addr <= 0xbf) {
+ if (addr & 1)
+ return vdp.readStatus();
+ else
+ return vdp.readData();
+ }
+ return 0;
+ },
+ write: function(addr, val) {
+ addr &= 0xff;
+ val &= 0xff;
+ //console.log('IO write', hex(addr,4), hex(val,2));
+ switch (addr >> 4) {
+ case 0x8: case 0x9: keypadMode = true; break;
+ case 0xc: case 0xd: keypadMode = false; break;
+ case 0xa: case 0xb:
+ if (addr & 1)
+ return vdp.writeAddress(val);
+ else
+ return vdp.writeData(val);
+ case 0xf: psg.setData(val); break;
+ }
+ }
+ };
+ cpu = this.newCPU(membus, iobus);
+ video = new RasterVideo(mainElement,canvasWidth,numVisibleScanlines);
+ video.create();
+ audio = new MasterAudio();
+ psg = new SN76489_Audio(audio);
+ var cru = {
+ setVDPInterrupt: (b) => {
+ if (b) {
+ cpu.nonMaskableInterrupt();
+ } else {
+ // TODO: reset interrupt?
+ }
+ }
+ };
+ vdp = new TMS9918A(video.canvas, cru, false);
+ setKeyboardFromMap(video, inputs, COLECOVISION_KEYCODE_MAP);
+ timer = new AnimationTimer(60, this.nextFrame.bind(this));
+ }
+
+ readAddress(addr) {
+ return membus.read(addr);
+ }
+
+ advance(novideo : boolean) {
+ for (var sl=0; sl0 doesn't seem to work
@@ -182,6 +182,8 @@ var PLATFORM_PARAMS = {
},
};
+PLATFORM_PARAMS['coleco.mame'] = PLATFORM_PARAMS['coleco'];
+
var _t1;
function starttime() { _t1 = new Date(); }
function endtime(msg) { var _t2 = new Date(); console.log(msg, _t2.getTime() - _t1.getTime(), "ms"); }
@@ -1025,7 +1027,7 @@ function linkSDLDZ80(step:BuildStep)
setupFS(FS, 'sdcc');
populateFiles(step, FS);
// TODO: coleco hack so that -u flag works
- if (step.platform == "coleco") {
+ if (step.platform.startsWith("coleco")) {
FS.writeFile('crt0.rel', FS.readFile('/share/lib/coleco/crt0.rel', {encoding:'utf8'}));
FS.writeFile('crt0.lst', '\n'); // TODO: needed so -u flag works
}
@@ -1046,7 +1048,7 @@ function linkSDLDZ80(step:BuildStep)
// return unchanged if no files changed
if (!anyTargetChanged(step, ["main.ihx", "main.noi"]))
return;
-
+
var listings = {};
for (var fn of step.files) {
if (fn.endsWith('.lst')) {
@@ -1161,7 +1163,7 @@ function preprocessMCPP(step:BuildStep) {
// TODO: make configurable by other compilers
var args = [
"-D", "__8BITWORKSHOP__",
- "-D", platform.toUpperCase().replace('-','_'),
+ "-D", platform.toUpperCase().replace(/[-.]/g,'_'),
"-D", "__SDCC_z80",
"-I", "/share/include",
"-Q",