From ab05e99d811cec563fdd5ce4ce667b4b1b79f691 Mon Sep 17 00:00:00 2001 From: Will Scullin Date: Sat, 12 Sep 2020 19:42:18 -0700 Subject: [PATCH] Simple Smartport (#31) * Simple Smartport * Block device support * turn off verbose debugging * rom cleanup * Turn off debugging, again * Turn off debugging, again, again --- .editorconfig | 4 + asm/smartport.s | 83 ++++++++++++++++++++ js/cards/disk2.js | 1 + js/cards/smartport.js | 152 ++++++++++++++++++++----------------- js/main2.js | 7 +- js/main2e.js | 9 ++- js/roms/cards/smartport.js | 26 +++++++ js/ui/apple2.js | 10 +-- 8 files changed, 209 insertions(+), 83 deletions(-) create mode 100644 asm/smartport.s create mode 100644 js/roms/cards/smartport.js diff --git a/.editorconfig b/.editorconfig index 07dad77..938c39e 100644 --- a/.editorconfig +++ b/.editorconfig @@ -13,6 +13,10 @@ indent_size = 4 [*.md] trim_trailing_whitespace = true +[*.s] +indent_size = 8 +indent_style = tab + [Makefile] indent_style = tab indent_size = 8 diff --git a/asm/smartport.s b/asm/smartport.s new file mode 100644 index 0000000..9af5b6f --- /dev/null +++ b/asm/smartport.s @@ -0,0 +1,83 @@ + ORG $C700 + +; Slot scan ZP addresses +SCAN_LO EQU $00 +SCAN_HI EQU $01 + +; ProDOS +COMMAND EQU $42 +UNIT EQU $43 +ADDRESS_LO EQU $44 +ADDRESS_HI EQU $45 +BLOCK_LO EQU $46 +BLOCK_HI EQU $47 + +MSLOT EQU $7F8 + +; Slot I/O addresses +STATUS EQU $C080 + +; ROM addresses +BASIC EQU $E000 +SLOOP EQU $FABA ; Resume boot scan +ROMRTS EQU $FF58 +BOOT EQU $0801 + + LDX #$20 ; $20 $00 $03 $00 - Smartport signature + LDX #$00 ; $20 $00 $03 $3C - Vanilla disk signature + LDX #$03 + LDX #$00 ; Override with $3C for DumbPort +; Determine our slot + JSR ROMRTS + TSX + LDA $0100,X + STA MSLOT ; $Cn + ASL + ASL + ASL + ASL + TAY ; $n0 +; Load the disk status bits + LDA STATUS,Y + LSR ; Check for Disk 1 + BCS DISKREADY ; Boot from Disk 1 + LDA SCAN_LO + BNE GO_BASIC + LDA SCAN_HI + CMP MSLOT + BNE GO_BASIC + JMP SLOOP ; Go back to scanning +GO_BASIC JMP BASIC ; Go to basic +; Boot routine +DISKREADY LDX #$01 ; Read + STX COMMAND + DEX + STX BLOCK_LO ; Block 0 + STX BLOCK_HI + STX ADDRESS_LO ; Into $800 + LDX #$08 + STX ADDRESS_HI + LDA MSLOT + PHA ; Save slot address + PHA ; RTS address hi byte + LDA #REENTRY - 1 + PHA ; RTS address lo byte + CLV + BVC BLOCK_ENT +REENTRY PLA ; Restore slot address + ASL ; Make I/O register index + ASL + ASL + ASL + TAX + JMP BOOT + DS 2 +BLOCK_ENT RTS + DS 2 +SMARTPOINT_ENT RTS +PADDING DS $C7FE - PADDING + ORG $C7FE +FLAGS DFB $D7 +ENTRY_LO DFB BLOCK_ENT + + END diff --git a/js/cards/disk2.js b/js/cards/disk2.js index c6f28e6..4b30038 100644 --- a/js/cards/disk2.js +++ b/js/cards/disk2.js @@ -27,6 +27,7 @@ export const DISK_TYPES = [ 'd13', 'do', 'dsk', + 'hdv', 'po', 'nib', 'woz' diff --git a/js/cards/smartport.js b/js/cards/smartport.js index ffc2d19..02029f4 100644 --- a/js/cards/smartport.js +++ b/js/cards/smartport.js @@ -11,40 +11,28 @@ import { base64_decode } from '../base64'; import { debug, toHex } from '../util'; +import { rom } from '../roms/cards/smartport'; -export default function SmartPort(io, cpu) { +export default function SmartPort(io, cpu, options ) { + var COMMAND = 0x42; + var UNIT = 0x43; + var ADDRESS_LO = 0x44; + // var ADDRESS_HI = 0x45; + var BLOCK_LO = 0x46; + // var BLOCK_HI = 0x47; - /* - $Cn01=$20 - $Cn03=$00 - $Cn05=$03 - $Cn07=$00 - */ - - var ROM = [ - 0xA2, 0x20, 0xA0, 0x00, 0xA2, 0x03, 0xA0, 0x3C, 0x20, 0x58, 0xFF, 0xBA, 0xBD, 0x00, 0x01, 0x0A, - 0x0A, 0x0A, 0x0A, 0xAA, 0x4C, 0x01, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x60, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xDF, 0x20 - ]; var disks = []; + function _init() { + if (options && options.block) { + rom[0x07] = 0x3C; + debug('DumbPort card'); + } else { + debug('SmartPort card'); + } + } + function decodeDisk(unit, disk) { disks[unit] = []; for (var idx = 0; idx < disk.blocks.length; idx++) { @@ -52,6 +40,10 @@ export default function SmartPort(io, cpu) { } } + function _debug() { + // debug.apply(this, arguments); + } + function Address() { var lo; var hi; @@ -173,9 +165,9 @@ export default function SmartPort(io, cpu) { */ function readBlock(state, drive, block, buffer) { - debug('read drive=' + drive); - debug('read buffer=' + buffer); - debug('read block=$' + toHex(block)); + _debug('read drive=' + drive); + _debug('read buffer=' + buffer); + _debug('read block=$' + toHex(block)); if (!disks[drive] || !disks[drive].length) { debug('Drive', drive, 'is empty'); @@ -198,9 +190,9 @@ export default function SmartPort(io, cpu) { */ function writeBlock(state, drive, block, buffer) { - debug('write drive=' + drive); - debug('write buffer=' + buffer); - debug('write block=$' + toHex(block)); + _debug('write drive=' + drive); + _debug('write buffer=' + buffer); + _debug('write block=$' + toHex(block)); if (!disks[drive] || !disks[drive].length) { debug('Drive', drive, 'is empty'); @@ -233,37 +225,64 @@ export default function SmartPort(io, cpu) { state.s &= 0xfe; } + function _access(off, val) { + var result; + var readMode = val === undefined; + + switch (off & 0x8f) { + case 0x80: + if (readMode) { + result = 0; + for (var idx = 0; idx < disks.length; idx++) { + result <<= 1; + if (disks[idx]) { + result |= 0x01; + } + } + } + break; + } + + return result; + } + + _init(); + /* * Interface */ return { - read: function(page, off, debugFlag) { + ioSwitch: function (off, val) { + return _access(off, val); + }, + + read: function(page, off) { var state = cpu.getState(); var cmd; var unit; var buffer; var block; + var blockOff = rom[0xff]; + var smartOff = blockOff + 3; - if (!debugFlag) { - debug('read $' + toHex(page) + toHex(off) + '=$' + toHex(ROM[off]), cpu.sync()); - } - - if (off == 0x00 && cpu.sync()) { - readBlock(state, 1, 0, new Address(0x0800)); - } else if (off == 0x20 && cpu.sync()) { // Regular block device entry POINT - debug('block device entry'); - cmd = cpu.read(0x00, 0x42); - unit = cpu.read(0x00, 0x43); - var bufferAddr; - var blockAddr; + if (off === blockOff && cpu.sync()) { // Regular block device entry POINT + _debug('block device entry'); + cmd = cpu.read(0x00, COMMAND); + unit = cpu.read(0x00, UNIT); + var bufferAddr = new Address(ADDRESS_LO); + var blockAddr = new Address(BLOCK_LO); var drive = (unit & 0x80) ? 2 : 1; var driveSlot = (unit & 0x70) >> 4; - debug('cmd=' + cmd); - debug('unit=$' + toHex(unit)); + buffer = bufferAddr.readAddress(); + block = blockAddr.readWord(); - debug('slot=' + driveSlot + ' drive=' + drive); + _debug('cmd=' + cmd); + _debug('unit=$' + toHex(unit)); + + _debug('slot=' + driveSlot + ' drive=' + drive); + _debug('buffer=' + toHex(buffer) + ' block=' + toHex(block)); switch (cmd) { case 0: // INFO @@ -271,41 +290,32 @@ export default function SmartPort(io, cpu) { break; case 1: // READ - bufferAddr = new Address(0x44); - buffer = bufferAddr.readAddress(); - blockAddr = new Address(0x46); - block = blockAddr.readWord(); - readBlock(state, drive, block, buffer); break; case 2: // WRITE - bufferAddr = new Address(0x44); - buffer = bufferAddr.readAddress(); - blockAddr = new Address(0x46); - block = blockAddr.readWord(); - writeBlock(state, drive, block, buffer); break; + case 3: // FORMAT formatDevice(state, unit); break; } - } else if (off == 0x23 && cpu.sync()) { - debug('smartport entry'); + } else if (off == smartOff && cpu.sync()) { + _debug('smartport entry'); var retVal = {}; var stackAddr = new Address(state.sp + 1, 0x01); retVal = stackAddr.readAddress(); - debug('return=' + retVal); + _debug('return=' + retVal); var cmdBlockAddr = retVal.inc(1); cmd = cmdBlockAddr.readByte(); var cmdListAddr = cmdBlockAddr.inc(1).readAddress(); - debug('cmd=' + cmd); - debug('cmdListAddr=' + cmdListAddr); + _debug('cmd=' + cmd); + _debug('cmdListAddr=' + cmdListAddr); stackAddr.writeAddress(retVal.inc(3)); @@ -314,13 +324,13 @@ export default function SmartPort(io, cpu) { buffer = cmdListAddr.inc(2).readAddress(); var status; - debug('parameterCount=' + parameterCount); + _debug('parameterCount=' + parameterCount); switch (cmd) { case 0x00: // INFO status = cmdListAddr.inc(4).readByte(); - debug('info unit=' + unit); - debug('info buffer=' + buffer); - debug('info status=' + status); + _debug('info unit=' + unit); + _debug('info buffer=' + buffer); + _debug('info status=' + status); switch (unit) { case 0: switch (status) { @@ -396,7 +406,7 @@ export default function SmartPort(io, cpu) { cpu.setState(state); - return ROM[off]; + return rom[off]; }, write: function() { diff --git a/js/main2.js b/js/main2.js index 215b310..768aca4 100644 --- a/js/main2.js +++ b/js/main2.js @@ -4,10 +4,10 @@ import { driveLights, initUI, updateUI } from './ui/apple2'; import Printer from './ui/printer'; import DiskII from './cards/disk2'; -import CFFA from './cards/cffa'; import LanguageCard from './cards/langcard'; import Parallel from './cards/parallel'; import RAMFactor from './cards/ramfactor'; +import SmartPort from './cards/smartport'; import Thunderclock from './cards/thunderclock'; import VideoTerm from './cards/videoterm'; @@ -103,9 +103,9 @@ var videoTerm = new VideoTerm(io, options.screen[0]); var slinky = new RAMFactor(io, 1024 * 1024); var disk2 = new DiskII(io, driveLights, sectors); var clock = new Thunderclock(io); -var cffa = new CFFA(io); +var smartport = new SmartPort(io, cpu, { block: true }); -initUI(apple2, disk2, cffa, false); +initUI(apple2, disk2, smartport, false); io.setSlot(0, lc); io.setSlot(1, parallel); @@ -113,5 +113,6 @@ io.setSlot(2, slinky); io.setSlot(4, clock); io.setSlot(3, videoTerm); io.setSlot(6, disk2); +io.setSlot(7, smartport); cpu.addPageHandler(lc); diff --git a/js/main2e.js b/js/main2e.js index cf2e0a2..453ace5 100644 --- a/js/main2e.js +++ b/js/main2e.js @@ -3,10 +3,10 @@ import Prefs from './prefs'; import { driveLights, initUI, updateUI } from './ui/apple2'; import Printer from './ui/printer'; -import CFFA from './cards/cffa'; import DiskII from './cards/disk2'; import Parallel from './cards/parallel'; import RAMFactor from './cards/ramfactor'; +import SmartPort from './cards/smartport'; import Thunderclock from './cards/thunderclock'; import apple2e_charset from './roms/apple2e_char'; @@ -76,6 +76,7 @@ if (canvas4) { var apple2 = new Apple2(options); var io = apple2.getIO(); +var cpu = apple2.getCPU(); var printer = new Printer('#printer-modal .paper'); @@ -83,14 +84,14 @@ var parallel = new Parallel(io, printer); var slinky = new RAMFactor(io, 1024 * 1024); var disk2 = new DiskII(io, driveLights); var clock = new Thunderclock(io); -var cffa = new CFFA(io); +var smartport = new SmartPort(io, cpu); -initUI(apple2, disk2, cffa, options.e); +initUI(apple2, disk2, smartport, options.e); io.setSlot(1, parallel); io.setSlot(2, slinky); io.setSlot(5, clock); io.setSlot(6, disk2); -io.setSlot(7, cffa); +io.setSlot(7, smartport); diff --git a/js/roms/cards/smartport.js b/js/roms/cards/smartport.js new file mode 100644 index 0000000..12cc161 --- /dev/null +++ b/js/roms/cards/smartport.js @@ -0,0 +1,26 @@ + +/* + $Cn01=$20 + $Cn03=$00 + $Cn05=$03 + $Cn07=$00 Smartport / $3C Disk controller +*/ + +export var rom = [ + 0xa2,0x20,0xa2,0x00,0xa2,0x03,0xa2,0x00, 0x20,0x58,0xff,0xba,0xbd,0x00,0x01,0x8d, + 0xf8,0x07,0x0a,0x0a,0x0a,0x0a,0xa8,0xb9, 0x80,0xc0,0x4a,0xb0,0x11,0xa5,0x00,0xd0, + 0x0a,0xa5,0x01,0xcd,0xf8,0x07,0xd0,0x03, 0x4c,0xba,0xfa,0x4c,0x00,0xe0,0xa2,0x01, + 0x86,0x42,0xca,0x86,0x46,0x86,0x47,0x86, 0x44,0xa2,0x08,0x86,0x45,0xad,0xf8,0x07, + 0x48,0x48,0xa9,0x47,0x48,0xb8,0x50,0x0b, 0x68,0x0a,0x0a,0x0a,0x0a,0xaa,0x4c,0x01, + 0x08,0x00,0x00,0x60,0x00,0x00,0x60,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0xd7,0x53, +]; diff --git a/js/ui/apple2.js b/js/ui/apple2.js index ca107b4..d2830c2 100644 --- a/js/ui/apple2.js +++ b/js/ui/apple2.js @@ -34,7 +34,7 @@ var stats; var vm; var tape; var _disk2; -var _cffa; +var _smartPort; var audio; var keyboard; var io; @@ -260,7 +260,7 @@ function doLoadLocalDisk(drive, file) { var ext = parts.pop().toLowerCase(); var name = parts.join('.'); if (this.result.byteLength >= 800 * 1024) { - if (_cffa.setBinary(drive, name, ext, this.result)) { + if (_smartPort.setBinary(drive, name, ext, this.result)) { driveLights.label(drive, name); focused = false; initGamepad(); @@ -321,7 +321,7 @@ export function doLoadHTTP(drive, _url) { var ext = fileParts.pop().toLowerCase(); var name = decodeURIComponent(fileParts.join('.')); if (data.byteLength >= 800 * 1024) { - if (_cffa.setBinary(drive, name, ext, data)) { + if (_smartPort.setBinary(drive, name, ext, data)) { driveLights.label(drive, name); initGamepad(); } @@ -769,7 +769,7 @@ export function openPrinterModal() { MicroModal.show('printer-modal'); } -export function initUI(apple2, disk2, cffa, e) { +export function initUI(apple2, disk2, smartPort, e) { _apple2 = apple2; cpu = _apple2.getCPU(); io = _apple2.getIO(); @@ -777,7 +777,7 @@ export function initUI(apple2, disk2, cffa, e) { vm = apple2.getVideoModes(); tape = new Tape(io); _disk2 = disk2; - _cffa = cffa; + _smartPort = smartPort; keyboard = new KeyBoard(cpu, io, e); keyboard.create('#keyboard');