From 43ac950a52c8530e9c03cf0ba971c1822cd8c7f8 Mon Sep 17 00:00:00 2001 From: Steven Hugg Date: Mon, 19 Nov 2018 09:10:13 -0500 Subject: [PATCH] added js coleco platform --- index.html | 1 + presets/nes/ex1.asm | 37 +++---- src/audio.ts | 17 ++- src/platform/coleco.ts | 233 ++++++++++++++++++++++++++++++++++++++- src/video/tms9918a.ts | 15 ++- src/worker/workermain.ts | 10 +- 6 files changed, 278 insertions(+), 35 deletions(-) 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",