mirror of
https://github.com/whscullin/apple2js.git
synced 2024-01-12 14:14:38 +00:00
Port language card and smartport to TypeScript (#62)
Port language card and smartport to TypeScript.
This commit is contained in:
parent
bf483e1178
commit
a3c7dd136b
@ -12,7 +12,7 @@
|
||||
import { base64_decode, base64_encode} from '../base64';
|
||||
import { bit, byte, Card, DiskFormat, MemberOf, memory, nibble, rom } from '../types';
|
||||
import { debug, toHex } from '../util';
|
||||
import { Disk, jsonDecode, jsonEncode, readSector } from '../formats/format_utils';
|
||||
import { Disk, JSONDisk, jsonDecode, jsonEncode, readSector } from '../formats/format_utils';
|
||||
|
||||
import { BOOTSTRAP_ROM_16, BOOTSTRAP_ROM_13 } from '../roms/cards/disk2';
|
||||
|
||||
@ -337,7 +337,7 @@ export default class DiskII implements Card {
|
||||
this.init();
|
||||
}
|
||||
|
||||
private debug(..._args: any) {
|
||||
private debug(..._args: any[]) {
|
||||
// debug.apply(this, arguments);
|
||||
}
|
||||
|
||||
@ -728,8 +728,8 @@ export default class DiskII implements Card {
|
||||
|
||||
/** Sets the data for `drive` from `disk`, which is expected to be JSON. */
|
||||
// TODO(flan): This implementation is not very safe.
|
||||
setDisk(drive: DriveNumber, disk: any) {
|
||||
const fmt = disk.type as DiskFormat;
|
||||
setDisk(drive: DriveNumber, disk: JSONDisk) {
|
||||
const fmt = disk.type;
|
||||
const readOnly = disk.readOnly;
|
||||
const name = disk.name;
|
||||
|
||||
@ -795,7 +795,7 @@ export default class DiskII implements Card {
|
||||
return jsonEncode(cur, pretty);
|
||||
}
|
||||
|
||||
setJSON(drive: DriveNumber, data: any) {
|
||||
setJSON(drive: DriveNumber, data: string) {
|
||||
const cur = this.drives[drive - 1];
|
||||
Object.assign(cur, jsonDecode(data));
|
||||
return true;
|
||||
|
@ -1,210 +0,0 @@
|
||||
/* Copyright 2010-2019 Will Scullin <scullin@scullinsteel.com>
|
||||
*
|
||||
* Permission to use, copy, modify, distribute, and sell this software and its
|
||||
* documentation for any purpose is hereby granted without fee, provided that
|
||||
* the above copyright notice appear in all copies and that both that
|
||||
* copyright notice and this permission notice appear in supporting
|
||||
* documentation. No representations are made about the suitability of this
|
||||
* software for any purpose. It is provided "as is" without express or
|
||||
* implied warranty.
|
||||
*/
|
||||
|
||||
import RAM from '../ram';
|
||||
import { debug } from '../util';
|
||||
|
||||
export default function LanguageCard(io, rom) {
|
||||
var _rom = rom;
|
||||
var _bank1 = null;
|
||||
var _bank2 = null;
|
||||
var _ram = null;
|
||||
|
||||
var _readbsr = false;
|
||||
var _writebsr = false;
|
||||
var _bsr2 = false;
|
||||
var _prewrite = false;
|
||||
|
||||
var _read1 = null;
|
||||
var _read2 = null;
|
||||
|
||||
var _write1 = null;
|
||||
var _write2 = null;
|
||||
|
||||
function _init() {
|
||||
debug('Language card');
|
||||
|
||||
_bank1 = new RAM(0xd0, 0xdf);
|
||||
_bank2 = new RAM(0xd0, 0xdf);
|
||||
_ram = new RAM(0xe0, 0xff);
|
||||
|
||||
_write1 = _rom;
|
||||
_write2 = _rom;
|
||||
|
||||
_read1 = _rom;
|
||||
_read2 = _rom;
|
||||
}
|
||||
|
||||
function _debug() {
|
||||
// debug.apply(null, arguments);
|
||||
}
|
||||
|
||||
_init();
|
||||
|
||||
var LOC = {
|
||||
// Bank 2
|
||||
READBSR2: 0x80,
|
||||
WRITEBSR2: 0x81,
|
||||
OFFBSR2: 0x82,
|
||||
READWRBSR2: 0x83,
|
||||
|
||||
// Bank 1
|
||||
READBSR1: 0x88,
|
||||
WRITEBSR1: 0x89,
|
||||
OFFBSR1: 0x8a,
|
||||
READWRBSR1: 0x8b,
|
||||
};
|
||||
|
||||
function _updateBanks() {
|
||||
if (_readbsr) {
|
||||
_read1 = _bsr2 ? _bank2 : _bank1;
|
||||
_read2 = _ram;
|
||||
} else {
|
||||
_read1 = _rom;
|
||||
_read2 = _rom;
|
||||
}
|
||||
|
||||
if (_writebsr) {
|
||||
_write1 = _bsr2 ? _bank2 : _bank1;
|
||||
_write2 = _ram;
|
||||
} else {
|
||||
_write1 = rom;
|
||||
_write2 = rom;
|
||||
}
|
||||
}
|
||||
|
||||
function _access(off, val) {
|
||||
var readMode = val === undefined;
|
||||
var result = 0;
|
||||
switch (off & 0x8B) {
|
||||
case LOC.READBSR2: // 0xC080
|
||||
_readbsr = true;
|
||||
_writebsr = false;
|
||||
_bsr2 = true;
|
||||
_prewrite = false;
|
||||
_debug('Bank 2 Read');
|
||||
break;
|
||||
case LOC.WRITEBSR2: // 0xC081
|
||||
_readbsr = false;
|
||||
if (readMode) {
|
||||
_writebsr = _prewrite;
|
||||
}
|
||||
_bsr2 = true;
|
||||
_prewrite = readMode;
|
||||
_debug('Bank 2 Write');
|
||||
break;
|
||||
case LOC.OFFBSR2: // 0xC082
|
||||
_readbsr = false;
|
||||
_writebsr = false;
|
||||
_bsr2 = true;
|
||||
_prewrite = false;
|
||||
_debug('Bank 2 Off');
|
||||
break;
|
||||
case LOC.READWRBSR2: // 0xC083
|
||||
_readbsr = true;
|
||||
if (readMode) {
|
||||
_writebsr = _prewrite;
|
||||
}
|
||||
_bsr2 = true;
|
||||
_prewrite = readMode;
|
||||
_debug('Bank 2 Read/Write');
|
||||
break;
|
||||
|
||||
case LOC.READBSR1: // 0xC088
|
||||
_readbsr = true;
|
||||
_writebsr = false;
|
||||
_bsr2 = false;
|
||||
_prewrite = false;
|
||||
_debug('Bank 1 Read');
|
||||
break;
|
||||
case LOC.WRITEBSR1: // 0xC089
|
||||
_readbsr = false;
|
||||
if (readMode) {
|
||||
_writebsr = _prewrite;
|
||||
}
|
||||
_bsr2 = false;
|
||||
_prewrite = readMode;
|
||||
_debug('Bank 1 Write');
|
||||
break;
|
||||
case LOC.OFFBSR1: // 0xC08A
|
||||
_readbsr = false;
|
||||
_writebsr = false;
|
||||
_bsr2 = false;
|
||||
_prewrite = false;
|
||||
_debug('Bank 1 Off');
|
||||
break;
|
||||
case LOC.READWRBSR1: // 0xC08B
|
||||
_readbsr = true;
|
||||
if (readMode) {
|
||||
_writebsr = _prewrite;
|
||||
}
|
||||
_bsr2 = false;
|
||||
_prewrite = readMode;
|
||||
_debug('Bank 1 Read/Write');
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
_updateBanks();
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
return {
|
||||
start: function() {
|
||||
return 0xd0;
|
||||
},
|
||||
end: function() {
|
||||
return 0xff;
|
||||
},
|
||||
ioSwitch: function(off, val) {
|
||||
return _access(off, val);
|
||||
},
|
||||
read: function(page, off) {
|
||||
var result = 0;
|
||||
if (page < 0xe0) {
|
||||
result = _read1.read(page, off);
|
||||
} else {
|
||||
result = _read2.read(page, off);
|
||||
}
|
||||
return result;
|
||||
},
|
||||
write: function(page, off, val) {
|
||||
if (page < 0xe0) {
|
||||
_write1.write(page, off, val);
|
||||
} else {
|
||||
_write2.write(page, off, val);
|
||||
}
|
||||
},
|
||||
getState: function() {
|
||||
return {
|
||||
readbsr: _readbsr,
|
||||
writebsr: _writebsr,
|
||||
bsr2: _bsr2,
|
||||
prewrite: _prewrite,
|
||||
ram: _ram.getState(),
|
||||
bank1: _bank1.getState(),
|
||||
bank2: _bank2.getState()
|
||||
};
|
||||
},
|
||||
setState: function(state) {
|
||||
_readbsr = state.readbsr;
|
||||
_writebsr = state.writebsr;
|
||||
_bsr2 = state.bsr2;
|
||||
_prewrite = state.prewrite;
|
||||
_ram.setState(state.ram);
|
||||
_bank1.setState(state.bank1);
|
||||
_bank2.setState(state.bank2);
|
||||
_updateBanks();
|
||||
}
|
||||
};
|
||||
}
|
194
js/cards/langcard.ts
Normal file
194
js/cards/langcard.ts
Normal file
@ -0,0 +1,194 @@
|
||||
/* Copyright 2010-2019 Will Scullin <scullin@scullinsteel.com>
|
||||
*
|
||||
* Permission to use, copy, modify, distribute, and sell this software and its
|
||||
* documentation for any purpose is hereby granted without fee, provided that
|
||||
* the above copyright notice appear in all copies and that both that
|
||||
* copyright notice and this permission notice appear in supporting
|
||||
* documentation. No representations are made about the suitability of this
|
||||
* software for any purpose. It is provided "as is" without express or
|
||||
* implied warranty.
|
||||
*/
|
||||
|
||||
import RAM, { RAMState } from '../ram';
|
||||
import { debug } from '../util';
|
||||
import { Card, Memory, byte, Restorable } from '../types';
|
||||
|
||||
export interface LanguageCardState {
|
||||
bank1: RAMState;
|
||||
bank2: RAMState;
|
||||
ram: RAMState;
|
||||
|
||||
readbsr: boolean;
|
||||
writebsr: boolean;
|
||||
bsr2: boolean;
|
||||
prewrite: boolean;
|
||||
}
|
||||
|
||||
export default class LanguageCard implements Card, Restorable<LanguageCardState> {
|
||||
private bank1: RAM;
|
||||
private bank2: RAM;
|
||||
private ram: RAM;
|
||||
|
||||
private readbsr = false;
|
||||
private writebsr = false;
|
||||
private bsr2 = false;
|
||||
private prewrite = false;
|
||||
|
||||
private read1: Memory;
|
||||
private read2: Memory;
|
||||
|
||||
private write1: Memory;
|
||||
private write2: Memory;
|
||||
|
||||
constructor(private rom: Memory) {
|
||||
debug('Language card');
|
||||
|
||||
this.bank1 = new RAM(0xd0, 0xdf);
|
||||
this.bank2 = new RAM(0xd0, 0xdf);
|
||||
this.ram = new RAM(0xe0, 0xff);
|
||||
|
||||
this.write1 = this.rom;
|
||||
this.write2 = this.rom;
|
||||
|
||||
this.read1 = this.rom;
|
||||
this.read2 = this.rom;
|
||||
}
|
||||
|
||||
private debug(...args: any[]) {
|
||||
debug.apply(null, args);
|
||||
}
|
||||
|
||||
private updateBanks() {
|
||||
if (this.readbsr) {
|
||||
this.read1 = this.bsr2 ? this.bank2 : this.bank1;
|
||||
this.read2 = this.ram;
|
||||
} else {
|
||||
this.read1 = this.rom;
|
||||
this.read2 = this.rom;
|
||||
}
|
||||
|
||||
if (this.writebsr) {
|
||||
this.write1 = this.bsr2 ? this.bank2 : this.bank1;
|
||||
this.write2 = this.ram;
|
||||
} else {
|
||||
this.write1 = this.rom;
|
||||
this.write2 = this.rom;
|
||||
}
|
||||
}
|
||||
|
||||
// Bank 2
|
||||
// READBSR2: 0x80
|
||||
// WRITEBSR2: 0x81
|
||||
// OFFBSR2: 0x82
|
||||
// READWRBSR2: 0x83
|
||||
|
||||
// Bank 1
|
||||
// READBSR1: 0x88
|
||||
// WRITEBSR1: 0x89
|
||||
// OFFBSR1: 0x8a
|
||||
// READWRBSR1: 0x8b
|
||||
|
||||
private access(off: byte, val?: byte) {
|
||||
const readMode = val === undefined;
|
||||
const result = readMode ? 0 : undefined;
|
||||
|
||||
const writeSwitch = off & 0x01;
|
||||
const offSwitch = off & 0x02;
|
||||
const bank1Switch = off & 0x08;
|
||||
|
||||
let bankStr;
|
||||
let rwStr;
|
||||
|
||||
if (writeSwitch) { // $C081, $C083, $C089, $C08B
|
||||
if (readMode) {
|
||||
this.writebsr = this.prewrite;
|
||||
}
|
||||
this.prewrite = readMode;
|
||||
|
||||
if (offSwitch) { // $C083, $C08B
|
||||
this.readbsr = true;
|
||||
rwStr = 'Read/Write';
|
||||
} else { // $C081, $C089
|
||||
this.readbsr = false;
|
||||
rwStr = 'Write';
|
||||
}
|
||||
} else { // $C080, $C082, $C088, $C08A
|
||||
this.writebsr = false;
|
||||
this.prewrite = false;
|
||||
|
||||
if (offSwitch) { // $C082, $C08A
|
||||
this.readbsr = false;
|
||||
rwStr = 'Off';
|
||||
} else { // $C080, $C088
|
||||
this.readbsr = true;
|
||||
rwStr = 'Read';
|
||||
}
|
||||
}
|
||||
|
||||
if (bank1Switch) { // C08[8-C]
|
||||
this.bsr2 = false;
|
||||
bankStr = 'Bank 1';
|
||||
} else { // C08[0-3]
|
||||
this.bsr2 = true;
|
||||
bankStr = 'Bank 2';
|
||||
}
|
||||
|
||||
this.debug(bankStr, rwStr);
|
||||
this.updateBanks();
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
start() {
|
||||
return 0xd0;
|
||||
}
|
||||
|
||||
end() {
|
||||
return 0xff;
|
||||
}
|
||||
|
||||
ioSwitch(off: byte, val?: byte) {
|
||||
return this.access(off, val);
|
||||
}
|
||||
|
||||
read(page: byte, off: byte): byte {
|
||||
let result: number = 0;
|
||||
if (page < 0xe0) {
|
||||
result = this.read1.read(page, off);
|
||||
} else {
|
||||
result = this.read2.read(page, off);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
write(page: byte, off: byte, val: byte) {
|
||||
if (page < 0xe0) {
|
||||
this.write1.write(page, off, val);
|
||||
} else {
|
||||
this.write2.write(page, off, val);
|
||||
}
|
||||
}
|
||||
|
||||
getState() {
|
||||
return {
|
||||
readbsr: this.readbsr,
|
||||
writebsr: this.writebsr,
|
||||
bsr2: this.bsr2,
|
||||
prewrite: this.prewrite,
|
||||
ram: this.ram.getState(),
|
||||
bank1: this.bank1.getState(),
|
||||
bank2: this.bank2.getState()
|
||||
};
|
||||
}
|
||||
|
||||
setState(state: LanguageCardState) {
|
||||
this.readbsr = state.readbsr;
|
||||
this.writebsr = state.writebsr;
|
||||
this.bsr2 = state.bsr2;
|
||||
this.prewrite = state.prewrite;
|
||||
this.ram.setState(state.ram);
|
||||
this.bank1.setState(state.bank1);
|
||||
this.bank2.setState(state.bank2);
|
||||
this.updateBanks();
|
||||
}
|
||||
}
|
@ -1,445 +0,0 @@
|
||||
/* Copyright 2010-2019 Will Scullin <scullin@scullinsteel.com>
|
||||
*
|
||||
* Permission to use, copy, modify, distribute, and sell this software and its
|
||||
* documentation for any purpose is hereby granted without fee, provided that
|
||||
* the above copyright notice appear in all copies and that both that
|
||||
* copyright notice and this permission notice appear in supporting
|
||||
* documentation. No representations are made about the suitability of this
|
||||
* software for any purpose. It is provided "as is" without express or
|
||||
* implied warranty.
|
||||
*/
|
||||
|
||||
import { debug, toHex } from '../util';
|
||||
import { rom } from '../roms/cards/smartport';
|
||||
|
||||
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;
|
||||
|
||||
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++) {
|
||||
disks[unit][idx] = new Uint8Array(disk.blocks[idx]);
|
||||
}
|
||||
}
|
||||
|
||||
function _debug() {
|
||||
// debug.apply(this, arguments);
|
||||
}
|
||||
|
||||
function Address() {
|
||||
var lo;
|
||||
var hi;
|
||||
|
||||
if (arguments.length == 1) {
|
||||
lo = arguments[0] & 0xff;
|
||||
hi = arguments[0] >> 8;
|
||||
} else if (arguments.length == 2) {
|
||||
lo = arguments[0];
|
||||
hi = arguments[1];
|
||||
}
|
||||
|
||||
return {
|
||||
loByte: function() {
|
||||
return lo;
|
||||
},
|
||||
|
||||
hiByte: function() {
|
||||
return hi;
|
||||
},
|
||||
|
||||
inc: function(val) {
|
||||
return new Address(((hi << 8 | lo) + val) & 0xffff);
|
||||
},
|
||||
|
||||
readByte: function() {
|
||||
return cpu.read(hi, lo);
|
||||
},
|
||||
|
||||
readWord: function() {
|
||||
var readLo = this.readByte();
|
||||
var readHi = this.inc(1).readByte();
|
||||
|
||||
return readHi << 8 | readLo;
|
||||
},
|
||||
|
||||
readAddress: function() {
|
||||
var readLo = this.readByte();
|
||||
var readHi = this.inc(1).readByte();
|
||||
|
||||
return new Address(readLo, readHi);
|
||||
},
|
||||
|
||||
writeByte: function(val) {
|
||||
cpu.write(hi, lo, val);
|
||||
},
|
||||
|
||||
writeWord: function(val) {
|
||||
this.writeByte(val & 0xff);
|
||||
this.inc(1).writeByte(val >> 8);
|
||||
},
|
||||
|
||||
writeAddress: function(val) {
|
||||
this.writeByte(val.loByte());
|
||||
this.inc(1).writeByte(val.hiByte());
|
||||
},
|
||||
|
||||
toString: function() {
|
||||
return '$' + toHex(hi) + toHex(lo);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/*
|
||||
* dumpBlock
|
||||
*/
|
||||
/*
|
||||
function dumpBlock(drive, block) {
|
||||
var result = '';
|
||||
var b;
|
||||
var jdx;
|
||||
|
||||
for (var idx = 0; idx < 32; idx++) {
|
||||
result += toHex(idx << 4, 4) + ': ';
|
||||
for (jdx = 0; jdx < 16; jdx++) {
|
||||
b = disks[drive][block][idx * 16 + jdx];
|
||||
if (jdx == 8) {
|
||||
result += ' ';
|
||||
}
|
||||
result += toHex(b) + ' ';
|
||||
}
|
||||
result += ' ';
|
||||
for (jdx = 0; jdx < 16; jdx++) {
|
||||
b = disks[drive][block][idx * 16 + jdx] & 0x7f;
|
||||
if (jdx == 8) {
|
||||
result += ' ';
|
||||
}
|
||||
if (b >= 0x20 && b < 0x7f) {
|
||||
result += String.fromCharCode(b);
|
||||
} else {
|
||||
result += '.';
|
||||
}
|
||||
}
|
||||
result += '\n';
|
||||
}
|
||||
return result;
|
||||
}
|
||||
*/
|
||||
/*
|
||||
* getDeviceInfo
|
||||
*/
|
||||
|
||||
function getDeviceInfo(state, drive) {
|
||||
if (disks[drive]) {
|
||||
var blocks = disks[drive].length;
|
||||
state.x = blocks & 0xff;
|
||||
state.y = blocks >> 8;
|
||||
|
||||
state.a = 0;
|
||||
state.s &= 0xfe;
|
||||
} else {
|
||||
state.a = 0x28;
|
||||
state.s |= 0x01;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* readBlock
|
||||
*/
|
||||
|
||||
function readBlock(state, drive, block, buffer) {
|
||||
_debug('read drive=' + drive);
|
||||
_debug('read buffer=' + buffer);
|
||||
_debug('read block=$' + toHex(block));
|
||||
|
||||
if (!disks[drive] || !disks[drive].length) {
|
||||
debug('Drive', drive, 'is empty');
|
||||
return;
|
||||
}
|
||||
|
||||
// debug('read', '\n' + dumpBlock(drive, block));
|
||||
|
||||
for (var idx = 0; idx < 512; idx++) {
|
||||
buffer.writeByte(disks[drive][block][idx]);
|
||||
buffer = buffer.inc(1);
|
||||
}
|
||||
|
||||
state.a = 0;
|
||||
state.s &= 0xfe;
|
||||
}
|
||||
|
||||
/*
|
||||
* writeBlock
|
||||
*/
|
||||
|
||||
function writeBlock(state, drive, block, buffer) {
|
||||
_debug('write drive=' + drive);
|
||||
_debug('write buffer=' + buffer);
|
||||
_debug('write block=$' + toHex(block));
|
||||
|
||||
if (!disks[drive] || !disks[drive].length) {
|
||||
debug('Drive', drive, 'is empty');
|
||||
return;
|
||||
}
|
||||
|
||||
// debug('write', '\n' + dumpBlock(drive, block));
|
||||
|
||||
for (var idx = 0; idx < 512; idx++) {
|
||||
disks[drive][block][idx] = buffer.readByte();
|
||||
buffer = buffer.inc(1);
|
||||
}
|
||||
state.a = 0;
|
||||
state.s &= 0xfe;
|
||||
}
|
||||
|
||||
/*
|
||||
* formatDevice
|
||||
*/
|
||||
|
||||
function formatDevice(state, drive) {
|
||||
for (var idx = 0; idx < disks[drive].length; idx++) {
|
||||
disks[drive][idx] = [];
|
||||
for (var jdx = 0; jdx < 512; jdx++) {
|
||||
disks[drive][idx][jdx] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
state.a = 0;
|
||||
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 {
|
||||
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 (off === blockOff && cpu.getSync()) { // 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;
|
||||
|
||||
buffer = bufferAddr.readAddress();
|
||||
block = blockAddr.readWord();
|
||||
|
||||
_debug('cmd=' + cmd);
|
||||
_debug('unit=$' + toHex(unit));
|
||||
|
||||
_debug('slot=' + driveSlot + ' drive=' + drive);
|
||||
_debug('buffer=' + toHex(buffer) + ' block=' + toHex(block));
|
||||
|
||||
switch (cmd) {
|
||||
case 0: // INFO
|
||||
getDeviceInfo(state, drive);
|
||||
break;
|
||||
|
||||
case 1: // READ
|
||||
readBlock(state, drive, block, buffer);
|
||||
break;
|
||||
|
||||
case 2: // WRITE
|
||||
writeBlock(state, drive, block, buffer);
|
||||
break;
|
||||
|
||||
case 3: // FORMAT
|
||||
formatDevice(state, unit);
|
||||
break;
|
||||
}
|
||||
} else if (off == smartOff && cpu.getSync()) {
|
||||
_debug('smartport entry');
|
||||
var retVal = {};
|
||||
var stackAddr = new Address(state.sp + 1, 0x01);
|
||||
|
||||
retVal = stackAddr.readAddress();
|
||||
|
||||
_debug('return=' + retVal);
|
||||
|
||||
var cmdBlockAddr = retVal.inc(1);
|
||||
cmd = cmdBlockAddr.readByte();
|
||||
var cmdListAddr = cmdBlockAddr.inc(1).readAddress();
|
||||
|
||||
_debug('cmd=' + cmd);
|
||||
_debug('cmdListAddr=' + cmdListAddr);
|
||||
|
||||
stackAddr.writeAddress(retVal.inc(3));
|
||||
|
||||
var parameterCount = cmdListAddr.readByte();
|
||||
unit = cmdListAddr.inc(1).readByte();
|
||||
buffer = cmdListAddr.inc(2).readAddress();
|
||||
var status;
|
||||
|
||||
_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);
|
||||
switch (unit) {
|
||||
case 0:
|
||||
switch (status) {
|
||||
case 0:
|
||||
buffer.writeByte(1); // one device
|
||||
buffer.inc(1).writeByte(1 << 6); // no interrupts
|
||||
buffer.inc(2).writeByte(0); // reserved
|
||||
buffer.inc(3).writeByte(0); // reserved
|
||||
buffer.inc(4).writeByte(0); // reserved
|
||||
buffer.inc(5).writeByte(0); // reserved
|
||||
buffer.inc(6).writeByte(0); // reserved
|
||||
buffer.inc(7).writeByte(0); // reserved
|
||||
state.x = 8;
|
||||
state.y = 0;
|
||||
state.a = 0;
|
||||
state.s &= 0xfe;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
default: // Unit 1
|
||||
switch (status) {
|
||||
case 0:
|
||||
var blocks = disks[unit].length;
|
||||
buffer.writeByte(0xf0); // W/R Block device in drive
|
||||
buffer.inc(1).writeByte(blocks & 0xff); // 1600 blocks
|
||||
buffer.inc(2).writeByte((blocks & 0xff00) >> 8);
|
||||
buffer.inc(3).writeByte((blocks & 0xff0000) >> 16);
|
||||
state.x = 4;
|
||||
state.y = 0;
|
||||
state.a = 0;
|
||||
state.s &= 0xfe;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
state.a = 0;
|
||||
state.s &= 0xfe;
|
||||
break;
|
||||
|
||||
case 0x01: // READ BLOCK
|
||||
block = cmdListAddr.inc(4).readWord();
|
||||
readBlock(state, unit, block, buffer);
|
||||
break;
|
||||
|
||||
case 0x02: // WRITE BLOCK
|
||||
block = cmdListAddr.inc(4).readWord();
|
||||
writeBlock(state, unit, block, buffer);
|
||||
break;
|
||||
|
||||
case 0x03: // FORMAT
|
||||
formatDevice(state, unit);
|
||||
break;
|
||||
|
||||
case 0x04: // CONTROL
|
||||
break;
|
||||
|
||||
case 0x05: // INIT
|
||||
break;
|
||||
|
||||
case 0x06: // OPEN
|
||||
break;
|
||||
|
||||
case 0x07: // CLOSE
|
||||
break;
|
||||
|
||||
case 0x08: // READ
|
||||
break;
|
||||
|
||||
case 0x09: // WRITE
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
cpu.setState(state);
|
||||
|
||||
return rom[off];
|
||||
},
|
||||
|
||||
write: function() {
|
||||
},
|
||||
|
||||
getState: function() {
|
||||
return {
|
||||
disks: disks.map(
|
||||
(disk) => disk.map(
|
||||
(block) => new Uint8Array(block)
|
||||
)
|
||||
)
|
||||
};
|
||||
},
|
||||
|
||||
setState: function(state) {
|
||||
disks = state.disks.map(
|
||||
(disk) => disk.map(
|
||||
(block) => new Uint8Array(block)
|
||||
)
|
||||
);
|
||||
},
|
||||
|
||||
setBinary: function (drive, name, fmt, data) {
|
||||
disks[drive] = [];
|
||||
if (fmt == '2mg') {
|
||||
data = data.slice(64);
|
||||
}
|
||||
for (var idx = 0; idx < data.byteLength; idx += 512) {
|
||||
disks[drive].push(new Uint8Array(data.slice(idx, idx + 512)));
|
||||
}
|
||||
},
|
||||
|
||||
setDisk: function(drive, json) {
|
||||
decodeDisk(drive, json);
|
||||
}
|
||||
};
|
||||
}
|
464
js/cards/smartport.ts
Normal file
464
js/cards/smartport.ts
Normal file
@ -0,0 +1,464 @@
|
||||
/* Copyright 2010-2019 Will Scullin <scullin@scullinsteel.com>
|
||||
*
|
||||
* Permission to use, copy, modify, distribute, and sell this software and its
|
||||
* documentation for any purpose is hereby granted without fee, provided that
|
||||
* the above copyright notice appear in all copies and that both that
|
||||
* copyright notice and this permission notice appear in supporting
|
||||
* documentation. No representations are made about the suitability of this
|
||||
* software for any purpose. It is provided "as is" without express or
|
||||
* implied warranty.
|
||||
*/
|
||||
|
||||
import { debug, toHex } from '../util';
|
||||
import { rom as smartPortRom } from '../roms/cards/smartport';
|
||||
import { Card, Restorable, byte, word, rom } from '../types';
|
||||
import CPU6502, { CpuState, flags } from '../cpu6502';
|
||||
|
||||
type SmartDisk = Uint8Array[];
|
||||
|
||||
interface BlockDevice {
|
||||
blocks: Uint8Array
|
||||
}
|
||||
|
||||
export interface SmartPortState {
|
||||
disks: SmartDisk[]
|
||||
}
|
||||
|
||||
export interface SmartPortOptions {
|
||||
block: boolean;
|
||||
}
|
||||
|
||||
class Address {
|
||||
lo: byte;
|
||||
hi: byte;
|
||||
|
||||
constructor(private cpu: CPU6502, a: byte | word, b?: byte) {
|
||||
if (b === undefined) {
|
||||
this.lo = a & 0xff;
|
||||
this.hi = a >> 8;
|
||||
} else {
|
||||
this.lo = a;
|
||||
this.hi = b;
|
||||
}
|
||||
}
|
||||
|
||||
loByte() {
|
||||
return this.lo;
|
||||
}
|
||||
|
||||
hiByte() {
|
||||
return this.hi;
|
||||
}
|
||||
|
||||
inc(val: byte) {
|
||||
return new Address(this.cpu, ((this.hi << 8 | this.lo) + val) & 0xffff);
|
||||
}
|
||||
|
||||
readByte() {
|
||||
return this.cpu.read(this.hi, this.lo);
|
||||
}
|
||||
|
||||
readWord() {
|
||||
const readLo = this.readByte();
|
||||
const readHi = this.inc(1).readByte();
|
||||
|
||||
return readHi << 8 | readLo;
|
||||
}
|
||||
|
||||
readAddress() {
|
||||
const readLo = this.readByte();
|
||||
const readHi = this.inc(1).readByte();
|
||||
|
||||
return new Address(this.cpu, readLo, readHi);
|
||||
}
|
||||
|
||||
writeByte(val: byte) {
|
||||
this.cpu.write(this.hi, this.lo, val);
|
||||
}
|
||||
|
||||
writeWord(val: word) {
|
||||
this.writeByte(val & 0xff);
|
||||
this.inc(1).writeByte(val >> 8);
|
||||
}
|
||||
|
||||
writeAddress(val: Address) {
|
||||
this.writeByte(val.loByte());
|
||||
this.inc(1).writeByte(val.hiByte());
|
||||
}
|
||||
|
||||
toString() {
|
||||
return '$' + toHex(this.hi) + toHex(this.lo);
|
||||
}
|
||||
}
|
||||
|
||||
// ProDOS zero page locations
|
||||
|
||||
const COMMAND = 0x42;
|
||||
const UNIT = 0x43;
|
||||
const ADDRESS_LO = 0x44;
|
||||
// const ADDRESS_HI = 0x45;
|
||||
const BLOCK_LO = 0x46;
|
||||
// const BLOCK_HI = 0x47;
|
||||
|
||||
export default class SmartPort implements Card, Restorable<SmartPortState> {
|
||||
|
||||
private rom: rom;
|
||||
private disks: SmartDisk[] = [];
|
||||
|
||||
constructor(private cpu: CPU6502, options: SmartPortOptions) {
|
||||
if (options?.block) {
|
||||
const dumbPortRom = new Uint8Array(smartPortRom);
|
||||
dumbPortRom[0x07] = 0x3C;
|
||||
this.rom = dumbPortRom;
|
||||
debug('DumbPort card');
|
||||
} else {
|
||||
debug('SmartPort card');
|
||||
}
|
||||
this.rom = smartPortRom;
|
||||
}
|
||||
|
||||
private decodeDisk(unit: number, disk: BlockDevice) {
|
||||
this.disks[unit] = [];
|
||||
for (let idx = 0; idx < disk.blocks.length; idx++) {
|
||||
this.disks[unit][idx] = new Uint8Array(disk.blocks[idx]);
|
||||
}
|
||||
}
|
||||
|
||||
private debug(..._args: any[]) {
|
||||
// debug.apply(this, arguments);
|
||||
}
|
||||
|
||||
/*
|
||||
* dumpBlock
|
||||
*/
|
||||
/*
|
||||
dumpBlock(drive, block) {
|
||||
const result = '';
|
||||
const b;
|
||||
const jdx;
|
||||
|
||||
for (const idx = 0; idx < 32; idx++) {
|
||||
result += toHex(idx << 4, 4) + ': ';
|
||||
for (jdx = 0; jdx < 16; jdx++) {
|
||||
b = disks[drive][block][idx * 16 + jdx];
|
||||
if (jdx == 8) {
|
||||
result += ' ';
|
||||
}
|
||||
result += toHex(b) + ' ';
|
||||
}
|
||||
result += ' ';
|
||||
for (jdx = 0; jdx < 16; jdx++) {
|
||||
b = disks[drive][block][idx * 16 + jdx] & 0x7f;
|
||||
if (jdx == 8) {
|
||||
result += ' ';
|
||||
}
|
||||
if (b >= 0x20 && b < 0x7f) {
|
||||
result += String.fromCharCode(b);
|
||||
} else {
|
||||
result += '.';
|
||||
}
|
||||
}
|
||||
result += '\n';
|
||||
}
|
||||
return result;
|
||||
}
|
||||
*/
|
||||
/*
|
||||
* getDeviceInfo
|
||||
*/
|
||||
|
||||
getDeviceInfo(state: CpuState, drive: number) {
|
||||
if (this.disks[drive]) {
|
||||
const blocks = this.disks[drive].length;
|
||||
state.x = blocks & 0xff;
|
||||
state.y = blocks >> 8;
|
||||
|
||||
state.a = 0;
|
||||
state.s &= ~flags.C;
|
||||
} else {
|
||||
state.a = 0x28;
|
||||
state.s |= flags.C;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* readBlock
|
||||
*/
|
||||
|
||||
readBlock(state: CpuState, drive: number, block: number, buffer: Address) {
|
||||
this.debug('read drive=' + drive);
|
||||
this.debug('read buffer=' + buffer);
|
||||
this.debug('read block=$' + toHex(block));
|
||||
|
||||
if (!this.disks[drive] || !this.disks[drive].length) {
|
||||
debug('Drive', drive, 'is empty');
|
||||
return;
|
||||
}
|
||||
|
||||
// debug('read', '\n' + dumpBlock(drive, block));
|
||||
|
||||
for (let idx = 0; idx < 512; idx++) {
|
||||
buffer.writeByte(this.disks[drive][block][idx]);
|
||||
buffer = buffer.inc(1);
|
||||
}
|
||||
|
||||
state.a = 0;
|
||||
state.s &= 0xfe;
|
||||
}
|
||||
|
||||
/*
|
||||
* writeBlock
|
||||
*/
|
||||
|
||||
writeBlock(state: CpuState, drive: number, block: number, buffer: Address) {
|
||||
this.debug('write drive=' + drive);
|
||||
this.debug('write buffer=' + buffer);
|
||||
this.debug('write block=$' + toHex(block));
|
||||
|
||||
if (!this.disks[drive] || !this.disks[drive].length) {
|
||||
debug('Drive', drive, 'is empty');
|
||||
return;
|
||||
}
|
||||
|
||||
// debug('write', '\n' + dumpBlock(drive, block));
|
||||
|
||||
for (let idx = 0; idx < 512; idx++) {
|
||||
this.disks[drive][block][idx] = buffer.readByte();
|
||||
buffer = buffer.inc(1);
|
||||
}
|
||||
state.a = 0;
|
||||
state.s &= 0xfe;
|
||||
}
|
||||
|
||||
/*
|
||||
* formatDevice
|
||||
*/
|
||||
|
||||
formatDevice(state: CpuState, drive: number) {
|
||||
for (let idx = 0; idx < this.disks[drive].length; idx++) {
|
||||
this.disks[drive][idx] = new Uint8Array();
|
||||
for (let jdx = 0; jdx < 512; jdx++) {
|
||||
this.disks[drive][idx][jdx] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
state.a = 0;
|
||||
state.s &= 0xfe;
|
||||
}
|
||||
|
||||
private access(off: byte, val: byte) {
|
||||
let result;
|
||||
const readMode = val === undefined;
|
||||
|
||||
switch (off & 0x8f) {
|
||||
case 0x80:
|
||||
if (readMode) {
|
||||
result = 0;
|
||||
for (let idx = 0; idx < this.disks.length; idx++) {
|
||||
result <<= 1;
|
||||
if (this.disks[idx]) {
|
||||
result |= 0x01;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
* Interface
|
||||
*/
|
||||
|
||||
ioSwitch(off: byte, val: byte) {
|
||||
return this.access(off, val);
|
||||
}
|
||||
|
||||
read(_page: byte, off: byte) {
|
||||
const state = this.cpu.getState();
|
||||
let cmd;
|
||||
let unit;
|
||||
let buffer;
|
||||
let block;
|
||||
const blockOff = this.rom[0xff];
|
||||
const smartOff = blockOff + 3;
|
||||
|
||||
if (off === blockOff && this.cpu.getSync()) { // Regular block device entry POINT
|
||||
this.debug('block device entry');
|
||||
cmd = this.cpu.read(0x00, COMMAND);
|
||||
unit = this.cpu.read(0x00, UNIT);
|
||||
const bufferAddr = new Address(this.cpu, ADDRESS_LO);
|
||||
const blockAddr = new Address(this.cpu, BLOCK_LO);
|
||||
const drive = (unit & 0x80) ? 2 : 1;
|
||||
const driveSlot = (unit & 0x70) >> 4;
|
||||
|
||||
buffer = bufferAddr.readAddress();
|
||||
block = blockAddr.readWord();
|
||||
|
||||
this.debug('cmd=' + cmd);
|
||||
this.debug('unit=$' + toHex(unit));
|
||||
|
||||
this.debug('slot=' + driveSlot + ' drive=' + drive);
|
||||
this.debug('buffer=' + buffer + ' block=$' + toHex(block));
|
||||
|
||||
switch (cmd) {
|
||||
case 0: // INFO
|
||||
this.getDeviceInfo(state, drive);
|
||||
break;
|
||||
|
||||
case 1: // READ
|
||||
this.readBlock(state, drive, block, buffer);
|
||||
break;
|
||||
|
||||
case 2: // WRITE
|
||||
this.writeBlock(state, drive, block, buffer);
|
||||
break;
|
||||
|
||||
case 3: // FORMAT
|
||||
this.formatDevice(state, unit);
|
||||
break;
|
||||
}
|
||||
} else if (off == smartOff && this.cpu.getSync()) {
|
||||
this.debug('smartport entry');
|
||||
const stackAddr = new Address(this.cpu, state.sp + 1, 0x01);
|
||||
let blocks;
|
||||
|
||||
const retVal = stackAddr.readAddress();
|
||||
|
||||
this.debug('return=' + retVal);
|
||||
|
||||
const cmdBlockAddr = retVal.inc(1);
|
||||
cmd = cmdBlockAddr.readByte();
|
||||
const cmdListAddr = cmdBlockAddr.inc(1).readAddress();
|
||||
|
||||
this.debug('cmd=' + cmd);
|
||||
this.debug('cmdListAddr=' + cmdListAddr);
|
||||
|
||||
stackAddr.writeAddress(retVal.inc(3));
|
||||
|
||||
const parameterCount = cmdListAddr.readByte();
|
||||
unit = cmdListAddr.inc(1).readByte();
|
||||
buffer = cmdListAddr.inc(2).readAddress();
|
||||
let status;
|
||||
|
||||
this.debug('parameterCount=' + parameterCount);
|
||||
switch (cmd) {
|
||||
case 0x00: // INFO
|
||||
status = cmdListAddr.inc(4).readByte();
|
||||
this.debug('info unit=' + unit);
|
||||
this.debug('info buffer=' + buffer);
|
||||
this.debug('info status=' + status);
|
||||
switch (unit) {
|
||||
case 0:
|
||||
switch (status) {
|
||||
case 0:
|
||||
buffer.writeByte(1); // one device
|
||||
buffer.inc(1).writeByte(1 << 6); // no interrupts
|
||||
buffer.inc(2).writeByte(0); // reserved
|
||||
buffer.inc(3).writeByte(0); // reserved
|
||||
buffer.inc(4).writeByte(0); // reserved
|
||||
buffer.inc(5).writeByte(0); // reserved
|
||||
buffer.inc(6).writeByte(0); // reserved
|
||||
buffer.inc(7).writeByte(0); // reserved
|
||||
state.x = 8;
|
||||
state.y = 0;
|
||||
state.a = 0;
|
||||
state.s &= 0xfe;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
default: // Unit 1
|
||||
switch (status) {
|
||||
case 0:
|
||||
blocks = this.disks[unit].length;
|
||||
buffer.writeByte(0xf0); // W/R Block device in drive
|
||||
buffer.inc(1).writeByte(blocks & 0xff); // 1600 blocks
|
||||
buffer.inc(2).writeByte((blocks & 0xff00) >> 8);
|
||||
buffer.inc(3).writeByte((blocks & 0xff0000) >> 16);
|
||||
state.x = 4;
|
||||
state.y = 0;
|
||||
state.a = 0;
|
||||
state.s &= 0xfe;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
state.a = 0;
|
||||
state.s &= 0xfe;
|
||||
break;
|
||||
|
||||
case 0x01: // READ BLOCK
|
||||
block = cmdListAddr.inc(4).readWord();
|
||||
this.readBlock(state, unit, block, buffer);
|
||||
break;
|
||||
|
||||
case 0x02: // WRITE BLOCK
|
||||
block = cmdListAddr.inc(4).readWord();
|
||||
this.writeBlock(state, unit, block, buffer);
|
||||
break;
|
||||
|
||||
case 0x03: // FORMAT
|
||||
this.formatDevice(state, unit);
|
||||
break;
|
||||
|
||||
case 0x04: // CONTROL
|
||||
break;
|
||||
|
||||
case 0x05: // INIT
|
||||
break;
|
||||
|
||||
case 0x06: // OPEN
|
||||
break;
|
||||
|
||||
case 0x07: // CLOSE
|
||||
break;
|
||||
|
||||
case 0x08: // READ
|
||||
break;
|
||||
|
||||
case 0x09: // WRITE
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
this.cpu.setState(state);
|
||||
|
||||
return this.rom[off];
|
||||
}
|
||||
|
||||
write() {
|
||||
}
|
||||
|
||||
getState() {
|
||||
return {
|
||||
disks: this.disks.map(
|
||||
(disk) => disk.map(
|
||||
(block) => new Uint8Array(block)
|
||||
)
|
||||
)
|
||||
};
|
||||
}
|
||||
|
||||
setState(state: SmartPortState) {
|
||||
this.disks = state.disks.map(
|
||||
(disk) => disk.map(
|
||||
(block) => new Uint8Array(block)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
setBinary(drive: number, _name: string, fmt: string, data: ArrayBuffer) {
|
||||
this.disks[drive] = [];
|
||||
if (fmt == '2mg') {
|
||||
data = data.slice(64);
|
||||
}
|
||||
for (let idx = 0; idx < data.byteLength; idx += 512) {
|
||||
this.disks[drive].push(new Uint8Array(data.slice(idx, idx + 512)));
|
||||
}
|
||||
}
|
||||
|
||||
setDisk(drive: number, json: BlockDevice) {
|
||||
this.decodeDisk(drive, json);
|
||||
}
|
||||
}
|
@ -13,20 +13,54 @@ import { byte, DiskFormat, memory } from '../types';
|
||||
import { base64_decode, base64_encode } from '../base64';
|
||||
import { bytify, debug, toHex } from '../util';
|
||||
|
||||
export type Disk = {
|
||||
format: DiskFormat,
|
||||
name: string,
|
||||
volume: byte,
|
||||
tracks: memory[],
|
||||
readOnly: boolean,
|
||||
};
|
||||
export interface Disk {
|
||||
format: DiskFormat
|
||||
name: string
|
||||
volume: byte
|
||||
tracks: memory[]
|
||||
readOnly: boolean
|
||||
}
|
||||
|
||||
export type Drive = {
|
||||
format: DiskFormat,
|
||||
volume: byte,
|
||||
tracks: memory[],
|
||||
readOnly: boolean,
|
||||
dirty: boolean,
|
||||
/**
|
||||
* Base format for JSON defined disks
|
||||
*/
|
||||
export class JSONDiskBase {
|
||||
type: DiskFormat
|
||||
name: string
|
||||
volume: byte
|
||||
readOnly: boolean
|
||||
}
|
||||
|
||||
/**
|
||||
* JSON Disk format with base64 encoded tracks
|
||||
*/
|
||||
|
||||
export interface Base64JSONDisk extends JSONDiskBase {
|
||||
encoding: 'base64'
|
||||
data: string[]
|
||||
}
|
||||
|
||||
/**
|
||||
* JSON Disk format with byte array tracks
|
||||
*/
|
||||
|
||||
export interface BinaryJSONDisk extends JSONDiskBase {
|
||||
encoding: 'binary'
|
||||
data: memory[][]
|
||||
}
|
||||
|
||||
/**
|
||||
* General JSON Disk format
|
||||
*/
|
||||
|
||||
export type JSONDisk = Base64JSONDisk | BinaryJSONDisk;
|
||||
|
||||
export interface Drive {
|
||||
format: DiskFormat
|
||||
volume: byte
|
||||
tracks: memory[]
|
||||
readOnly: boolean
|
||||
dirty: boolean
|
||||
}
|
||||
|
||||
/**
|
||||
@ -52,7 +86,7 @@ export const PO = [
|
||||
0x0, 0x8, 0x1, 0x9, 0x2, 0xa, 0x3, 0xb,
|
||||
0x4, 0xc, 0x5, 0xd, 0x6, 0xe, 0x7, 0xf
|
||||
];
|
||||
|
||||
|
||||
/**
|
||||
* ProDOS Logical sector order (index is ProDOS sector, value is physical sector).
|
||||
*/
|
||||
|
@ -76,13 +76,13 @@ var io = apple2.getIO();
|
||||
|
||||
var printer = new Printer('#printer-modal .paper');
|
||||
|
||||
var lc = new LanguageCard(io, rom);
|
||||
var lc = new LanguageCard(rom);
|
||||
var parallel = new Parallel(io, printer);
|
||||
var videoTerm = new VideoTerm(io);
|
||||
var slinky = new RAMFactor(io, 1024 * 1024);
|
||||
var disk2 = new DiskII(io, driveLights, sectors);
|
||||
var clock = new Thunderclock(io);
|
||||
var smartport = new SmartPort(io, cpu, { block: true });
|
||||
var smartport = new SmartPort(cpu, { block: true });
|
||||
|
||||
initUI(apple2, disk2, smartport, printer, false);
|
||||
|
||||
|
@ -62,7 +62,7 @@ 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 smartport = new SmartPort(io, cpu);
|
||||
var smartport = new SmartPort(cpu);
|
||||
|
||||
initUI(apple2, disk2, smartport, printer, options.e);
|
||||
|
||||
|
94
js/mmu.ts
94
js/mmu.ts
@ -695,69 +695,53 @@ export default class MMU implements Memory, Restorable<MMUState> {
|
||||
|
||||
_accessLangCard(off: byte, val?: byte) {
|
||||
const readMode = val === undefined;
|
||||
const result = readMode ? 0 : undefined;
|
||||
|
||||
switch (off & 0x8b) {
|
||||
// Language Card Switches
|
||||
const writeSwitch = off & 0x01;
|
||||
const offSwitch = off & 0x02;
|
||||
const bank1Switch = off & 0x08;
|
||||
|
||||
case LOC.READBSR2: // 0xC080
|
||||
this._bank1 = false;
|
||||
let bankStr;
|
||||
let rwStr;
|
||||
|
||||
if (writeSwitch) { // 0xC081, 0xC083
|
||||
if (readMode) {
|
||||
this._writebsr = this._prewrite;
|
||||
}
|
||||
this._prewrite = readMode;
|
||||
|
||||
if (offSwitch) { // 0xC08B
|
||||
this._readbsr = true;
|
||||
this._writebsr = false;
|
||||
this._prewrite = false;
|
||||
// _debug('Bank 2 Read');
|
||||
break;
|
||||
case LOC.WRITEBSR2: // 0xC081
|
||||
this._bank1 = false;
|
||||
rwStr = 'Read/Write';
|
||||
} else {
|
||||
this._readbsr = false;
|
||||
if (readMode) { this._writebsr = this._prewrite; }
|
||||
this._prewrite = readMode;
|
||||
// _debug('Bank 2 Write');
|
||||
break;
|
||||
case LOC.OFFBSR2: // 0xC082
|
||||
this._bank1 = false;
|
||||
rwStr = 'Write';
|
||||
}
|
||||
} else { // 0xC080, 0xC082
|
||||
this._writebsr = false;
|
||||
this._prewrite = false;
|
||||
|
||||
if (offSwitch) { // 0xC082
|
||||
this._readbsr = false;
|
||||
this._writebsr = false;
|
||||
this._prewrite = false;
|
||||
// _debug('Bank 2 Off');
|
||||
break;
|
||||
case LOC.READWRBSR2: // 0xC083
|
||||
this._bank1 = false;
|
||||
rwStr = 'Off';
|
||||
} else { // 0xC080
|
||||
this._readbsr = true;
|
||||
if (readMode) { this._writebsr = this._prewrite; }
|
||||
this._prewrite = readMode;
|
||||
// _debug('Bank 2 Read/Write');
|
||||
break;
|
||||
case LOC.READBSR1: // 0xC088
|
||||
this._bank1 = true;
|
||||
this._readbsr = true;
|
||||
this._writebsr = false;
|
||||
this._prewrite = false;
|
||||
// _debug('Bank 1 Read');
|
||||
break;
|
||||
case LOC.WRITEBSR1: // 0xC089
|
||||
this._bank1 = true;
|
||||
this._readbsr = false;
|
||||
if (readMode) { this._writebsr = this._prewrite; }
|
||||
this._prewrite = readMode;
|
||||
// _debug('Bank 1 Write');
|
||||
break;
|
||||
case LOC.OFFBSR1: // 0xC08A
|
||||
this._bank1 = true;
|
||||
this._readbsr = false;
|
||||
this._writebsr = false;
|
||||
this._prewrite = false;
|
||||
// _debug('Bank 1 Off');
|
||||
break;
|
||||
case LOC.READWRBSR1: // 0xC08B
|
||||
this._bank1 = true;
|
||||
this._readbsr = true;
|
||||
if (readMode) { this._writebsr = this._prewrite; }
|
||||
this._prewrite = readMode;
|
||||
//_debug('Bank 1 Read/Write');
|
||||
break;
|
||||
rwStr = 'Read';
|
||||
}
|
||||
}
|
||||
|
||||
if (bank1Switch) {
|
||||
this._bank1 = true;
|
||||
bankStr = 'Bank 1';
|
||||
} else {
|
||||
this._bank1 = false;
|
||||
bankStr = 'Bank 2';
|
||||
}
|
||||
|
||||
this._debug(bankStr, rwStr);
|
||||
this._updateBanks();
|
||||
}
|
||||
|
||||
return result; }
|
||||
|
||||
/*
|
||||
* The Big Switch
|
||||
|
@ -47,7 +47,7 @@ export interface MemoryPages extends Memory {
|
||||
/* An interface card */
|
||||
export interface Card extends Memory, Restorable {
|
||||
/* Reset the card */
|
||||
reset(): void;
|
||||
reset?(): void;
|
||||
|
||||
/* Draw card to canvas */
|
||||
blit?(): ImageData;
|
||||
|
Loading…
Reference in New Issue
Block a user