Simple Smartport (#31)

* Simple Smartport

* Block device support

* turn off verbose debugging

* rom cleanup

* Turn off debugging, again

* Turn off debugging, again, again
This commit is contained in:
Will Scullin 2020-09-12 19:42:18 -07:00 committed by GitHub
parent 7f9ad9b9a1
commit ab05e99d81
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 209 additions and 83 deletions

View File

@ -13,6 +13,10 @@ indent_size = 4
[*.md] [*.md]
trim_trailing_whitespace = true trim_trailing_whitespace = true
[*.s]
indent_size = 8
indent_style = tab
[Makefile] [Makefile]
indent_style = tab indent_style = tab
indent_size = 8 indent_size = 8

83
asm/smartport.s Normal file
View File

@ -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

View File

@ -27,6 +27,7 @@ export const DISK_TYPES = [
'd13', 'd13',
'do', 'do',
'dsk', 'dsk',
'hdv',
'po', 'po',
'nib', 'nib',
'woz' 'woz'

View File

@ -11,40 +11,28 @@
import { base64_decode } from '../base64'; import { base64_decode } from '../base64';
import { debug, toHex } from '../util'; 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 = []; var disks = [];
function _init() {
if (options && options.block) {
rom[0x07] = 0x3C;
debug('DumbPort card');
} else {
debug('SmartPort card');
}
}
function decodeDisk(unit, disk) { function decodeDisk(unit, disk) {
disks[unit] = []; disks[unit] = [];
for (var idx = 0; idx < disk.blocks.length; idx++) { 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() { function Address() {
var lo; var lo;
var hi; var hi;
@ -173,9 +165,9 @@ export default function SmartPort(io, cpu) {
*/ */
function readBlock(state, drive, block, buffer) { function readBlock(state, drive, block, buffer) {
debug('read drive=' + drive); _debug('read drive=' + drive);
debug('read buffer=' + buffer); _debug('read buffer=' + buffer);
debug('read block=$' + toHex(block)); _debug('read block=$' + toHex(block));
if (!disks[drive] || !disks[drive].length) { if (!disks[drive] || !disks[drive].length) {
debug('Drive', drive, 'is empty'); debug('Drive', drive, 'is empty');
@ -198,9 +190,9 @@ export default function SmartPort(io, cpu) {
*/ */
function writeBlock(state, drive, block, buffer) { function writeBlock(state, drive, block, buffer) {
debug('write drive=' + drive); _debug('write drive=' + drive);
debug('write buffer=' + buffer); _debug('write buffer=' + buffer);
debug('write block=$' + toHex(block)); _debug('write block=$' + toHex(block));
if (!disks[drive] || !disks[drive].length) { if (!disks[drive] || !disks[drive].length) {
debug('Drive', drive, 'is empty'); debug('Drive', drive, 'is empty');
@ -233,37 +225,64 @@ export default function SmartPort(io, cpu) {
state.s &= 0xfe; 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 * Interface
*/ */
return { return {
read: function(page, off, debugFlag) { ioSwitch: function (off, val) {
return _access(off, val);
},
read: function(page, off) {
var state = cpu.getState(); var state = cpu.getState();
var cmd; var cmd;
var unit; var unit;
var buffer; var buffer;
var block; var block;
var blockOff = rom[0xff];
var smartOff = blockOff + 3;
if (!debugFlag) { if (off === blockOff && cpu.sync()) { // Regular block device entry POINT
debug('read $' + toHex(page) + toHex(off) + '=$' + toHex(ROM[off]), cpu.sync()); _debug('block device entry');
} cmd = cpu.read(0x00, COMMAND);
unit = cpu.read(0x00, UNIT);
if (off == 0x00 && cpu.sync()) { var bufferAddr = new Address(ADDRESS_LO);
readBlock(state, 1, 0, new Address(0x0800)); var blockAddr = new Address(BLOCK_LO);
} 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;
var drive = (unit & 0x80) ? 2 : 1; var drive = (unit & 0x80) ? 2 : 1;
var driveSlot = (unit & 0x70) >> 4; var driveSlot = (unit & 0x70) >> 4;
debug('cmd=' + cmd); buffer = bufferAddr.readAddress();
debug('unit=$' + toHex(unit)); 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) { switch (cmd) {
case 0: // INFO case 0: // INFO
@ -271,41 +290,32 @@ export default function SmartPort(io, cpu) {
break; break;
case 1: // READ case 1: // READ
bufferAddr = new Address(0x44);
buffer = bufferAddr.readAddress();
blockAddr = new Address(0x46);
block = blockAddr.readWord();
readBlock(state, drive, block, buffer); readBlock(state, drive, block, buffer);
break; break;
case 2: // WRITE case 2: // WRITE
bufferAddr = new Address(0x44);
buffer = bufferAddr.readAddress();
blockAddr = new Address(0x46);
block = blockAddr.readWord();
writeBlock(state, drive, block, buffer); writeBlock(state, drive, block, buffer);
break; break;
case 3: // FORMAT case 3: // FORMAT
formatDevice(state, unit); formatDevice(state, unit);
break; break;
} }
} else if (off == 0x23 && cpu.sync()) { } else if (off == smartOff && cpu.sync()) {
debug('smartport entry'); _debug('smartport entry');
var retVal = {}; var retVal = {};
var stackAddr = new Address(state.sp + 1, 0x01); var stackAddr = new Address(state.sp + 1, 0x01);
retVal = stackAddr.readAddress(); retVal = stackAddr.readAddress();
debug('return=' + retVal); _debug('return=' + retVal);
var cmdBlockAddr = retVal.inc(1); var cmdBlockAddr = retVal.inc(1);
cmd = cmdBlockAddr.readByte(); cmd = cmdBlockAddr.readByte();
var cmdListAddr = cmdBlockAddr.inc(1).readAddress(); var cmdListAddr = cmdBlockAddr.inc(1).readAddress();
debug('cmd=' + cmd); _debug('cmd=' + cmd);
debug('cmdListAddr=' + cmdListAddr); _debug('cmdListAddr=' + cmdListAddr);
stackAddr.writeAddress(retVal.inc(3)); stackAddr.writeAddress(retVal.inc(3));
@ -314,13 +324,13 @@ export default function SmartPort(io, cpu) {
buffer = cmdListAddr.inc(2).readAddress(); buffer = cmdListAddr.inc(2).readAddress();
var status; var status;
debug('parameterCount=' + parameterCount); _debug('parameterCount=' + parameterCount);
switch (cmd) { switch (cmd) {
case 0x00: // INFO case 0x00: // INFO
status = cmdListAddr.inc(4).readByte(); status = cmdListAddr.inc(4).readByte();
debug('info unit=' + unit); _debug('info unit=' + unit);
debug('info buffer=' + buffer); _debug('info buffer=' + buffer);
debug('info status=' + status); _debug('info status=' + status);
switch (unit) { switch (unit) {
case 0: case 0:
switch (status) { switch (status) {
@ -396,7 +406,7 @@ export default function SmartPort(io, cpu) {
cpu.setState(state); cpu.setState(state);
return ROM[off]; return rom[off];
}, },
write: function() { write: function() {

View File

@ -4,10 +4,10 @@ import { driveLights, initUI, updateUI } from './ui/apple2';
import Printer from './ui/printer'; import Printer from './ui/printer';
import DiskII from './cards/disk2'; import DiskII from './cards/disk2';
import CFFA from './cards/cffa';
import LanguageCard from './cards/langcard'; import LanguageCard from './cards/langcard';
import Parallel from './cards/parallel'; import Parallel from './cards/parallel';
import RAMFactor from './cards/ramfactor'; import RAMFactor from './cards/ramfactor';
import SmartPort from './cards/smartport';
import Thunderclock from './cards/thunderclock'; import Thunderclock from './cards/thunderclock';
import VideoTerm from './cards/videoterm'; 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 slinky = new RAMFactor(io, 1024 * 1024);
var disk2 = new DiskII(io, driveLights, sectors); var disk2 = new DiskII(io, driveLights, sectors);
var clock = new Thunderclock(io); 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(0, lc);
io.setSlot(1, parallel); io.setSlot(1, parallel);
@ -113,5 +113,6 @@ io.setSlot(2, slinky);
io.setSlot(4, clock); io.setSlot(4, clock);
io.setSlot(3, videoTerm); io.setSlot(3, videoTerm);
io.setSlot(6, disk2); io.setSlot(6, disk2);
io.setSlot(7, smartport);
cpu.addPageHandler(lc); cpu.addPageHandler(lc);

View File

@ -3,10 +3,10 @@ import Prefs from './prefs';
import { driveLights, initUI, updateUI } from './ui/apple2'; import { driveLights, initUI, updateUI } from './ui/apple2';
import Printer from './ui/printer'; import Printer from './ui/printer';
import CFFA from './cards/cffa';
import DiskII from './cards/disk2'; import DiskII from './cards/disk2';
import Parallel from './cards/parallel'; import Parallel from './cards/parallel';
import RAMFactor from './cards/ramfactor'; import RAMFactor from './cards/ramfactor';
import SmartPort from './cards/smartport';
import Thunderclock from './cards/thunderclock'; import Thunderclock from './cards/thunderclock';
import apple2e_charset from './roms/apple2e_char'; import apple2e_charset from './roms/apple2e_char';
@ -76,6 +76,7 @@ if (canvas4) {
var apple2 = new Apple2(options); var apple2 = new Apple2(options);
var io = apple2.getIO(); var io = apple2.getIO();
var cpu = apple2.getCPU();
var printer = new Printer('#printer-modal .paper'); 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 slinky = new RAMFactor(io, 1024 * 1024);
var disk2 = new DiskII(io, driveLights); var disk2 = new DiskII(io, driveLights);
var clock = new Thunderclock(io); 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(1, parallel);
io.setSlot(2, slinky); io.setSlot(2, slinky);
io.setSlot(5, clock); io.setSlot(5, clock);
io.setSlot(6, disk2); io.setSlot(6, disk2);
io.setSlot(7, cffa); io.setSlot(7, smartport);

View File

@ -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,
];

View File

@ -34,7 +34,7 @@ var stats;
var vm; var vm;
var tape; var tape;
var _disk2; var _disk2;
var _cffa; var _smartPort;
var audio; var audio;
var keyboard; var keyboard;
var io; var io;
@ -260,7 +260,7 @@ function doLoadLocalDisk(drive, file) {
var ext = parts.pop().toLowerCase(); var ext = parts.pop().toLowerCase();
var name = parts.join('.'); var name = parts.join('.');
if (this.result.byteLength >= 800 * 1024) { 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); driveLights.label(drive, name);
focused = false; focused = false;
initGamepad(); initGamepad();
@ -321,7 +321,7 @@ export function doLoadHTTP(drive, _url) {
var ext = fileParts.pop().toLowerCase(); var ext = fileParts.pop().toLowerCase();
var name = decodeURIComponent(fileParts.join('.')); var name = decodeURIComponent(fileParts.join('.'));
if (data.byteLength >= 800 * 1024) { if (data.byteLength >= 800 * 1024) {
if (_cffa.setBinary(drive, name, ext, data)) { if (_smartPort.setBinary(drive, name, ext, data)) {
driveLights.label(drive, name); driveLights.label(drive, name);
initGamepad(); initGamepad();
} }
@ -769,7 +769,7 @@ export function openPrinterModal() {
MicroModal.show('printer-modal'); MicroModal.show('printer-modal');
} }
export function initUI(apple2, disk2, cffa, e) { export function initUI(apple2, disk2, smartPort, e) {
_apple2 = apple2; _apple2 = apple2;
cpu = _apple2.getCPU(); cpu = _apple2.getCPU();
io = _apple2.getIO(); io = _apple2.getIO();
@ -777,7 +777,7 @@ export function initUI(apple2, disk2, cffa, e) {
vm = apple2.getVideoModes(); vm = apple2.getVideoModes();
tape = new Tape(io); tape = new Tape(io);
_disk2 = disk2; _disk2 = disk2;
_cffa = cffa; _smartPort = smartPort;
keyboard = new KeyBoard(cpu, io, e); keyboard = new KeyBoard(cpu, io, e);
keyboard.create('#keyboard'); keyboard.create('#keyboard');