diff --git a/css/ui.css b/css/ui.css
index bc5f9d0ce..a8bffc00e 100644
--- a/css/ui.css
+++ b/css/ui.css
@@ -251,7 +251,6 @@ div.emuspacer {
.emuvideo {
border-radius:20px;
border: 4px solid #222;
- outline-color: #ccc;
padding: 20px;
background: #000;
margin-top:40px;
@@ -259,9 +258,9 @@ div.emuspacer {
margin-right:3%;
width:94%;
pointer-events:auto;
+ cursor: crosshair;
}
.emuvideo:focus {
- outline:none;
border-color:#888;
}
canvas.pixelated {
diff --git a/index.html b/index.html
index 78f71ba4e..b85dbdfc9 100644
--- a/index.html
+++ b/index.html
@@ -321,6 +321,13 @@
←↑↓→ Joystick
Space Button
+
←↑↓→ Joypad
Space Button A
diff --git a/presets/apple2/paddles.dasm b/presets/apple2/paddles.dasm
new file mode 100644
index 000000000..cfcbcabbe
--- /dev/null
+++ b/presets/apple2/paddles.dasm
@@ -0,0 +1,107 @@
+ processor 6502
+ org $0803
+
+; --- CONSTANTS ---
+BASL equ $28
+BASH equ $29
+PREAD equ $FB1E
+SW0 equ $C061
+SW1 equ $C062
+SW2 equ $C063
+HOME equ $FC58
+TABV equ $FB5B
+
+; --- ZERO PAGE VARIABLES ---
+PREVX equ $06
+PREVY equ $07
+NEWX equ $08
+NEWY equ $09
+BRUSH equ $0A
+
+; --- PROGRAM ---
+SETUP:
+ jsr HOME
+ lda #$00
+ sta PREVX
+ sta PREVY
+
+LOOP:
+ ; CHECK FOR MOVEMENT
+ lda NEWX
+ cmp PREVX
+ bne SELECT_BRUSH
+ lda NEWY
+ cmp PREVY
+ beq NOPAINT
+
+SELECT_BRUSH:
+ ; PAINT PREVIOUS X/Y
+ lda SW0
+ bpl NOSW0
+ lda #$20 ; INVERSE SPACE
+ jmp PAINT
+NOSW0: lda SW1
+ bpl NOSW1
+ lda #$60 ; FLASHING SPACE
+ jmp PAINT
+NOSW1: lda SW2
+ bpl NOSW2
+ lda #$AE ; PERIOD
+ jmp PAINT
+NOSW2: lda #$A0 ; SPACE
+
+PAINT:
+ sta BRUSH
+ lda PREVY
+ jsr TABV
+ ldy PREVX
+ lda BRUSH
+ sta (BASL),Y
+
+ ; SAVE PREVIOUS X/Y
+ lda NEWX
+ sta PREVX
+ lda NEWY
+ sta PREVY
+NOPAINT:
+ ; CALCULATE NEW X (0-39)
+ ldx #$00
+ jsr PREAD
+ lda XTABLE,Y
+ sta NEWX
+
+ ; CALCULATE NEW Y (0-23)
+ ldx #$01
+ jsr PREAD
+ lda YTABLE,Y
+ sta NEWY
+
+; DRAW CURSOR
+ lda NEWY
+ jsr TABV
+ ldy NEWX
+
+ lda #$20 ; INVERSE SPACE
+ sta (BASL),Y
+NODRAW:
+ jmp LOOP
+
+
+; --- LOOKUP TABLES ---
+
+XTABLE:
+ ; MAP [0,255] -> [0,39]
+X set 0
+ repeat 256
+ .byte X * 40 / 256
+X set X + 1
+ repend
+
+YTABLE:
+ ; MAP [0,255] -> [0,23]
+Y set 0
+ repeat 256
+ .byte Y * 24 / 256
+Y set Y + 1
+ repend
+
diff --git a/src/common/baseplatform.ts b/src/common/baseplatform.ts
index 2fced0afc..9fdd4be10 100644
--- a/src/common/baseplatform.ts
+++ b/src/common/baseplatform.ts
@@ -881,6 +881,11 @@ export abstract class BaseMachinePlatform
extends BaseDebugPl
if (hasPaddleInput(this.machine)) {
this.machine.setPaddleInput(0, this.video.paddle_x);
this.machine.setPaddleInput(1, this.video.paddle_y);
+ if (this.machine.setPaddleButton) {
+ this.machine.setPaddleButton(0, this.video.paddle_buttons[0]);
+ this.machine.setPaddleButton(1, this.video.paddle_buttons[1]);
+ this.machine.setPaddleButton(2, this.video.paddle_buttons[2]);
+ }
}
// TODO: put into interface
if (this.machine['pollControls']) {
diff --git a/src/common/devices.ts b/src/common/devices.ts
index d22848fe8..fd834088e 100644
--- a/src/common/devices.ts
+++ b/src/common/devices.ts
@@ -112,6 +112,7 @@ export interface AcceptsKeyInput {
export interface AcceptsPaddleInput {
setPaddleInput(controller: number, value: number): void;
+ setPaddleButton?(index: number, pressed: boolean): void;
}
// TODO: interface not yet used (setKeyInput() handles joystick)
diff --git a/src/common/emu.ts b/src/common/emu.ts
index d14ec15e5..ccc592a15 100644
--- a/src/common/emu.ts
+++ b/src/common/emu.ts
@@ -10,24 +10,24 @@ export var PLATFORMS = {};
var _random_state = 1;
export function noise() {
- let x = _random_state;
- x ^= x << 13;
- x ^= x >> 17;
- x ^= x << 5;
- return (_random_state = x) & 0xff;
+ let x = _random_state;
+ x ^= x << 13;
+ x ^= x >> 17;
+ x ^= x << 5;
+ return (_random_state = x) & 0xff;
}
export function getNoiseSeed() {
return _random_state;
}
-export function setNoiseSeed(x : number) {
+export function setNoiseSeed(x: number) {
_random_state = x;
}
-type KeyboardCallback = (which:number, charCode:number, flags:KeyFlags) => void;
+type KeyboardCallback = (which: number, charCode: number, flags: KeyFlags) => void;
-export function __createCanvas(doc:HTMLDocument, mainElement:HTMLElement, width:number, height:number) : HTMLCanvasElement {
+export function __createCanvas(doc: HTMLDocument, mainElement: HTMLElement, width: number, height: number): HTMLCanvasElement {
var canvas = doc.createElement('canvas');
canvas.width = width;
canvas.height = height;
@@ -50,47 +50,50 @@ export enum KeyFlags {
// TODO: don't use which/keyCode anymore?
// TODO: let keycode = e.key ? e.key.charCodeAt(0) : e.keyCode;
// TODO: let charCode = e.key ? e.key.charCodeAt(0) : e.charCode;
-export function _setKeyboardEvents(canvas:HTMLElement, callback:KeyboardCallback) {
+export function _setKeyboardEvents(canvas: HTMLElement, callback: KeyboardCallback) {
canvas.onkeydown = (e) => {
let flags = _metakeyflags(e);
- callback(e.which, e.keyCode, KeyFlags.KeyDown|flags);
+ callback(e.which, e.keyCode, KeyFlags.KeyDown | flags);
if (!flags) e.preventDefault(); // eat all keys that don't have a modifier
};
canvas.onkeyup = (e) => {
- callback(e.which, e.keyCode, KeyFlags.KeyUp|_metakeyflags(e));
+ callback(e.which, e.keyCode, KeyFlags.KeyUp | _metakeyflags(e));
};
};
-type VideoCanvasOptions = {rotate?:number, overscan?:boolean, aspect?:number};
+type VideoCanvasOptions = { rotate?: number, overscan?: boolean, aspect?: number };
export class RasterVideo {
- mainElement : HTMLElement;
- width : number;
- height : number;
- options : VideoCanvasOptions;
+ mainElement: HTMLElement;
+ width: number;
+ height: number;
+ options: VideoCanvasOptions;
- constructor(mainElement:HTMLElement, width:number, height:number, options?:VideoCanvasOptions) {
+ constructor(mainElement: HTMLElement, width: number, height: number, options?: VideoCanvasOptions) {
this.mainElement = mainElement;
this.width = width;
this.height = height;
this.options = options;
}
-
- canvas : HTMLCanvasElement;
- ctx : CanvasRenderingContext2D;
- imageData : ImageData;
- datau32 : Uint32Array;
- vcanvas : JQuery;
-
- paddle_x = 255;
- paddle_y = 255;
-
- setRotate(rotate:number) {
+
+ canvas: HTMLCanvasElement;
+ ctx: CanvasRenderingContext2D;
+ imageData: ImageData;
+ datau32: Uint32Array;
+ vcanvas: JQuery;
+
+ // Start paddles/joystick centered in [0,255] range.
+ paddle_x = 128;
+ paddle_y = 128;
+ // Platforms can support up to three buttons.
+ paddle_buttons: boolean[] = [false, false, false];
+
+ setRotate(rotate: number) {
var canvas = this.canvas;
if (rotate) {
// TODO: aspect ratio?
- canvas.style.transform = "rotate("+rotate+"deg)";
+ canvas.style.transform = "rotate(" + rotate + "deg)";
if (canvas.width < canvas.height)
canvas.style.paddingLeft = canvas.style.paddingRight = "10%";
} else {
@@ -107,11 +110,11 @@ export class RasterVideo {
this.setRotate(this.options.rotate);
}
if (this.options && this.options.overscan) {
- this.vcanvas.css('padding','0px');
+ this.vcanvas.css('padding', '0px');
}
if (this.options && this.options.aspect) {
console.log(this.options);
- this.vcanvas.css('aspect-ratio', this.options.aspect+"");
+ this.vcanvas.css('aspect-ratio', this.options.aspect + "");
}
this.ctx = canvas.getContext('2d');
this.imageData = this.ctx.createImageData(this.width, this.height);
@@ -126,27 +129,34 @@ export class RasterVideo {
getContext() { return this.ctx; }
- updateFrame(sx?:number, sy?:number, dx?:number, dy?:number, w?:number, h?:number) {
+ updateFrame(sx?: number, sy?: number, dx?: number, dy?: number, w?: number, h?: number) {
if (w && h)
this.ctx.putImageData(this.imageData, sx, sy, dx, dy, w, h);
else
this.ctx.putImageData(this.imageData, 0, 0);
}
- clearRect(dx:number, dy:number, w:number, h:number) {
+ clearRect(dx: number, dy: number, w: number, h: number) {
var ctx = this.ctx;
ctx.fillStyle = '#000000';
ctx.fillRect(dx, dy, w, h);
}
- setupMouseEvents(el? : HTMLCanvasElement) {
+ setupMouseEvents(el?: HTMLCanvasElement) {
if (!el) el = this.canvas;
- $(el).mousemove( (e) => {
+ $(el).mousemove((e) => {
var pos = getMousePos(el, e);
- var new_x = Math.floor(pos.x * 255 / this.canvas.width);
- var new_y = Math.floor(pos.y * 255 / this.canvas.height);
+ var new_x = Math.round(pos.x * 255 / this.canvas.width);
+ var new_y = Math.round(pos.y * 255 / this.canvas.height);
this.paddle_x = clamp(0, 255, new_x);
this.paddle_y = clamp(0, 255, new_y);
+ }).mousedown((e) => {
+ // TODO Allows users to specify mapping in settings.
+ this.paddle_buttons[0] = !e.shiftKey && !e.altKey;
+ this.paddle_buttons[1] = e.shiftKey && !e.altKey;
+ this.paddle_buttons[2] = e.altKey && !e.shiftKey;
+ }).mouseup((e) => {
+ this.paddle_buttons.fill(false);
});
};
}
@@ -156,13 +166,13 @@ export class VectorVideo extends RasterVideo {
persistenceAlpha = 0.5;
jitter = 1.0;
gamma = 0.8;
- sx : number;
- sy : number;
-
+ sx: number;
+ sy: number;
+
create() {
super.create();
- this.sx = this.width/1024.0;
- this.sy = this.height/1024.0;
+ this.sx = this.width / 1024.0;
+ this.sy = this.height / 1024.0;
}
clear() {
@@ -186,7 +196,7 @@ export class VectorVideo extends RasterVideo {
'#ffffff'
];
- drawLine(x1:number, y1:number, x2:number, y2:number, intensity:number, color:number) {
+ drawLine(x1: number, y1: number, x2: number, y2: number, intensity: number, color: number) {
var ctx = this.ctx;
var sx = this.sx;
var sy = this.sy;
@@ -204,25 +214,25 @@ export class VectorVideo extends RasterVideo {
x2 += jx;
y1 += jy;
y2 += jy;
- ctx.moveTo(x1*sx, this.height-y1*sy);
+ ctx.moveTo(x1 * sx, this.height - y1 * sy);
if (x1 == x2 && y1 == y2)
- ctx.lineTo(x2*sx+1, this.height-y2*sy);
+ ctx.lineTo(x2 * sx + 1, this.height - y2 * sy);
else
- ctx.lineTo(x2*sx, this.height-y2*sy);
+ ctx.lineTo(x2 * sx, this.height - y2 * sy);
ctx.strokeStyle = this.COLORS[color & 7];
ctx.stroke();
}
}
}
-export function drawCrosshair(ctx:CanvasRenderingContext2D, x:number, y:number, width:number) {
+export function drawCrosshair(ctx: CanvasRenderingContext2D, x: number, y: number, width: number) {
if (!ctx?.setLineDash) return; // for unit testing
ctx.fillStyle = 'rgba(0,0,0,0.25)';
- ctx.fillRect(x-2, 0, 5, 32767);
- ctx.fillRect(0, y-2, 32767, 5);
+ ctx.fillRect(x - 2, 0, 5, 32767);
+ ctx.fillRect(0, y - 2, 32767, 5);
ctx.lineWidth = width;
ctx.strokeStyle = 'rgba(255,255,255,0.75)';
- ctx.setLineDash([width*2,width*2]);
+ ctx.setLineDash([width * 2, width * 2]);
ctx.beginPath();
ctx.moveTo(x, 0);
ctx.lineTo(x, 32767);
@@ -232,14 +242,14 @@ export function drawCrosshair(ctx:CanvasRenderingContext2D, x:number, y:number,
}
export class RAM {
- mem : Uint8Array;
- constructor(size:number) {
+ mem: Uint8Array;
+ constructor(size: number) {
this.mem = new Uint8Array(new ArrayBuffer(size));
}
}
export class EmuHalt extends Error {
- $loc : SourceLocation;
+ $loc: SourceLocation;
squelchError = true;
constructor(msg: string, loc?: SourceLocation) {
super(msg);
@@ -248,27 +258,27 @@ export class EmuHalt extends Error {
}
}
-export var useRequestAnimationFrame : boolean = false;
+export var useRequestAnimationFrame: boolean = false;
export class AnimationTimer {
- callback;
- running : boolean = false;
- pulsing : boolean = false;
+ callback;
+ running: boolean = false;
+ pulsing: boolean = false;
nextts = 0;
nframes;
startts; // for FPS calc
frameRate;
intervalMsec;
useReqAnimFrame = useRequestAnimationFrame && typeof window.requestAnimationFrame === 'function'; // need for unit test
-
- constructor(frequencyHz:number, callback:() => void) {
+
+ constructor(frequencyHz: number, callback: () => void) {
this.frameRate = frequencyHz;
this.intervalMsec = 1000.0 / frequencyHz;
this.callback = callback;
}
- scheduleFrame(msec:number) {
+ scheduleFrame(msec: number) {
var fn = (timestamp) => {
try {
this.nextFrame(this.useReqAnimFrame ? timestamp : Date.now());
@@ -283,8 +293,8 @@ export class AnimationTimer {
else
setTimeout(fn, msec);
}
-
- nextFrame(ts:number) {
+
+ nextFrame(ts: number) {
if (ts > this.nextts) {
if (this.running) {
this.callback();
@@ -292,7 +302,7 @@ export class AnimationTimer {
if (this.nframes == 0)
this.startts = ts;
if (this.nframes++ == 300) {
- console.log("Avg framerate: " + this.nframes*1000/(ts-this.startts) + " fps");
+ console.log("Avg framerate: " + this.nframes * 1000 / (ts - this.startts) + " fps");
}
}
this.nextts += this.intervalMsec;
@@ -328,19 +338,19 @@ export class AnimationTimer {
// TODO: move to util?
-export function dumpRAM(ram:ArrayLike, ramofs:number, ramlen:number) : string {
+export function dumpRAM(ram: ArrayLike, ramofs: number, ramlen: number): string {
var s = "";
var bpel = ram['BYTES_PER_ELEMENT'] || 1;
var perline = Math.ceil(16 / bpel);
var isFloat = ram instanceof Float32Array || ram instanceof Float64Array;
// TODO: show scrollable RAM for other platforms
- for (var ofs=0; ofs, ramofs:number, ramlen:number) : s
}
export interface KeyDef {
- c:number, // key code
- n:string, // name
+ c: number, // key code
+ n: string, // name
// for gamepad
- plyr?:number,
- xaxis?:number,
- yaxis?:number,
- button?:number
- };
+ plyr?: number,
+ xaxis?: number,
+ yaxis?: number,
+ button?: number
+};
export interface KeyMapEntry {
- index:number;
- mask:number;
- def:KeyDef;
+ index: number;
+ mask: number;
+ def: KeyDef;
}
-type KeyCodeMap = Map;
+type KeyCodeMap = Map;
export const Keys = {
- ANYKEY: {c: 0, n: "?"},
- // https://w3c.github.io/gamepad/#remapping
- // gamepad and keyboard (player 0)
- UP: {c: 38, n: "Up", plyr:0, button:12, yaxis:-1},
- DOWN: {c: 40, n: "Down", plyr:0, button:13, yaxis:1},
- LEFT: {c: 37, n: "Left", plyr:0, button:14, xaxis:-1},
- RIGHT: {c: 39, n: "Right", plyr:0, button:15, xaxis:1},
- A: {c: 32, n: "Space", plyr:0, button:0},
- B: {c: 16, n: "Shift", plyr:0, button:1},
- GP_A: {c: 88, n: "X", plyr:0, button:0},
- GP_B: {c: 90, n: "Z", plyr:0, button:1},
- GP_C: {c: 86, n: "V", plyr:0, button:2},
- GP_D: {c: 67, n: "C", plyr:0, button:3},
- SELECT: {c: 220, n: "\\", plyr:0, button:8},
- START: {c: 13, n: "Enter", plyr:0, button:9},
- OPTION: {c: 8, n: "Bcksp", plyr:0, button:10},
- // gamepad and keyboard (player 1)
- P2_UP: {c: 87, n: "W", plyr:1, button:12, yaxis:-1},
- P2_DOWN: {c: 83, n: "S", plyr:1, button:13, yaxis:1},
- P2_LEFT: {c: 65, n: "A", plyr:1, button:14, xaxis:-1},
- P2_RIGHT: {c: 68, n: "D", plyr:1, button:15, xaxis:1},
- P2_A: {c: 84, n: "T", plyr:1, button:0},
- P2_B: {c: 82, n: "R", plyr:1, button:1},
- P2_GP_A: {c: 69, n: "E", plyr:1, button:0},
- P2_GP_B: {c: 82, n: "R", plyr:1, button:1},
- P2_GP_C: {c: 84, n: "T", plyr:1, button:2},
- P2_GP_D: {c: 89, n: "Y", plyr:1, button:3},
- P2_SELECT: {c: 70, n: "F", plyr:1, button:8},
- P2_START: {c: 71, n: "G", plyr:1, button:9},
- // keyboard only
- VK_ESCAPE: {c: 27, n: "Esc"},
- VK_F1: {c: 112, n: "F1"},
- VK_F2: {c: 113, n: "F2"},
- VK_F3: {c: 114, n: "F3"},
- VK_F4: {c: 115, n: "F4"},
- VK_F5: {c: 116, n: "F5"},
- VK_F6: {c: 117, n: "F6"},
- VK_F7: {c: 118, n: "F7"},
- VK_F8: {c: 119, n: "F8"},
- VK_F9: {c: 120, n: "F9"},
- VK_F10: {c: 121, n: "F10"},
- VK_F11: {c: 122, n: "F11"},
- VK_F12: {c: 123, n: "F12"},
- VK_SCROLL_LOCK: {c: 145, n: "ScrLck"},
- VK_PAUSE: {c: 19, n: "Pause"},
- VK_QUOTE: {c: 222, n: "'"},
- VK_1: {c: 49, n: "1"},
- VK_2: {c: 50, n: "2"},
- VK_3: {c: 51, n: "3"},
- VK_4: {c: 52, n: "4"},
- VK_5: {c: 53, n: "5"},
- VK_6: {c: 54, n: "6"},
- VK_7: {c: 55, n: "7"},
- VK_8: {c: 56, n: "8"},
- VK_9: {c: 57, n: "9"},
- VK_0: {c: 48, n: "0"},
- VK_MINUS: {c: 189, n: "-"},
- VK_MINUS2: {c: 173, n: "-"},
- VK_EQUALS: {c: 187, n: "="},
- VK_EQUALS2: {c: 61, n: "="},
- VK_BACK_SPACE: {c: 8, n: "Bkspc"},
- VK_TAB: {c: 9, n: "Tab"},
- VK_Q: {c: 81, n: "Q"},
- VK_W: {c: 87, n: "W"},
- VK_E: {c: 69, n: "E"},
- VK_R: {c: 82, n: "R"},
- VK_T: {c: 84, n: "T"},
- VK_Y: {c: 89, n: "Y"},
- VK_U: {c: 85, n: "U"},
- VK_I: {c: 73, n: "I"},
- VK_O: {c: 79, n: "O"},
- VK_P: {c: 80, n: "P"},
- VK_ACUTE: {c: 219, n: "´"},
- VK_OPEN_BRACKET: {c: 219, n: "["},
- VK_CLOSE_BRACKET: {c: 221, n: "]"},
- VK_CAPS_LOCK: {c: 20, n: "CpsLck"},
- VK_A: {c: 65, n: "A"},
- VK_S: {c: 83, n: "S"},
- VK_D: {c: 68, n: "D"},
- VK_F: {c: 70, n: "F"},
- VK_G: {c: 71, n: "G"},
- VK_H: {c: 72, n: "H"},
- VK_J: {c: 74, n: "J"},
- VK_K: {c: 75, n: "K"},
- VK_L: {c: 76, n: "L"},
- VK_CEDILLA: {c: 186, n: "Ç"},
- VK_TILDE: {c: 222, n: "~"},
- VK_ENTER: {c: 13, n: "Enter"},
- VK_SHIFT: {c: 16, n: "Shift"},
- VK_BACK_SLASH: {c: 220, n: "\\"},
- VK_Z: {c: 90, n: "Z"},
- VK_X: {c: 88, n: "X"},
- VK_C: {c: 67, n: "C"},
- VK_V: {c: 86, n: "V"},
- VK_B: {c: 66, n: "B"},
- VK_N: {c: 78, n: "N"},
- VK_M: {c: 77, n: "M"},
- VK_COMMA: {c: 188, n: "] ="},
- VK_PERIOD: {c: 190, n: "."},
- VK_SEMICOLON: {c: 59, n: ";"},
- VK_SLASH: {c: 191, n: "/"},
- VK_CONTROL: {c: 17, n: "Ctrl"},
- VK_ALT: {c: 18, n: "Alt"},
- VK_COMMAND: {c: 224, n: "Cmd"},
- VK_SPACE: {c: 32, n: "Space"},
- VK_INSERT: {c: 45, n: "Ins"},
- VK_DELETE: {c: 46, n: "Del"},
- VK_HOME: {c: 36, n: "Home"},
- VK_END: {c: 35, n: "End"},
- VK_PAGE_UP: {c: 33, n: "PgUp"},
- VK_PAGE_DOWN: {c: 34, n: "PgDown"},
- VK_UP: {c: 38, n: "Up"},
- VK_DOWN: {c: 40, n: "Down"},
- VK_LEFT: {c: 37, n: "Left"},
- VK_RIGHT: {c: 39, n: "Right"},
- VK_NUM_LOCK: {c: 144, n: "Num"},
- VK_DIVIDE: {c: 111, n: "Num /"},
- VK_MULTIPLY: {c: 106, n: "Num *"},
- VK_SUBTRACT: {c: 109, n: "Num -"},
- VK_ADD: {c: 107, n: "Num +"},
- VK_DECIMAL: {c: 194, n: "Num ."},
- VK_NUMPAD0: {c: 96, n: "Num 0"},
- VK_NUMPAD1: {c: 97, n: "Num 1"},
- VK_NUMPAD2: {c: 98, n: "Num 2"},
- VK_NUMPAD3: {c: 99, n: "Num 3"},
- VK_NUMPAD4: {c: 100, n: "Num 4"},
- VK_NUMPAD5: {c: 101, n: "Num 5"},
- VK_NUMPAD6: {c: 102, n: "Num 6"},
- VK_NUMPAD7: {c: 103, n: "Num 7"},
- VK_NUMPAD8: {c: 104, n: "Num 8"},
- VK_NUMPAD9: {c: 105, n: "Num 9"},
- VK_NUMPAD_CENTER: {c: 12, n: "Num Cntr"}
+ ANYKEY: { c: 0, n: "?" },
+ // https://w3c.github.io/gamepad/#remapping
+ // gamepad and keyboard (player 0)
+ UP: { c: 38, n: "Up", plyr: 0, button: 12, yaxis: -1 },
+ DOWN: { c: 40, n: "Down", plyr: 0, button: 13, yaxis: 1 },
+ LEFT: { c: 37, n: "Left", plyr: 0, button: 14, xaxis: -1 },
+ RIGHT: { c: 39, n: "Right", plyr: 0, button: 15, xaxis: 1 },
+ A: { c: 32, n: "Space", plyr: 0, button: 0 },
+ B: { c: 16, n: "Shift", plyr: 0, button: 1 },
+ GP_A: { c: 88, n: "X", plyr: 0, button: 0 },
+ GP_B: { c: 90, n: "Z", plyr: 0, button: 1 },
+ GP_C: { c: 86, n: "V", plyr: 0, button: 2 },
+ GP_D: { c: 67, n: "C", plyr: 0, button: 3 },
+ SELECT: { c: 220, n: "\\", plyr: 0, button: 8 },
+ START: { c: 13, n: "Enter", plyr: 0, button: 9 },
+ OPTION: { c: 8, n: "Bcksp", plyr: 0, button: 10 },
+ // gamepad and keyboard (player 1)
+ P2_UP: { c: 87, n: "W", plyr: 1, button: 12, yaxis: -1 },
+ P2_DOWN: { c: 83, n: "S", plyr: 1, button: 13, yaxis: 1 },
+ P2_LEFT: { c: 65, n: "A", plyr: 1, button: 14, xaxis: -1 },
+ P2_RIGHT: { c: 68, n: "D", plyr: 1, button: 15, xaxis: 1 },
+ P2_A: { c: 84, n: "T", plyr: 1, button: 0 },
+ P2_B: { c: 82, n: "R", plyr: 1, button: 1 },
+ P2_GP_A: { c: 69, n: "E", plyr: 1, button: 0 },
+ P2_GP_B: { c: 82, n: "R", plyr: 1, button: 1 },
+ P2_GP_C: { c: 84, n: "T", plyr: 1, button: 2 },
+ P2_GP_D: { c: 89, n: "Y", plyr: 1, button: 3 },
+ P2_SELECT: { c: 70, n: "F", plyr: 1, button: 8 },
+ P2_START: { c: 71, n: "G", plyr: 1, button: 9 },
+ // keyboard only
+ VK_ESCAPE: { c: 27, n: "Esc" },
+ VK_F1: { c: 112, n: "F1" },
+ VK_F2: { c: 113, n: "F2" },
+ VK_F3: { c: 114, n: "F3" },
+ VK_F4: { c: 115, n: "F4" },
+ VK_F5: { c: 116, n: "F5" },
+ VK_F6: { c: 117, n: "F6" },
+ VK_F7: { c: 118, n: "F7" },
+ VK_F8: { c: 119, n: "F8" },
+ VK_F9: { c: 120, n: "F9" },
+ VK_F10: { c: 121, n: "F10" },
+ VK_F11: { c: 122, n: "F11" },
+ VK_F12: { c: 123, n: "F12" },
+ VK_SCROLL_LOCK: { c: 145, n: "ScrLck" },
+ VK_PAUSE: { c: 19, n: "Pause" },
+ VK_QUOTE: { c: 222, n: "'" },
+ VK_1: { c: 49, n: "1" },
+ VK_2: { c: 50, n: "2" },
+ VK_3: { c: 51, n: "3" },
+ VK_4: { c: 52, n: "4" },
+ VK_5: { c: 53, n: "5" },
+ VK_6: { c: 54, n: "6" },
+ VK_7: { c: 55, n: "7" },
+ VK_8: { c: 56, n: "8" },
+ VK_9: { c: 57, n: "9" },
+ VK_0: { c: 48, n: "0" },
+ VK_MINUS: { c: 189, n: "-" },
+ VK_MINUS2: { c: 173, n: "-" },
+ VK_EQUALS: { c: 187, n: "=" },
+ VK_EQUALS2: { c: 61, n: "=" },
+ VK_BACK_SPACE: { c: 8, n: "Bkspc" },
+ VK_TAB: { c: 9, n: "Tab" },
+ VK_Q: { c: 81, n: "Q" },
+ VK_W: { c: 87, n: "W" },
+ VK_E: { c: 69, n: "E" },
+ VK_R: { c: 82, n: "R" },
+ VK_T: { c: 84, n: "T" },
+ VK_Y: { c: 89, n: "Y" },
+ VK_U: { c: 85, n: "U" },
+ VK_I: { c: 73, n: "I" },
+ VK_O: { c: 79, n: "O" },
+ VK_P: { c: 80, n: "P" },
+ VK_ACUTE: { c: 219, n: "´" },
+ VK_OPEN_BRACKET: { c: 219, n: "[" },
+ VK_CLOSE_BRACKET: { c: 221, n: "]" },
+ VK_CAPS_LOCK: { c: 20, n: "CpsLck" },
+ VK_A: { c: 65, n: "A" },
+ VK_S: { c: 83, n: "S" },
+ VK_D: { c: 68, n: "D" },
+ VK_F: { c: 70, n: "F" },
+ VK_G: { c: 71, n: "G" },
+ VK_H: { c: 72, n: "H" },
+ VK_J: { c: 74, n: "J" },
+ VK_K: { c: 75, n: "K" },
+ VK_L: { c: 76, n: "L" },
+ VK_CEDILLA: { c: 186, n: "Ç" },
+ VK_TILDE: { c: 222, n: "~" },
+ VK_ENTER: { c: 13, n: "Enter" },
+ VK_SHIFT: { c: 16, n: "Shift" },
+ VK_BACK_SLASH: { c: 220, n: "\\" },
+ VK_Z: { c: 90, n: "Z" },
+ VK_X: { c: 88, n: "X" },
+ VK_C: { c: 67, n: "C" },
+ VK_V: { c: 86, n: "V" },
+ VK_B: { c: 66, n: "B" },
+ VK_N: { c: 78, n: "N" },
+ VK_M: { c: 77, n: "M" },
+ VK_COMMA: { c: 188, n: "] =" },
+ VK_PERIOD: { c: 190, n: "." },
+ VK_SEMICOLON: { c: 59, n: ";" },
+ VK_SLASH: { c: 191, n: "/" },
+ VK_CONTROL: { c: 17, n: "Ctrl" },
+ VK_ALT: { c: 18, n: "Alt" },
+ VK_COMMAND: { c: 224, n: "Cmd" },
+ VK_SPACE: { c: 32, n: "Space" },
+ VK_INSERT: { c: 45, n: "Ins" },
+ VK_DELETE: { c: 46, n: "Del" },
+ VK_HOME: { c: 36, n: "Home" },
+ VK_END: { c: 35, n: "End" },
+ VK_PAGE_UP: { c: 33, n: "PgUp" },
+ VK_PAGE_DOWN: { c: 34, n: "PgDown" },
+ VK_UP: { c: 38, n: "Up" },
+ VK_DOWN: { c: 40, n: "Down" },
+ VK_LEFT: { c: 37, n: "Left" },
+ VK_RIGHT: { c: 39, n: "Right" },
+ VK_NUM_LOCK: { c: 144, n: "Num" },
+ VK_DIVIDE: { c: 111, n: "Num /" },
+ VK_MULTIPLY: { c: 106, n: "Num *" },
+ VK_SUBTRACT: { c: 109, n: "Num -" },
+ VK_ADD: { c: 107, n: "Num +" },
+ VK_DECIMAL: { c: 194, n: "Num ." },
+ VK_NUMPAD0: { c: 96, n: "Num 0" },
+ VK_NUMPAD1: { c: 97, n: "Num 1" },
+ VK_NUMPAD2: { c: 98, n: "Num 2" },
+ VK_NUMPAD3: { c: 99, n: "Num 3" },
+ VK_NUMPAD4: { c: 100, n: "Num 4" },
+ VK_NUMPAD5: { c: 101, n: "Num 5" },
+ VK_NUMPAD6: { c: 102, n: "Num 6" },
+ VK_NUMPAD7: { c: 103, n: "Num 7" },
+ VK_NUMPAD8: { c: 104, n: "Num 8" },
+ VK_NUMPAD9: { c: 105, n: "Num 9" },
+ VK_NUMPAD_CENTER: { c: 12, n: "Num Cntr" }
};
function _metakeyflags(e) {
- return (e.shiftKey?KeyFlags.Shift:0) |
- (e.ctrlKey?KeyFlags.Ctrl:0) |
- (e.altKey?KeyFlags.Alt:0) |
- (e.metaKey?KeyFlags.Meta:0);
+ return (e.shiftKey ? KeyFlags.Shift : 0) |
+ (e.ctrlKey ? KeyFlags.Ctrl : 0) |
+ (e.altKey ? KeyFlags.Alt : 0) |
+ (e.metaKey ? KeyFlags.Meta : 0);
}
-type KeyMapFunction = (o:KeyMapEntry, key:number, code:number, flags:number) => void;
+type KeyMapFunction = (o: KeyMapEntry, key: number, code: number, flags: number) => void;
-export function newKeyboardHandler(switches:number[]|Uint8Array, map:KeyCodeMap, func?:KeyMapFunction, alwaysfunc?:boolean) {
- return (key:number,code:number,flags:number) => {
+export function newKeyboardHandler(switches: number[] | Uint8Array, map: KeyCodeMap, func?: KeyMapFunction, alwaysfunc?: boolean) {
+ return (key: number, code: number, flags: number) => {
if (!map) {
func(null, key, code, flags);
return;
}
- var o : KeyMapEntry = map[key];
+ var o: KeyMapEntry = map[key];
if (!o) o = map[0];
if (func && (o || alwaysfunc)) {
func(o, key, code, flags);
@@ -538,23 +548,23 @@ export function newKeyboardHandler(switches:number[]|Uint8Array, map:KeyCodeMap,
};
}
-export function setKeyboardFromMap(video:RasterVideo, switches:number[]|Uint8Array, map:KeyCodeMap, func?:KeyMapFunction, alwaysfunc?:boolean) {
+export function setKeyboardFromMap(video: RasterVideo, switches: number[] | Uint8Array, map: KeyCodeMap, func?: KeyMapFunction, alwaysfunc?: boolean) {
var handler = newKeyboardHandler(switches, map, func, alwaysfunc);
video.setKeyboardEvents(handler);
return new ControllerPoller(handler);
}
-export function makeKeycodeMap(table : [KeyDef,number,number][]) : KeyCodeMap {
- var map = new Map();
- for (var i=0; i();
+ for (var i = 0; i < table.length; i++) {
var entry = table[i];
- var val : KeyMapEntry = {index:entry[1], mask:entry[2], def:entry[0]};
+ var val: KeyMapEntry = { index: entry[1], mask: entry[2], def: entry[0] };
map[entry[0].c] = val;
}
return map;
}
-const DEFAULT_CONTROLLER_KEYS : KeyDef[] = [
+const DEFAULT_CONTROLLER_KEYS: KeyDef[] = [
Keys.UP, Keys.DOWN, Keys.LEFT, Keys.RIGHT, Keys.A, Keys.B, Keys.SELECT, Keys.START,
Keys.P2_UP, Keys.P2_DOWN, Keys.P2_LEFT, Keys.P2_RIGHT, Keys.P2_A, Keys.P2_B, Keys.P2_SELECT, Keys.P2_START,
];
@@ -562,10 +572,10 @@ const DEFAULT_CONTROLLER_KEYS : KeyDef[] = [
export class ControllerPoller {
active = false;
handler;
- state : Int32Array[];
- lastState : Int32Array[];
+ state: Int32Array[];
+ lastState: Int32Array[];
AXIS0 = 24; // first joystick axis index
- constructor(handler:(key,code,flags) => void) {
+ constructor(handler: (key, code, flags) => void) {
this.handler = handler;
window.addEventListener("gamepadconnected", (event) => {
console.log("Gamepad connected:", event);
@@ -582,7 +592,7 @@ export class ControllerPoller {
let numGamepads = navigator.getGamepads().length;
this.state = new Array(numGamepads);
this.lastState = new Array(numGamepads);
- for (var i=0; i len) {
throw Error("Data too long, " + data.length + " > " + len);
}
var r = new RAM(len);
if (padstart)
- r.mem.set(data, len-data.length);
+ r.mem.set(data, len - data.length);
else
r.mem.set(data);
return r.mem;
}
-type AddressReadWriteFn = ((a:number) => number) | ((a:number,v:number) => void);
+type AddressReadWriteFn = ((a: number) => number) | ((a: number, v: number) => void);
type AddressDecoderEntry = [number, number, number, AddressReadWriteFn];
-type AddressDecoderOptions = {gmask?:number, defaultval?:number};
+type AddressDecoderOptions = { gmask?: number, defaultval?: number };
// TODO: better performance, check values
-export function AddressDecoder(table : AddressDecoderEntry[], options?:AddressDecoderOptions) {
+export function AddressDecoder(table: AddressDecoderEntry[], options?: AddressDecoderOptions) {
var self = this;
function makeFunction() {
var s = "";
if (options && options.gmask) {
s += "a&=" + options.gmask + ";";
}
- for (var i=0; i=" + start + " && a<="+end + "){";
- if (mask) s += "a&="+mask+";";
- s += "return this.__fn"+i+"(a,v)&0xff;}\n";
+ self['__fn' + i] = func;
+ s += "if (a>=" + start + " && a<=" + end + "){";
+ if (mask) s += "a&=" + mask + ";";
+ s += "return this.__fn" + i + "(a,v)&0xff;}\n";
}
- s += "return "+(options?.defaultval|0)+";";
+ s += "return " + (options?.defaultval | 0) + ";";
return new Function('a', 'v', s);
}
return makeFunction().bind(self);
}
-export function newAddressDecoder(table : AddressDecoderEntry[], options?:AddressDecoderOptions) : (a:number,v?:number) => number {
+export function newAddressDecoder(table: AddressDecoderEntry[], options?: AddressDecoderOptions): (a: number, v?: number) => number {
return new (AddressDecoder as any)(table, options);
}
// https://stackoverflow.com/questions/17130395/real-mouse-position-in-canvas
-export function getMousePos(canvas : HTMLCanvasElement, evt) : {x:number,y:number} {
+export function getMousePos(canvas: HTMLCanvasElement, evt): { x: number, y: number } {
+ // Improves reachability of min and max values at edges of canvas. Without delta,
+ // min and max values would be represented by 1/2 as many pixels as other values.
+ const delta = .5;
+ // Canvas HTML element has integer `width` and `height`, but getBoundingClientRect() pos & size
+ // are scaled to non-integer values. Mouse events return integer x/y values in the range
+ // [Math.floor(rect.left/top), Math.floor(rect.left/top) + Math.floor(rect.width/height)],
+ // which may not be the same as [Math.floor(rect.left/top), Math.floor(rect.right/bottom)].
var rect = canvas.getBoundingClientRect(), // abs. size of element
- scaleX = canvas.width / rect.width, // relationship bitmap vs. element for X
- scaleY = canvas.height / rect.height; // relationship bitmap vs. element for Y
-
+ // Ignore canvas borders and padding, so mouse position is relative to canvas content.
+ style = window.getComputedStyle(canvas),
+ left = parseFloat(style.borderLeftWidth) + parseFloat(style.paddingLeft),
+ right = parseFloat(style.borderRightWidth) + parseFloat(style.paddingRight),
+ top = parseFloat(style.borderTopWidth) + parseFloat(style.paddingTop),
+ bottom = parseFloat(style.borderBottomWidth) + parseFloat(style.paddingBottom),
+ width = rect.width - left - right,
+ height = rect.height - top - bottom,
+
+ // Determine scale factors to map mouse coordinates to canvas coordinates.
+ scaleX = canvas.width / Math.floor(width - 2 * delta),
+ scaleY = canvas.height / Math.floor(height - 2 * delta),
+
+ // Scale coordinates after they've been adjusted to be relative to canvas content.
+ x = (evt.clientX - delta - rect.left - left) * scaleX,
+ y = (evt.clientY - delta - rect.top - top) * scaleY;
return {
- x: (evt.clientX - rect.left) * scaleX, // scale mouse coordinates after they have
- y: (evt.clientY - rect.top) * scaleY // been adjusted to be relative to element
+ // Clamp to [0, canvas.width/height] to prevent non-integer rect position
+ // and size, and potentially `delta`, from returning out of bounds values.
+ x: Math.max(0, Math.min(canvas.width, x)),
+ y: Math.max(0, Math.min(canvas.height, y))
}
}
///
// TODO: https://stackoverflow.com/questions/10463518/converting-em-to-px-in-javascript-and-getting-default-font-size
-export function getVisibleEditorLineHeight() : number{
+export function getVisibleEditorLineHeight(): number {
return $("#booksMenuButton").first().height();
}
export interface VirtualTextLine {
- text : string;
- clas? : string;
+ text: string;
+ clas?: string;
}
export class VirtualTextScroller {
memorylist;
- maindiv : HTMLElement;
- getLineAt : (row:number) => VirtualTextLine;
+ maindiv: HTMLElement;
+ getLineAt: (row: number) => VirtualTextLine;
- constructor(parent : HTMLElement) {
+ constructor(parent: HTMLElement) {
var div = document.createElement('div');
div.setAttribute("class", "memdump");
parent.appendChild(div);
this.maindiv = div;
}
-
- create(workspace : HTMLElement, maxRowCount : number, fn : (row:number) => VirtualTextLine) {
+
+ create(workspace: HTMLElement, maxRowCount: number, fn: (row: number) => VirtualTextLine) {
this.getLineAt = fn;
this.memorylist = new VirtualList({
w: $(workspace).width(),
h: $(workspace).height(),
itemHeight: getVisibleEditorLineHeight(),
totalRows: maxRowCount, // TODO?
- generatorFn: (row : number) => {
+ generatorFn: (row: number) => {
var line = fn(row);
var linediv = document.createElement("div");
linediv.appendChild(document.createTextNode(line.text));
@@ -747,7 +779,7 @@ export class VirtualTextScroller {
// TODO: refactor with elsewhere
refresh() {
if (this.memorylist) {
- $(this.maindiv).find('[data-index]').each( (i,e) => {
+ $(this.maindiv).find('[data-index]').each((i, e) => {
var div = e;
var row = parseInt(div.getAttribute('data-index'));
var oldtext = div.innerText;
@@ -783,5 +815,5 @@ export function gtia_ntsc_to_rgb(val: number) {
var r = y + 0.956 * i + 0.621 * q;
var g = y - 0.272 * i - 0.647 * q;
var b = y - 1.107 * i + 1.704 * q;
- return RGBA(clamp(0,255,r), clamp(0,255,g), clamp(0,255,b));
+ return RGBA(clamp(0, 255, r), clamp(0, 255, g), clamp(0, 255, b));
}
diff --git a/src/machine/apple2.ts b/src/machine/apple2.ts
index c8c96a959..c75072686 100644
--- a/src/machine/apple2.ts
+++ b/src/machine/apple2.ts
@@ -1,171 +1,182 @@
-
import { MOS6502, MOS6502State } from "../common/cpu/MOS6502";
-import { Bus, BasicScanlineMachine, SavesState, AcceptsBIOS } from "../common/devices";
+import { Bus, BasicScanlineMachine, SavesState, AcceptsBIOS, AcceptsPaddleInput } from "../common/devices";
import { KeyFlags } from "../common/emu"; // TODO
import { hex, lzgmini, stringToByteArray, RGBA, printFlags, arrayCompare } from "../common/util";
interface AppleIIStateBase {
- ram : Uint8Array;
- soundstate : number;
- auxRAMselected,writeinhibit : boolean;
- auxRAMbank : number;
+ ram: Uint8Array;
+ soundstate: number;
+ auxRAMselected, writeinhibit: boolean;
+ auxRAMbank: number;
}
interface AppleIIControlsState {
- inputs : Uint8Array; // unused?
- kbdlatch : number;
+ inputs: Uint8Array; // unused?
+ kbdlatch: number;
}
interface AppleIIState extends AppleIIStateBase, AppleIIControlsState {
- c : MOS6502State;
- grswitch : number;
- slots: SlotDevice[];
+ c: MOS6502State;
+ grswitch: number;
+ slots: SlotDevice[];
+ paddleValues: number[];
+ paddleButtons: boolean[];
}
interface SlotDevice extends Bus {
- readROM(address: number) : number;
- readConst(address: number) : number;
+ readROM(address: number): number;
+ readConst(address: number): number;
}
-export class AppleII extends BasicScanlineMachine implements AcceptsBIOS {
+export class AppleII extends BasicScanlineMachine implements AcceptsBIOS, AcceptsPaddleInput {
// approx: http://www.cs.columbia.edu/~sedwards/apple2fpga/
- cpuFrequency = 1022727;
- sampleRate = this.cpuFrequency;
- cpuCyclesPerLine = 65;
- cpuCyclesPerFrame = this.cpuCyclesPerLine * 262;
- canvasWidth = 280;
- numVisibleScanlines = 192;
- numTotalScanlines = 262;
- defaultROMSize = 0x13000; // we'll never need one that big, but...
-
- // these are set later
- LOAD_BASE = 0;
- HDR_SIZE = 0;
-
- ram = new Uint8Array(0x13000); // 64K + 16K LC RAM - 4K hardware + 12K ROM
- bios : Uint8Array;
- cpu = new MOS6502();
- grdirty = new Array(0xc000 >> 7);
- grparams = {dirty:this.grdirty, grswitch:GR_TXMODE, mem:this.ram};
- ap2disp;
- kbdlatch = 0;
- soundstate = 0;
- // language card switches
- auxRAMselected = false;
- auxRAMbank = 1;
- writeinhibit = true;
- // value to add when reading & writing each of these banks
- // bank 1 is E000-FFFF, bank 2 is D000-DFFF
- bank2rdoffset=0;
- bank2wroffset=0;
- // disk II
- slots : SlotDevice[] = new Array(8);
- // fake disk drive that loads program into RAM
- fakeDrive : SlotDevice = {
- readROM: (a) => {
- var pc = this.cpu.getPC();
- if (pc >= 0xC600 && pc < 0xC700) {
- // We're reading code to EXECUTE.
- // Load the built program directly into memory, and "read"
- // a JMP directly to it.
- //console.log(`fakeDrive (EXEC): ${a.toString(16)}`);
- switch (a) {
- // JMP VM_BASE
- case 0:
- // SHOULD load program into RAM here, but have to do it
- // below instead.
- return 0;
- case 1: return this.LOAD_BASE&0xff;
- case 2: return (this.LOAD_BASE>>8)&0xff;
- default: return 0;
- }
- }
- else {
- // We're reading code, but not executing it.
- // This is probably the Monitor routine to identify whether
- // this slot is a Disk ][ drive, so... give it what it wants.
- //console.log(`fakeDrive (NOEX): ${a.toString(16)}`);
- switch (a) {
- case 0:
- // Actually, if we get here, we probably ARE being
- // executed. For some reason, the instruction at $C600
- // gets read for execution, BEFORE the PC gets set to
- // the correct location. So we handle loading the program
- // into RAM and returning the JMP here, instead of above
- // where it would otherwise belong.
- if (this.rom) {
- this.loadRAMWithProgram();
- }
- return 0x4c; // JMP
- case 1: return 0x20;
- case 3: return 0x00;
- case 5: return 0x03;
- case 7: return 0x3c;
- default: return 0;
- }
- }
- },
- readConst: (a) => {
- return 0;
- },
- read: (a) => { return this.floatbus(); },
- write: (a,v) => { }
- };
-
- constructor() {
- super();
- this.loadBIOS(new lzgmini().decode(stringToByteArray(atob(APPLEIIGO_LZG))));
- this.connectCPUMemoryBus(this);
- // This line is inappropriate for real ROMs, but was there for
- // the APPLE][GO ROM, so keeping it only in the constructor, for
- // that special case (in case it really is important for this
- // address to be an RTS).
- this.bios[0xD39A - (0x10000 - this.bios.length)] = 0x60; // $d39a = RTS
- }
- saveState() : AppleIIState {
- // TODO: automagic
- return {
- c: this.cpu.saveState(),
- ram: this.ram.slice(),
- kbdlatch: this.kbdlatch,
- soundstate: this.soundstate,
- grswitch: this.grparams.grswitch,
- auxRAMselected: this.auxRAMselected,
- auxRAMbank: this.auxRAMbank,
- writeinhibit: this.writeinhibit,
- slots: this.slots.map((slot) => { return slot && slot['saveState'] && slot['saveState']() }),
- inputs: this.ram.slice(0,0) // unused
- };
- }
- loadState(s:AppleIIState) {
- this.cpu.loadState(s.c);
- this.ram.set(s.ram);
- this.kbdlatch = s.kbdlatch;
- this.soundstate = s.soundstate;
- this.grparams.grswitch = s.grswitch;
- this.auxRAMselected = s.auxRAMselected;
- this.auxRAMbank = s.auxRAMbank;
- this.writeinhibit = s.writeinhibit;
- this.setupLanguageCardConstants();
- for (var i=0; i> 7);
+ grparams = { dirty: this.grdirty, grswitch: GR_TXMODE, mem: this.ram };
+ ap2disp;
+ kbdlatch = 0;
+ soundstate = 0;
+ // language card switches
+ auxRAMselected = false;
+ auxRAMbank = 1;
+ writeinhibit = true;
+ // value to add when reading & writing each of these banks
+ // bank 1 is E000-FFFF, bank 2 is D000-DFFF
+ bank2rdoffset = 0;
+ bank2wroffset = 0;
+ // Paddle/joystick, PDL0-PDL3 values (0-255).
+ paddleValues = [0, 0, 0, 0];
+ paddleButtons = [false, false, false];
+ paddleLastTriggered = 0;
+ // disk II
+ slots: SlotDevice[] = new Array(8);
+ // fake disk drive that loads program into RAM
+ fakeDrive: SlotDevice = {
+ readROM: (a) => {
+ var pc = this.cpu.getPC();
+ if (pc >= 0xC600 && pc < 0xC700) {
+ // We're reading code to EXECUTE.
+ // Load the built program directly into memory, and "read"
+ // a JMP directly to it.
+ //console.log(`fakeDrive (EXEC): ${a.toString(16)}`);
+ switch (a) {
+ // JMP VM_BASE
+ case 0:
+ // SHOULD load program into RAM here, but have to do it
+ // below instead.
+ return 0;
+ case 1: return this.LOAD_BASE & 0xff;
+ case 2: return (this.LOAD_BASE >> 8) & 0xff;
+ default: return 0;
+ }
+ }
+ else {
+ // We're reading code, but not executing it.
+ // This is probably the Monitor routine to identify whether
+ // this slot is a Disk ][ drive, so... give it what it wants.
+ //console.log(`fakeDrive (NOEX): ${a.toString(16)}`);
+ switch (a) {
+ case 0:
+ // Actually, if we get here, we probably ARE being
+ // executed. For some reason, the instruction at $C600
+ // gets read for execution, BEFORE the PC gets set to
+ // the correct location. So we handle loading the program
+ // into RAM and returning the JMP here, instead of above
+ // where it would otherwise belong.
+ if (this.rom) {
+ this.loadRAMWithProgram();
+ }
+ return 0x4c; // JMP
+ case 1: return 0x20;
+ case 3: return 0x00;
+ case 5: return 0x03;
+ case 7: return 0x3c;
+ default: return 0;
+ }
+ }
+ },
+ readConst: (a) => {
+ return 0;
+ },
+ read: (a) => { return this.floatbus(); },
+ write: (a, v) => { }
+ };
+
+ constructor() {
+ super();
+ this.loadBIOS(new lzgmini().decode(stringToByteArray(atob(APPLEIIGO_LZG))));
+ this.connectCPUMemoryBus(this);
+ // This line is inappropriate for real ROMs, but was there for
+ // the APPLE][GO ROM, so keeping it only in the constructor, for
+ // that special case (in case it really is important for this
+ // address to be an RTS).
+ this.bios[0xD39A - (0x10000 - this.bios.length)] = 0x60; // $d39a = RTS
+ }
+ saveState(): AppleIIState {
+ // TODO: automagic
+ return {
+ c: this.cpu.saveState(),
+ ram: this.ram.slice(),
+ kbdlatch: this.kbdlatch,
+ soundstate: this.soundstate,
+ grswitch: this.grparams.grswitch,
+ auxRAMselected: this.auxRAMselected,
+ auxRAMbank: this.auxRAMbank,
+ writeinhibit: this.writeinhibit,
+ slots: this.slots.map((slot) => { return slot && slot['saveState'] && slot['saveState']() }),
+ paddleValues: this.paddleValues.slice(),
+ paddleButtons: this.paddleButtons.slice(),
+ inputs: this.ram.slice(0, 0) // unused
+ };
+ }
+ loadState(s: AppleIIState) {
+ this.cpu.loadState(s.c);
+ this.ram.set(s.ram);
+ this.kbdlatch = s.kbdlatch;
+ this.soundstate = s.soundstate;
+ this.grparams.grswitch = s.grswitch;
+ this.auxRAMselected = s.auxRAMselected;
+ this.auxRAMbank = s.auxRAMbank;
+ this.writeinhibit = s.writeinhibit;
+ this.setupLanguageCardConstants();
+ for (var i = 0; i < this.slots.length; i++)
+ if (this.slots[i] && this.slots[i]['loadState'])
+ this.slots[i]['loadState'](s.slots[i]);
+ this.paddleValues = s.paddleValues.slice();
+ this.paddleButtons = s.paddleButtons.slice();
+ this.ap2disp.invalidate(); // repaint entire screen
+ }
+ saveControlsState(): AppleIIControlsState {
+ return { inputs: null, kbdlatch: this.kbdlatch };
+ }
+ loadControlsState(s: AppleIIControlsState) {
+ this.kbdlatch = s.kbdlatch;
+ }
+ loadBIOS(data, title?) {
if (data.length != 0x3000) {
- console.log(`apple2 loadBIOS !!!WARNING!!!: BIOS wants length 0x3000, but BIOS '${title}' has length 0x${data.length.toString(16)}`);
- console.log("will load BIOS to end of memory anyway...");
+ console.log(`apple2 loadBIOS !!!WARNING!!!: BIOS wants length 0x3000, but BIOS '${title}' has length 0x${data.length.toString(16)}`);
+ console.log("will load BIOS to end of memory anyway...");
}
this.bios = Uint8Array.from(data);
- }
+ }
loadROM(data) {
// is it a 16-sector 35-track disk image?
if (data.length == 16 * 35 * 256) {
@@ -209,27 +220,27 @@ export class AppleII extends BasicScanlineMachine implements AcceptsBIOS {
this.ram[0xbf6f] = 0x01;
}
}
- reset() {
- this.auxRAMselected = false;
- this.auxRAMbank = 1;
- this.writeinhibit = true;
- this.ram.fill(0, 0x300, 0x400); // Clear soft-reset vector
- // (force hard reset)
- super.reset();
- this.skipboot();
- }
- skipboot() {
- // execute until $c600 boot
- for (var i=0; i<2000000; i++) {
- this.cpu.advanceClock();
- if ((this.cpu.getPC()>>8) == 0xc6) break;
- }
- // get out of $c600 boot
- for (var i=0; i<2000000; i++) {
- this.cpu.advanceClock();
- if ((this.cpu.getPC()>>8) < 0xc6) break;
- }
- }
+ reset() {
+ this.auxRAMselected = false;
+ this.auxRAMbank = 1;
+ this.writeinhibit = true;
+ this.ram.fill(0, 0x300, 0x400); // Clear soft-reset vector
+ // (force hard reset)
+ super.reset();
+ this.skipboot();
+ }
+ skipboot() {
+ // execute until $c600 boot
+ for (var i = 0; i < 2000000; i++) {
+ this.cpu.advanceClock();
+ if ((this.cpu.getPC() >> 8) == 0xc6) break;
+ }
+ // get out of $c600 boot
+ for (var i = 0; i < 2000000; i++) {
+ this.cpu.advanceClock();
+ if ((this.cpu.getPC() >> 8) < 0xc6) break;
+ }
+ }
readConst(address: number): number {
if (address < 0xc000) {
return this.ram[address];
@@ -247,459 +258,482 @@ export class AppleII extends BasicScanlineMachine implements AcceptsBIOS {
return 0;
}
}
- read(address:number) : number {
- address &= 0xffff;
- if (address < 0xc000 || address >= 0xd000) {
- return this.readConst(address);
- } else if (address < 0xc100) {
- this.probe.logIORead(address, 0); // TODO: value
- var slot = (address >> 4) & 0x0f;
- switch (slot)
- {
- case 0:
- return this.kbdlatch;
- case 1:
- this.kbdlatch &= 0x7f;
- break;
- case 3:
- this.soundstate = this.soundstate ^ 1;
- break;
- case 5:
- if ((address & 0x0f) < 8) {
- // graphics
- if ((address & 1) != 0)
- this.grparams.grswitch |= 1 << ((address >> 1) & 0x07);
- else
- this.grparams.grswitch &= ~(1 << ((address >> 1) & 0x07));
- }
- break;
- case 6:
- // tapein, joystick, buttons
- switch (address & 7) {
- // buttons (off)
- case 1:
- case 2:
- case 3:
- return this.floatbus() & 0x7f;
- // joystick
- case 4:
- case 5:
+ read(address: number): number {
+ address &= 0xffff;
+ if (address < 0xc000 || address >= 0xd000) {
+ return this.readConst(address);
+ } else if (address < 0xc100) {
+ this.probe.logIORead(address, 0); // TODO: value
+ var slot = (address >> 4) & 0x0f;
+ switch (slot) {
+ case 0: // $C00x
+ return this.kbdlatch;
+ case 1: // $C01x
+ this.kbdlatch &= 0x7f;
+ break;
+ case 3: // $C03x
+ this.soundstate = this.soundstate ^ 1;
+ break;
+ case 5: // $C05x
+ if ((address & 0x0f) < 8) {
+ // graphics
+ if ((address & 1) != 0)
+ this.grparams.grswitch |= 1 << ((address >> 1) & 0x07);
+ else
+ this.grparams.grswitch &= ~(1 << ((address >> 1) & 0x07));
+ }
+ break;
+ case 6: // $C06x
+ // tapein, joystick, buttons
+ switch (address & 7) {
+ case 1: // $C061 / $C069 - GAME SW0
+ case 2: // $C062 / $C06A - GAME SW1
+ case 3: // $C063 / $C06B - GAME SW2
+ // buttons
+ var btn = (address & 7) - 1;
+ if (this.paddleButtons[btn])
+ return this.floatbus() | 0x80;
+ else
+ return this.floatbus() & 0x7f;
+ case 4: // $C064 / $C06C - GAME PDL0
+ case 5: // $C065 / $C06D - GAME PDL1
+ case 6: // $C066 / $C06E - GAME PDL2
+ case 7: // $C067 / $C06F - GAME PDL3
+ var pdl = (address & 0x0f) - 4;
+ var elapsed = this.frameCycles - this.paddleLastTriggered;
+ // Bit 7 remains high until paddle value reached.
+ if (elapsed < this.paddleValues[pdl] * this.cpuCyclesPaddleUnit)
+ return this.floatbus() | 0x80;
+ else
+ return this.floatbus() & 0x7f;
+ default:
+ return this.floatbus();
+ }
+ case 7: // $C07x
+ // Strobing PTRIG ($C070) triggers paddles.
+ if (address == 0xc070) {
+ this.paddleLastTriggered = this.frameCycles;
return this.floatbus() | 0x80;
- default:
- return this.floatbus();
- }
- case 7:
- // joy reset
- if (address == 0xc070)
- return this.floatbus() | 0x80;
- case 8:
- return this.doLanguageCardIO(address);
- case 9: case 10: case 11: case 12: case 13: case 14: case 15:
- return (this.slots[slot-8] && this.slots[slot-8].read(address & 0xf)) | 0;
+ }
+ case 8: // $C08x
+ return this.doLanguageCardIO(address);
+ case 9: // $C09x
+ case 10: // $C0Ax
+ case 11: // $C0Bx
+ case 12: // $C0Cx
+ case 13: // $C0Dx
+ case 14: // $C0Ex
+ case 15: // $C0Fx
+ return (this.slots[slot - 8] && this.slots[slot - 8].read(address & 0xf)) | 0;
+ }
+ } else if (address >= 0xc100 && address < 0xc800) {
+ var slot = (address >> 8) & 7;
+ return (this.slots[slot] && this.slots[slot].readROM(address & 0xff)) | 0;
}
- } else if (address >= 0xc100 && address < 0xc800) {
- var slot = (address >> 8) & 7;
- return (this.slots[slot] && this.slots[slot].readROM(address & 0xff)) | 0;
- }
- return this.floatbus();
- }
- write(address:number, val:number) : void {
- address &= 0xffff;
- val &= 0xff;
- if (address < 0xc000) {
- this.ram[address] = val;
- this.grdirty[address>>7] = 1;
- } else if (address < 0xc090) {
- this.read(address); // strobe address, discard result
- } else if (address < 0xc100) {
- var slot = (address >> 4) & 0x0f;
- this.slots[slot-8] && this.slots[slot-8].write(address & 0xf, val);
- this.probe.logIOWrite(address, val);
- } else if (address >= 0xd000 && !this.writeinhibit) {
- if (address >= 0xe000)
- this.ram[address] = val;
- else
- this.ram[address + this.bank2wroffset] = val;
- }
- }
- // http://www.deater.net/weave/vmwprod/megademo/vapor_lock.html
- // https://retrocomputing.stackexchange.com/questions/14012/what-is-dram-refresh-and-why-is-the-weird-apple-ii-video-memory-layout-affected
- // http://www.apple-iigs.info/doc/fichiers/TheappleIIcircuitdescription1.pdf
- // http://rich12345.tripod.com/aiivideo/softalk.html
- // https://github.com/MiSTer-devel/Apple-II_MiSTer/blob/master/rtl/timing_generator.vhd
- floatbus() : number {
- var fcyc = this.frameCycles;
- var yline = Math.floor(fcyc / 65);
- var xcyc = Math.floor(fcyc % 65);
- var addr = this.ap2disp.getAddressForScanline(yline);
- return this.readConst(addr + xcyc);
- }
-
- connectVideo(pixels:Uint32Array) {
- super.connectVideo(pixels);
- this.ap2disp = this.pixels && new Apple2Display(this.pixels, this.grparams);
- }
- startScanline() {
- }
- drawScanline() {
- // TODO: draw scanline via ap2disp
- }
- advanceFrame(trap) : number {
- var clocks = super.advanceFrame(trap);
- this.ap2disp && this.ap2disp.updateScreen();
- return clocks;
- }
- advanceCPU() {
- this.audio.feedSample(this.soundstate, 1);
- return super.advanceCPU();
- }
-
- setKeyInput(key:number, code:number, flags:number) : void {
- console.log(`setKeyInput: ${key} ${code} ${flags}`);
- if (flags & KeyFlags.KeyDown) {
- code = 0;
- switch (key) {
- case 16: case 17: case 18: case 91:
- return; // ignore shift/ctrl/alt - don't set any key
- case 8:
- code=8; // left
- if (flags & KeyFlags.Shift) {
- // (possibly) soft reset
- this.cpu.reset();
- return;
- }
- break;
- case 13: code=13; break; // return
- case 27: code=27; break; // escape
- case 37: code=8; break; // left
- case 39: code=21; break; // right
- case 38: code=11; break; // up
- case 40: code=10; break; // down
- case 48: code = (flags & KeyFlags.Shift) ? 0x29 : 0x30; break; // ) or 0
- case 49: code = (flags & KeyFlags.Shift) ? 0x21 : 0x31; break; // ! or 1
- case 50: code = (flags & KeyFlags.Shift) ? 0x40 : 0x32; break; // @ or 2
- case 51: code = (flags & KeyFlags.Shift) ? 0x23 : 0x33; break; // # or 3
- case 52: code = (flags & KeyFlags.Shift) ? 0x24 : 0x34; break; // $ or 4
- case 53: code = (flags & KeyFlags.Shift) ? 0x25 : 0x35; break; // % or 5
- case 54: code = (flags & KeyFlags.Shift) ? 0x5e : 0x36; break; // ^ or 6
- case 55: code = (flags & KeyFlags.Shift) ? 0x26 : 0x37; break; // & or 7
- case 56: code = (flags & KeyFlags.Shift) ? 0x2a : 0x38; break; // * or 8
- case 57: code = (flags & KeyFlags.Shift) ? 0x28 : 0x39; break; // ( or 9
- case 61: code = (flags & KeyFlags.Shift) ? 0x5f : 0x2d; break; // _ or -
- case 173: code = (flags & KeyFlags.Shift) ? 0x2b : 0x3d; break; // + or =
- case 59: code = (flags & KeyFlags.Shift) ? 0x3a : 0x3b; break; // : or ;
- case 186: code = (flags & KeyFlags.Shift) ? 0x3a : 0x3b; break; // : or ;
- case 187: code = (flags & KeyFlags.Shift) ? 0x2b : 0x3d; break; // + or =
- case 188: code = (flags & KeyFlags.Shift) ? 0x3c : 0x2c; break;
- case 189: code = (flags & KeyFlags.Shift) ? 0x5f : 0x2d; break; // _ or -
- case 190: code = (flags & KeyFlags.Shift) ? 0x3e : 0x2e; break;
- case 191: code = (flags & KeyFlags.Shift) ? 0x3f : 0x2f; break;
- case 222: code = (flags & KeyFlags.Shift) ? 0x22 : 0x27; break;
- default:
- code = key;
- // convert to uppercase for Apple ][
- if (code >= 0x61 && code <= 0x7a) code -= 32;
- // convert to control codes if Ctrl pressed
- if (code >= 65 && code < 65+26) {
- if (flags & KeyFlags.Ctrl) code -= 64; // ctrl
+ return this.floatbus();
+ }
+ write(address: number, val: number): void {
+ address &= 0xffff;
+ val &= 0xff;
+ if (address < 0xc000) {
+ this.ram[address] = val;
+ this.grdirty[address >> 7] = 1;
+ } else if (address < 0xc090) {
+ this.read(address); // strobe address, discard result
+ } else if (address < 0xc100) {
+ var slot = (address >> 4) & 0x0f;
+ this.slots[slot - 8] && this.slots[slot - 8].write(address & 0xf, val);
+ this.probe.logIOWrite(address, val);
+ } else if (address >= 0xd000 && !this.writeinhibit) {
+ if (address >= 0xe000)
+ this.ram[address] = val;
+ else
+ this.ram[address + this.bank2wroffset] = val;
+ }
+ }
+ // http://www.deater.net/weave/vmwprod/megademo/vapor_lock.html
+ // https://retrocomputing.stackexchange.com/questions/14012/what-is-dram-refresh-and-why-is-the-weird-apple-ii-video-memory-layout-affected
+ // http://www.apple-iigs.info/doc/fichiers/TheappleIIcircuitdescription1.pdf
+ // http://rich12345.tripod.com/aiivideo/softalk.html
+ // https://github.com/MiSTer-devel/Apple-II_MiSTer/blob/master/rtl/timing_generator.vhd
+ floatbus(): number {
+ var fcyc = this.frameCycles;
+ var yline = Math.floor(fcyc / 65);
+ var xcyc = Math.floor(fcyc % 65);
+ var addr = this.ap2disp.getAddressForScanline(yline);
+ return this.readConst(addr + xcyc);
+ }
+
+ connectVideo(pixels: Uint32Array) {
+ super.connectVideo(pixels);
+ this.ap2disp = this.pixels && new Apple2Display(this.pixels, this.grparams);
+ }
+ startScanline() {
+ }
+ drawScanline() {
+ // TODO: draw scanline via ap2disp
+ }
+ advanceFrame(trap): number {
+ var clocks = super.advanceFrame(trap);
+ this.ap2disp && this.ap2disp.updateScreen();
+ return clocks;
+ }
+ postFrame() {
+ this.paddleLastTriggered -= this.frameCycles;
+ // Prevent extreme negative values if paddles aren't read.
+ if (this.paddleLastTriggered < -this.cpuCyclesPaddleMax) {
+ this.paddleLastTriggered = -this.cpuCyclesPaddleMax;
+ }
+ }
+ advanceCPU() {
+ this.audio.feedSample(this.soundstate, 1);
+ return super.advanceCPU();
+ }
+
+ setKeyInput(key: number, code: number, flags: number): void {
+ console.log(`setKeyInput: ${key} ${code} ${flags}`);
+ if (flags & KeyFlags.KeyDown) {
+ code = 0;
+ switch (key) {
+ case 16: case 17: case 18: case 91:
+ return; // ignore shift/ctrl/alt - don't set any key
+ case 8:
+ code = 8; // left
+ if (flags & KeyFlags.Shift) {
+ // (possibly) soft reset
+ this.cpu.reset();
+ return;
+ }
+ break;
+ case 13: code = 13; break; // return
+ case 27: code = 27; break; // escape
+ case 37: code = 8; break; // left
+ case 39: code = 21; break; // right
+ case 38: code = 11; break; // up
+ case 40: code = 10; break; // down
+ case 48: code = (flags & KeyFlags.Shift) ? 0x29 : 0x30; break; // ) or 0
+ case 49: code = (flags & KeyFlags.Shift) ? 0x21 : 0x31; break; // ! or 1
+ case 50: code = (flags & KeyFlags.Shift) ? 0x40 : 0x32; break; // @ or 2
+ case 51: code = (flags & KeyFlags.Shift) ? 0x23 : 0x33; break; // # or 3
+ case 52: code = (flags & KeyFlags.Shift) ? 0x24 : 0x34; break; // $ or 4
+ case 53: code = (flags & KeyFlags.Shift) ? 0x25 : 0x35; break; // % or 5
+ case 54: code = (flags & KeyFlags.Shift) ? 0x5e : 0x36; break; // ^ or 6
+ case 55: code = (flags & KeyFlags.Shift) ? 0x26 : 0x37; break; // & or 7
+ case 56: code = (flags & KeyFlags.Shift) ? 0x2a : 0x38; break; // * or 8
+ case 57: code = (flags & KeyFlags.Shift) ? 0x28 : 0x39; break; // ( or 9
+ case 61: code = (flags & KeyFlags.Shift) ? 0x5f : 0x2d; break; // _ or -
+ case 173: code = (flags & KeyFlags.Shift) ? 0x2b : 0x3d; break; // + or =
+ case 59: code = (flags & KeyFlags.Shift) ? 0x3a : 0x3b; break; // : or ;
+ case 186: code = (flags & KeyFlags.Shift) ? 0x3a : 0x3b; break; // : or ;
+ case 187: code = (flags & KeyFlags.Shift) ? 0x2b : 0x3d; break; // + or =
+ case 188: code = (flags & KeyFlags.Shift) ? 0x3c : 0x2c; break;
+ case 189: code = (flags & KeyFlags.Shift) ? 0x5f : 0x2d; break; // _ or -
+ case 190: code = (flags & KeyFlags.Shift) ? 0x3e : 0x2e; break;
+ case 191: code = (flags & KeyFlags.Shift) ? 0x3f : 0x2f; break;
+ case 222: code = (flags & KeyFlags.Shift) ? 0x22 : 0x27; break;
+ default:
+ code = key;
+ // convert to uppercase for Apple ][
+ if (code >= 0x61 && code <= 0x7a) code -= 32;
+ // convert to control codes if Ctrl pressed
+ if (code >= 65 && code < 65 + 26) {
+ if (flags & KeyFlags.Ctrl) code -= 64; // ctrl
+ }
+ }
+ if (code) {
+ this.kbdlatch = (code | 0x80) & 0xff;
}
}
- if (code) {
- this.kbdlatch = (code | 0x80) & 0xff;
+ }
+
+ setPaddleInput(controller: number, value: number): void {
+ if (controller >= 0 && controller < this.paddleValues.length) {
+ this.paddleValues[controller] = value & 0xff;
}
- }
- }
-
- doLanguageCardIO(address:number) {
- // TODO: require two writes in a row for some things
- switch (address & 0x0f) {
+ }
+
+ setPaddleButton(index: number, pressed: boolean): void {
+ if (index >= 0 && index < this.paddleButtons.length) {
+ this.paddleButtons[index] = pressed;
+ }
+ }
+
+ doLanguageCardIO(address: number) {
+ // TODO: require two writes in a row for some things
+ switch (address & 0x0f) {
// Select aux RAM bank 2, write protected.
- case 0x0:
- case 0x4:
- this.auxRAMselected = true;
- this.auxRAMbank = 2;
- this.writeinhibit = true;
- break;
- // Select ROM, write enable aux RAM bank 2.
- case 0x1:
- case 0x5:
- this.auxRAMselected = false;
- this.auxRAMbank = 2;
- this.writeinhibit = false;
- break;
- // Select ROM, write protect aux RAM (either bank).
- case 0x2:
- case 0x6:
- case 0xA:
- case 0xE:
- this.auxRAMselected = false;
- this.writeinhibit = true;
- break;
- // Select aux RAM bank 2, write enabled.
- case 0x3:
- case 0x7:
- this.auxRAMselected = true;
- this.auxRAMbank = 2;
- this.writeinhibit = false;
- break;
- // Select aux RAM bank 1, write protected.
- case 0x8:
- case 0xC:
- this.auxRAMselected = true;
- this.auxRAMbank = 1;
- this.writeinhibit = true;
- break;
- // Select ROM, write enable aux RAM bank 1.
- case 0x9:
- case 0xD:
- this.auxRAMselected = false;
- this.auxRAMbank = 1;
- this.writeinhibit = false;
- break;
- // Select aux RAM bank 1, write enabled.
- case 0xB:
- case 0xF:
- this.auxRAMselected = true;
- this.auxRAMbank = 1;
- this.writeinhibit = false;
- break;
- }
- this.setupLanguageCardConstants();
- return this.floatbus();
- }
-
- setupLanguageCardConstants() {
- // reset language card constants
- if (this.auxRAMbank == 2)
- this.bank2rdoffset = -0x1000; // map 0xd000-0xdfff -> 0xc000-0xcfff
- else
- this.bank2rdoffset = 0x3000; // map 0xd000-0xdfff -> 0x10000-0x10fff
- if (this.auxRAMbank == 2)
- this.bank2wroffset = -0x1000; // map 0xd000-0xdfff -> 0xc000-0xcfff
- else
- this.bank2wroffset = 0x3000; // map 0xd000-0xdfff -> 0x10000-0x10fff
- }
-
- getDebugCategories() {
- return ['CPU','Stack','I/O','Disk'];
- }
- getDebugInfo(category:string, state:AppleIIState) {
- switch (category) {
- case 'I/O': return "AUX RAM Bank: " + state.auxRAMbank +
- "\nAUX RAM Select: " + state.auxRAMselected +
- "\nAUX RAM Write: " + !state.writeinhibit +
- "\n\nGR Switches: " + printFlags(state.grswitch, ["Graphics","Mixed","Page2","Hires"], false) +
- "\n";
- case 'Disk': return (this.slots[6] && this.slots[6]['toLongString'] && this.slots[6]['toLongString']()) || "\n";
- }
- }
+ case 0x0:
+ case 0x4:
+ this.auxRAMselected = true;
+ this.auxRAMbank = 2;
+ this.writeinhibit = true;
+ break;
+ // Select ROM, write enable aux RAM bank 2.
+ case 0x1:
+ case 0x5:
+ this.auxRAMselected = false;
+ this.auxRAMbank = 2;
+ this.writeinhibit = false;
+ break;
+ // Select ROM, write protect aux RAM (either bank).
+ case 0x2:
+ case 0x6:
+ case 0xA:
+ case 0xE:
+ this.auxRAMselected = false;
+ this.writeinhibit = true;
+ break;
+ // Select aux RAM bank 2, write enabled.
+ case 0x3:
+ case 0x7:
+ this.auxRAMselected = true;
+ this.auxRAMbank = 2;
+ this.writeinhibit = false;
+ break;
+ // Select aux RAM bank 1, write protected.
+ case 0x8:
+ case 0xC:
+ this.auxRAMselected = true;
+ this.auxRAMbank = 1;
+ this.writeinhibit = true;
+ break;
+ // Select ROM, write enable aux RAM bank 1.
+ case 0x9:
+ case 0xD:
+ this.auxRAMselected = false;
+ this.auxRAMbank = 1;
+ this.writeinhibit = false;
+ break;
+ // Select aux RAM bank 1, write enabled.
+ case 0xB:
+ case 0xF:
+ this.auxRAMselected = true;
+ this.auxRAMbank = 1;
+ this.writeinhibit = false;
+ break;
+ }
+ this.setupLanguageCardConstants();
+ return this.floatbus();
+ }
+
+ setupLanguageCardConstants() {
+ // reset language card constants
+ if (this.auxRAMbank == 2)
+ this.bank2rdoffset = -0x1000; // map 0xd000-0xdfff -> 0xc000-0xcfff
+ else
+ this.bank2rdoffset = 0x3000; // map 0xd000-0xdfff -> 0x10000-0x10fff
+ if (this.auxRAMbank == 2)
+ this.bank2wroffset = -0x1000; // map 0xd000-0xdfff -> 0xc000-0xcfff
+ else
+ this.bank2wroffset = 0x3000; // map 0xd000-0xdfff -> 0x10000-0x10fff
+ }
+
+ getDebugCategories() {
+ return ['CPU', 'Stack', 'I/O', 'Disk'];
+ }
+ getDebugInfo(category: string, state: AppleIIState) {
+ switch (category) {
+ case 'I/O': return "AUX RAM Bank: " + state.auxRAMbank +
+ "\nAUX RAM Select: " + state.auxRAMselected +
+ "\nAUX RAM Write: " + !state.writeinhibit +
+ "\n\nGR Switches: " + printFlags(state.grswitch, ["Graphics", "Mixed", "Page2", "Hires"], false) +
+ "\n";
+ case 'Disk': return (this.slots[6] && this.slots[6]['toLongString'] && this.slots[6]['toLongString']()) || "\n";
+ }
+ }
}
-const GR_TXMODE = 1;
-const GR_MIXMODE = 2;
-const GR_PAGE1 = 4;
-const GR_HIRES = 8;
-
-type AppleGRParams = {dirty:boolean[], grswitch:number, mem:Uint8Array};
-
-var Apple2Display = function(pixels : Uint32Array, apple : AppleGRParams) {
- var XSIZE = 280;
- var YSIZE = 192;
- var PIXELON = 0xffffffff;
- var PIXELOFF = 0xff000000;
-
- var oldgrmode = -1;
- var textbuf = new Array(40*24);
-
- const flashInterval = 250;
-
- // https://mrob.com/pub/xapple2/colors.html
- const loresColor = [
- RGBA(0, 0, 0),
- RGBA(227, 30, 96),
- RGBA(96, 78, 189),
- RGBA(255, 68, 253),
- RGBA(0, 163, 96),
- RGBA(156, 156, 156),
- RGBA(20, 207, 253),
- RGBA(208, 195, 255),
- RGBA(96, 114, 3),
- RGBA(255, 106, 60),
- RGBA(156, 156, 156),
- RGBA(255, 160, 208),
- RGBA(20, 245, 60),
- RGBA(208, 221, 141),
- RGBA(114, 255, 208),
- RGBA(255, 255, 255)
- ];
-
- const text_lut = [
- 0x000, 0x080, 0x100, 0x180, 0x200, 0x280, 0x300, 0x380,
- 0x028, 0x0a8, 0x128, 0x1a8, 0x228, 0x2a8, 0x328, 0x3a8,
- 0x050, 0x0d0, 0x150, 0x1d0, 0x250, 0x2d0, 0x350, 0x3d0
- ];
-
- const hires_lut = [
- 0x0000, 0x0400, 0x0800, 0x0c00, 0x1000, 0x1400, 0x1800, 0x1c00,
- 0x0080, 0x0480, 0x0880, 0x0c80, 0x1080, 0x1480, 0x1880, 0x1c80,
- 0x0100, 0x0500, 0x0900, 0x0d00, 0x1100, 0x1500, 0x1900, 0x1d00,
- 0x0180, 0x0580, 0x0980, 0x0d80, 0x1180, 0x1580, 0x1980, 0x1d80,
- 0x0200, 0x0600, 0x0a00, 0x0e00, 0x1200, 0x1600, 0x1a00, 0x1e00,
- 0x0280, 0x0680, 0x0a80, 0x0e80, 0x1280, 0x1680, 0x1a80, 0x1e80,
- 0x0300, 0x0700, 0x0b00, 0x0f00, 0x1300, 0x1700, 0x1b00, 0x1f00,
- 0x0380, 0x0780, 0x0b80, 0x0f80, 0x1380, 0x1780, 0x1b80, 0x1f80,
- 0x0028, 0x0428, 0x0828, 0x0c28, 0x1028, 0x1428, 0x1828, 0x1c28,
- 0x00a8, 0x04a8, 0x08a8, 0x0ca8, 0x10a8, 0x14a8, 0x18a8, 0x1ca8,
- 0x0128, 0x0528, 0x0928, 0x0d28, 0x1128, 0x1528, 0x1928, 0x1d28,
- 0x01a8, 0x05a8, 0x09a8, 0x0da8, 0x11a8, 0x15a8, 0x19a8, 0x1da8,
- 0x0228, 0x0628, 0x0a28, 0x0e28, 0x1228, 0x1628, 0x1a28, 0x1e28,
- 0x02a8, 0x06a8, 0x0aa8, 0x0ea8, 0x12a8, 0x16a8, 0x1aa8, 0x1ea8,
- 0x0328, 0x0728, 0x0b28, 0x0f28, 0x1328, 0x1728, 0x1b28, 0x1f28,
- 0x03a8, 0x07a8, 0x0ba8, 0x0fa8, 0x13a8, 0x17a8, 0x1ba8, 0x1fa8,
- 0x0050, 0x0450, 0x0850, 0x0c50, 0x1050, 0x1450, 0x1850, 0x1c50,
- 0x00d0, 0x04d0, 0x08d0, 0x0cd0, 0x10d0, 0x14d0, 0x18d0, 0x1cd0,
- 0x0150, 0x0550, 0x0950, 0x0d50, 0x1150, 0x1550, 0x1950, 0x1d50,
- 0x01d0, 0x05d0, 0x09d0, 0x0dd0, 0x11d0, 0x15d0, 0x19d0, 0x1dd0,
- 0x0250, 0x0650, 0x0a50, 0x0e50, 0x1250, 0x1650, 0x1a50, 0x1e50,
- 0x02d0, 0x06d0, 0x0ad0, 0x0ed0, 0x12d0, 0x16d0, 0x1ad0, 0x1ed0,
- 0x0350, 0x0750, 0x0b50, 0x0f50, 0x1350, 0x1750, 0x1b50, 0x1f50,
- 0x03d0, 0x07d0, 0x0bd0, 0x0fd0, 0x13d0, 0x17d0, 0x1bd0, 0x1fd0,
- // just for floating bus, y >= 192
- 0x0078, 0x0478, 0x0878, 0x0c78, 0x1078, 0x1478, 0x1878, 0x1c78,
- 0x00f8, 0x04f8, 0x08f8, 0x0cf8, 0x10f8, 0x14f8, 0x18f8, 0x1cf8,
- 0x0178, 0x0578, 0x0978, 0x0d78, 0x1178, 0x1578, 0x1978, 0x1d78,
- 0x01f8, 0x05f8, 0x09f8, 0x0df8, 0x11f8, 0x15f8, 0x19f8, 0x1df8,
- 0x0278, 0x0678, 0x0a78, 0x0e78, 0x1278, 0x1678, 0x1a78, 0x1e78,
- 0x02f8, 0x06f8, 0x0af8, 0x0ef8, 0x12f8, 0x16f8, 0x1af8, 0x1ef8,
- 0x0378, 0x0778, 0x0b78, 0x0f78, 0x1378, 0x1778, 0x1b78, 0x1f78,
- 0x03f8, 0x07f8, 0x0bf8, 0x0ff8, 0x13f8, 0x17f8, 0x1bf8, 0x1ff8,
- 0x0000, 0x0400, 0x0800, 0x0c00, 0x1000, 0x1400,
+const GR_TXMODE = 1;
+const GR_MIXMODE = 2;
+const GR_PAGE1 = 4;
+const GR_HIRES = 8;
+
+type AppleGRParams = { dirty: boolean[], grswitch: number, mem: Uint8Array };
+
+var Apple2Display = function (pixels: Uint32Array, apple: AppleGRParams) {
+ var XSIZE = 280;
+ var YSIZE = 192;
+ var PIXELON = 0xffffffff;
+ var PIXELOFF = 0xff000000;
+
+ var oldgrmode = -1;
+ var textbuf = new Array(40 * 24);
+
+ const flashInterval = 250;
+
+ // https://mrob.com/pub/xapple2/colors.html
+ const loresColor = [
+ RGBA(0, 0, 0),
+ RGBA(227, 30, 96),
+ RGBA(96, 78, 189),
+ RGBA(255, 68, 253),
+ RGBA(0, 163, 96),
+ RGBA(156, 156, 156),
+ RGBA(20, 207, 253),
+ RGBA(208, 195, 255),
+ RGBA(96, 114, 3),
+ RGBA(255, 106, 60),
+ RGBA(156, 156, 156),
+ RGBA(255, 160, 208),
+ RGBA(20, 245, 60),
+ RGBA(208, 221, 141),
+ RGBA(114, 255, 208),
+ RGBA(255, 255, 255)
+ ];
+
+ const text_lut = [
+ 0x000, 0x080, 0x100, 0x180, 0x200, 0x280, 0x300, 0x380,
+ 0x028, 0x0a8, 0x128, 0x1a8, 0x228, 0x2a8, 0x328, 0x3a8,
+ 0x050, 0x0d0, 0x150, 0x1d0, 0x250, 0x2d0, 0x350, 0x3d0
+ ];
+
+ const hires_lut = [
+ 0x0000, 0x0400, 0x0800, 0x0c00, 0x1000, 0x1400, 0x1800, 0x1c00,
+ 0x0080, 0x0480, 0x0880, 0x0c80, 0x1080, 0x1480, 0x1880, 0x1c80,
+ 0x0100, 0x0500, 0x0900, 0x0d00, 0x1100, 0x1500, 0x1900, 0x1d00,
+ 0x0180, 0x0580, 0x0980, 0x0d80, 0x1180, 0x1580, 0x1980, 0x1d80,
+ 0x0200, 0x0600, 0x0a00, 0x0e00, 0x1200, 0x1600, 0x1a00, 0x1e00,
+ 0x0280, 0x0680, 0x0a80, 0x0e80, 0x1280, 0x1680, 0x1a80, 0x1e80,
+ 0x0300, 0x0700, 0x0b00, 0x0f00, 0x1300, 0x1700, 0x1b00, 0x1f00,
+ 0x0380, 0x0780, 0x0b80, 0x0f80, 0x1380, 0x1780, 0x1b80, 0x1f80,
+ 0x0028, 0x0428, 0x0828, 0x0c28, 0x1028, 0x1428, 0x1828, 0x1c28,
+ 0x00a8, 0x04a8, 0x08a8, 0x0ca8, 0x10a8, 0x14a8, 0x18a8, 0x1ca8,
+ 0x0128, 0x0528, 0x0928, 0x0d28, 0x1128, 0x1528, 0x1928, 0x1d28,
+ 0x01a8, 0x05a8, 0x09a8, 0x0da8, 0x11a8, 0x15a8, 0x19a8, 0x1da8,
+ 0x0228, 0x0628, 0x0a28, 0x0e28, 0x1228, 0x1628, 0x1a28, 0x1e28,
+ 0x02a8, 0x06a8, 0x0aa8, 0x0ea8, 0x12a8, 0x16a8, 0x1aa8, 0x1ea8,
+ 0x0328, 0x0728, 0x0b28, 0x0f28, 0x1328, 0x1728, 0x1b28, 0x1f28,
+ 0x03a8, 0x07a8, 0x0ba8, 0x0fa8, 0x13a8, 0x17a8, 0x1ba8, 0x1fa8,
+ 0x0050, 0x0450, 0x0850, 0x0c50, 0x1050, 0x1450, 0x1850, 0x1c50,
+ 0x00d0, 0x04d0, 0x08d0, 0x0cd0, 0x10d0, 0x14d0, 0x18d0, 0x1cd0,
+ 0x0150, 0x0550, 0x0950, 0x0d50, 0x1150, 0x1550, 0x1950, 0x1d50,
+ 0x01d0, 0x05d0, 0x09d0, 0x0dd0, 0x11d0, 0x15d0, 0x19d0, 0x1dd0,
+ 0x0250, 0x0650, 0x0a50, 0x0e50, 0x1250, 0x1650, 0x1a50, 0x1e50,
+ 0x02d0, 0x06d0, 0x0ad0, 0x0ed0, 0x12d0, 0x16d0, 0x1ad0, 0x1ed0,
+ 0x0350, 0x0750, 0x0b50, 0x0f50, 0x1350, 0x1750, 0x1b50, 0x1f50,
+ 0x03d0, 0x07d0, 0x0bd0, 0x0fd0, 0x13d0, 0x17d0, 0x1bd0, 0x1fd0,
+ // just for floating bus, y >= 192
+ 0x0078, 0x0478, 0x0878, 0x0c78, 0x1078, 0x1478, 0x1878, 0x1c78,
+ 0x00f8, 0x04f8, 0x08f8, 0x0cf8, 0x10f8, 0x14f8, 0x18f8, 0x1cf8,
+ 0x0178, 0x0578, 0x0978, 0x0d78, 0x1178, 0x1578, 0x1978, 0x1d78,
+ 0x01f8, 0x05f8, 0x09f8, 0x0df8, 0x11f8, 0x15f8, 0x19f8, 0x1df8,
+ 0x0278, 0x0678, 0x0a78, 0x0e78, 0x1278, 0x1678, 0x1a78, 0x1e78,
+ 0x02f8, 0x06f8, 0x0af8, 0x0ef8, 0x12f8, 0x16f8, 0x1af8, 0x1ef8,
+ 0x0378, 0x0778, 0x0b78, 0x0f78, 0x1378, 0x1778, 0x1b78, 0x1f78,
+ 0x03f8, 0x07f8, 0x0bf8, 0x0ff8, 0x13f8, 0x17f8, 0x1bf8, 0x1ff8,
+ 0x0000, 0x0400, 0x0800, 0x0c00, 0x1000, 0x1400,
];
- var colors_lut;
+ var colors_lut;
- /**
- * This function makes the color lookup table for hires mode.
- * We make a table of 1024 * 2 * 7 entries.
- * Why? Because we assume each color byte has 10 bits
- * (8 real bits + 1 on each side) and we need different colors
- * for odd and even addresses (2) and each byte displays 7 pixels.
- */
- {
- colors_lut = new Array(256*4*2*7);
- var i,j;
- var c1,c2,c3 = 15;
- var base = 0;
-
- // go thru odd and even
- for (j=0; j<2; j++)
- {
- // go thru 1024 values
- for (var b1=0; b1<1024; b1++)
- {
- // see if the hi bit is set
- if ((b1 & 0x80) == 0)
- {
- c1 = 3; c2 = 12; // purple & green
- } else
- {
- c1 = 6; c2 = 9; // blue & orange
- }
- // make a value consisting of:
- // the 8th bit, then bits 0-7, then the 9th bit
- var b = ((b1 & 0x100) >> 8) | ((b1 & 0x7f) << 1) |
- ((b1 & 0x200) >> 1);
- // go through each pixel
- for (i=0; i<7; i++)
- {
- var c;
- // is this pixel lit?
- if (((2<> 4];
- for (i=0; i<4; i++)
- {
- pixels[base] =
- pixels[base+1] =
- pixels[base+2] =
- pixels[base+3] =
- pixels[base+4] =
- pixels[base+5] =
- pixels[base+6] = c;
- base += XSIZE;
- }
- }
-
- function drawTextChar(x, y, b, invert)
- {
- var base = (y<<3)*XSIZE + x*7; // (x<<2) + (x<<1) + x
- var on,off;
- if (invert)
- {
- on = PIXELOFF;
- off = PIXELON;
- } else
- {
- on = PIXELON;
- off = PIXELOFF;
- }
-
- for (var yy=0; yy<8; yy++)
- {
- var chr = apple2_charset[(b<<3)+yy];
- pixels[base] = ((chr & 64) > 0)?on:off;
- pixels[base+1] = ((chr & 32) > 0)?on:off;
- pixels[base+2] = ((chr & 16) > 0)?on:off;
- pixels[base+3] = ((chr & 8) > 0)?on:off;
- pixels[base+4] = ((chr & 4) > 0)?on:off;
- pixels[base+5] = ((chr & 2) > 0)?on:off;
- pixels[base+6] = ((chr & 1) > 0)?on:off;
- base += XSIZE;
- }
- }
-
- this.getAddressForScanline = function(y:number) : number {
+ /**
+ * This function makes the color lookup table for hires mode.
+ * We make a table of 1024 * 2 * 7 entries.
+ * Why? Because we assume each color byte has 10 bits
+ * (8 real bits + 1 on each side) and we need different colors
+ * for odd and even addresses (2) and each byte displays 7 pixels.
+ */
+ {
+ colors_lut = new Array(256 * 4 * 2 * 7);
+ var i, j;
+ var c1, c2, c3 = 15;
+ var base = 0;
+
+ // go thru odd and even
+ for (j = 0; j < 2; j++) {
+ // go thru 1024 values
+ for (var b1 = 0; b1 < 1024; b1++) {
+ // see if the hi bit is set
+ if ((b1 & 0x80) == 0) {
+ c1 = 3; c2 = 12; // purple & green
+ } else {
+ c1 = 6; c2 = 9; // blue & orange
+ }
+ // make a value consisting of:
+ // the 8th bit, then bits 0-7, then the 9th bit
+ var b = ((b1 & 0x100) >> 8) | ((b1 & 0x7f) << 1) |
+ ((b1 & 0x200) >> 1);
+ // go through each pixel
+ for (i = 0; i < 7; i++) {
+ var c;
+ // is this pixel lit?
+ if (((2 << i) & b) != 0) {
+ // are there pixels lit on both sides of this one?
+ if (((7 << i) & b) == (7 << i))
+ // yes, make it white
+ c = 15;
+ else
+ // no, choose color based on odd/even byte
+ // and odd/even pixel column
+ c = ((((j ^ i) & 1) == 0) ? c1 : c2);
+ } else {
+ // are there pixels lit in the previous & next
+ // column but none in this?
+ if (((5 << i) & b) == (5 << i))
+ // color this pixel
+ c = ((((j ^ i) & 1) != 0) ? c1 : c2);
+ else
+ c = 0;
+ }
+ colors_lut[base] = loresColor[c];
+ base++;
+ }
+ }
+ }
+ }
+
+ function drawLoresChar(x, y, b) {
+ var i, base, adr, c;
+ base = (y << 3) * XSIZE + x * 7; //(x<<2) + (x<<1) + x
+ c = loresColor[b & 0x0f];
+ for (i = 0; i < 4; i++) {
+ pixels[base] =
+ pixels[base + 1] =
+ pixels[base + 2] =
+ pixels[base + 3] =
+ pixels[base + 4] =
+ pixels[base + 5] =
+ pixels[base + 6] = c;
+ base += XSIZE;
+ }
+ c = loresColor[b >> 4];
+ for (i = 0; i < 4; i++) {
+ pixels[base] =
+ pixels[base + 1] =
+ pixels[base + 2] =
+ pixels[base + 3] =
+ pixels[base + 4] =
+ pixels[base + 5] =
+ pixels[base + 6] = c;
+ base += XSIZE;
+ }
+ }
+
+ function drawTextChar(x, y, b, invert) {
+ var base = (y << 3) * XSIZE + x * 7; // (x<<2) + (x<<1) + x
+ var on, off;
+ if (invert) {
+ on = PIXELOFF;
+ off = PIXELON;
+ } else {
+ on = PIXELON;
+ off = PIXELOFF;
+ }
+
+ for (var yy = 0; yy < 8; yy++) {
+ var chr = apple2_charset[(b << 3) + yy];
+ pixels[base] = ((chr & 64) > 0) ? on : off;
+ pixels[base + 1] = ((chr & 32) > 0) ? on : off;
+ pixels[base + 2] = ((chr & 16) > 0) ? on : off;
+ pixels[base + 3] = ((chr & 8) > 0) ? on : off;
+ pixels[base + 4] = ((chr & 4) > 0) ? on : off;
+ pixels[base + 5] = ((chr & 2) > 0) ? on : off;
+ pixels[base + 6] = ((chr & 1) > 0) ? on : off;
+ base += XSIZE;
+ }
+ }
+
+ this.getAddressForScanline = function (y: number): number {
var base = hires_lut[y];
if ((apple.grswitch & GR_HIRES) && (y < 160 || !(apple.grswitch & GR_MIXMODE)))
base = base | ((apple.grswitch & GR_PAGE1) ? 0x4000 : 0x2000);
@@ -708,409 +742,389 @@ var Apple2Display = function(pixels : Uint32Array, apple : AppleGRParams) {
return base;
}
- function drawHiresLines(y, maxy)
- {
- var yb = y*XSIZE;
- for (; y < maxy; y++)
- {
- var base = hires_lut[y] + (((apple.grswitch & GR_PAGE1) != 0) ? 0x4000 : 0x2000);
- if (!apple.dirty[base >> 7])
- {
- yb += XSIZE;
- continue;
- }
- var c1, c2;
- var b = 0;
- var b1 = apple.mem[base] & 0xff;
- for (var x1=0; x1<20; x1++)
- {
- var b2 = apple.mem[base+1] & 0xff;
- var b3 = apple.mem[base+2] & 0xff;
- var d1 = (((b&0x40)<<2) | b1 | b2<<9) & 0x3ff;
- for (var i=0; i<7; i++)
- pixels[yb+i] = colors_lut[d1*7+i];
- var d2 = (((b1&0x40)<<2) | b2 | b3<<9) & 0x3ff;
- for (var i=0; i<7; i++)
- pixels[yb+7+i] = colors_lut[d2*7+7168+i];
- yb += 14;
- base += 2;
- b = b2;
- b1 = b3;
- }
- }
- }
-
- function drawLoresLine(y)
- {
- // get the base address of this line
- var base = text_lut[y] +
- (((apple.grswitch & GR_PAGE1) != 0) ? 0x800 : 0x400);
- // if (!dirty[base >> 7])
- // return;
- for (var x=0; x<40; x++)
- {
- var b = apple.mem[base+x] & 0xff;
- // if the char. changed, draw it
- if (b != textbuf[y*40+x])
- {
- drawLoresChar(x, y, b);
- textbuf[y*40+x] = b;
- }
- }
- }
-
- function drawTextLine(y, flash)
- {
- // get the base address of this line
- var base = text_lut[y] +
- (((apple.grswitch & GR_PAGE1) != 0) ? 0x800 : 0x400);
- // if (!dirty[base >> 7])
- // return;
- for (var x=0; x<40; x++)
- {
- var b = apple.mem[base+x] & 0xff;
- var invert;
- // invert flash characters 1/2 of the time
- if (b >= 0x80)
- {
- invert = false;
- } else if (b >= 0x40)
- {
- invert = flash;
- if (flash)
- b -= 0x40;
- else
- b += 0x40;
- } else
- invert = true;
- // if the char. changed, draw it
- if (b != textbuf[y*40+x])
- {
- drawTextChar(x, y, b & 0x7f, invert);
- textbuf[y*40+x] = b;
- }
- }
- }
-
- this.updateScreen = function(totalrepaint)
- {
- var y;
- var flash = (new Date().getTime() % (flashInterval<<1)) > flashInterval;
-
- // if graphics mode changed, repaint whole screen
- if (apple.grswitch != oldgrmode)
- {
- oldgrmode = apple.grswitch;
- totalrepaint = true;
- }
- if (totalrepaint)
- {
- // clear textbuf if in text mode
- if ((apple.grswitch & GR_TXMODE) != 0 || (apple.grswitch & GR_MIXMODE) != 0)
- {
- for (y=0; y<24; y++)
- for (var x=0; x<40; x++)
- textbuf[y*40+x] = -1;
- }
- for (var i=0; i> 7]) {
+ yb += XSIZE;
+ continue;
+ }
+ var c1, c2;
+ var b = 0;
+ var b1 = apple.mem[base] & 0xff;
+ for (var x1 = 0; x1 < 20; x1++) {
+ var b2 = apple.mem[base + 1] & 0xff;
+ var b3 = apple.mem[base + 2] & 0xff;
+ var d1 = (((b & 0x40) << 2) | b1 | b2 << 9) & 0x3ff;
+ for (var i = 0; i < 7; i++)
+ pixels[yb + i] = colors_lut[d1 * 7 + i];
+ var d2 = (((b1 & 0x40) << 2) | b2 | b3 << 9) & 0x3ff;
+ for (var i = 0; i < 7; i++)
+ pixels[yb + 7 + i] = colors_lut[d2 * 7 + 7168 + i];
+ yb += 14;
+ base += 2;
+ b = b2;
+ b1 = b3;
+ }
+ }
+ }
+
+ function drawLoresLine(y) {
+ // get the base address of this line
+ var base = text_lut[y] +
+ (((apple.grswitch & GR_PAGE1) != 0) ? 0x800 : 0x400);
+ // if (!dirty[base >> 7])
+ // return;
+ for (var x = 0; x < 40; x++) {
+ var b = apple.mem[base + x] & 0xff;
+ // if the char. changed, draw it
+ if (b != textbuf[y * 40 + x]) {
+ drawLoresChar(x, y, b);
+ textbuf[y * 40 + x] = b;
+ }
+ }
+ }
+
+ function drawTextLine(y, flash) {
+ // get the base address of this line
+ var base = text_lut[y] +
+ (((apple.grswitch & GR_PAGE1) != 0) ? 0x800 : 0x400);
+ // if (!dirty[base >> 7])
+ // return;
+ for (var x = 0; x < 40; x++) {
+ var b = apple.mem[base + x] & 0xff;
+ var invert;
+ // invert flash characters 1/2 of the time
+ if (b >= 0x80) {
+ invert = false;
+ } else if (b >= 0x40) {
+ invert = flash;
+ if (flash)
+ b -= 0x40;
+ else
+ b += 0x40;
+ } else
+ invert = true;
+ // if the char. changed, draw it
+ if (b != textbuf[y * 40 + x]) {
+ drawTextChar(x, y, b & 0x7f, invert);
+ textbuf[y * 40 + x] = b;
+ }
+ }
+ }
+
+ this.updateScreen = function (totalrepaint) {
+ var y;
+ var flash = (new Date().getTime() % (flashInterval << 1)) > flashInterval;
+
+ // if graphics mode changed, repaint whole screen
+ if (apple.grswitch != oldgrmode) {
+ oldgrmode = apple.grswitch;
+ totalrepaint = true;
+ }
+ if (totalrepaint) {
+ // clear textbuf if in text mode
+ if ((apple.grswitch & GR_TXMODE) != 0 || (apple.grswitch & GR_MIXMODE) != 0) {
+ for (y = 0; y < 24; y++)
+ for (var x = 0; x < 40; x++)
+ textbuf[y * 40 + x] = -1;
+ }
+ for (var i = 0; i < apple.dirty.length; i++)
+ apple.dirty[i] = true;
+ }
+
+ // first, draw top part of window
+ if ((apple.grswitch & GR_TXMODE) != 0) {
+ for (y = 0; y < 20; y++)
+ drawTextLine(y, flash);
+ } else {
+ if ((apple.grswitch & GR_HIRES) != 0)
+ drawHiresLines(0, 160);
+ else
+ for (y = 0; y < 20; y++)
+ drawLoresLine(y);
+ }
+
+ // now do mixed part of window
+ if ((apple.grswitch & GR_TXMODE) != 0 || (apple.grswitch & GR_MIXMODE) != 0) {
+ for (y = 20; y < 24; y++)
+ drawTextLine(y, flash);
+ } else {
+ if ((apple.grswitch & GR_HIRES) != 0)
+ drawHiresLines(160, 192);
+ else
+ for (y = 20; y < 24; y++)
+ drawLoresLine(y);
+ }
+ for (var i = 0; i < apple.dirty.length; i++)
+ apple.dirty[i] = false;
+ }
+
+ this.invalidate = function () {
+ oldgrmode = -1;
+ }
}
/*exported apple2_charset */
const apple2_charset = [
- 0x00,0x1c,0x22,0x2a,0x2e,0x2c,0x20,0x1e,
- 0x00,0x08,0x14,0x22,0x22,0x3e,0x22,0x22,
- 0x00,0x3c,0x22,0x22,0x3c,0x22,0x22,0x3c,
- 0x00,0x1c,0x22,0x20,0x20,0x20,0x22,0x1c,
- 0x00,0x3c,0x22,0x22,0x22,0x22,0x22,0x3c,
- 0x00,0x3e,0x20,0x20,0x3c,0x20,0x20,0x3e,
- 0x00,0x3e,0x20,0x20,0x3c,0x20,0x20,0x20,
- 0x00,0x1e,0x20,0x20,0x20,0x26,0x22,0x1e,
- 0x00,0x22,0x22,0x22,0x3e,0x22,0x22,0x22,
- 0x00,0x1c,0x08,0x08,0x08,0x08,0x08,0x1c,
- 0x00,0x02,0x02,0x02,0x02,0x02,0x22,0x1c,
- 0x00,0x22,0x24,0x28,0x30,0x28,0x24,0x22,
- 0x00,0x20,0x20,0x20,0x20,0x20,0x20,0x3e,
- 0x00,0x22,0x36,0x2a,0x2a,0x22,0x22,0x22,
- 0x00,0x22,0x22,0x32,0x2a,0x26,0x22,0x22,
- 0x00,0x1c,0x22,0x22,0x22,0x22,0x22,0x1c,
- 0x00,0x3c,0x22,0x22,0x3c,0x20,0x20,0x20,
- 0x00,0x1c,0x22,0x22,0x22,0x2a,0x24,0x1a,
- 0x00,0x3c,0x22,0x22,0x3c,0x28,0x24,0x22,
- 0x00,0x1c,0x22,0x20,0x1c,0x02,0x22,0x1c,
- 0x00,0x3e,0x08,0x08,0x08,0x08,0x08,0x08,
- 0x00,0x22,0x22,0x22,0x22,0x22,0x22,0x1c,
- 0x00,0x22,0x22,0x22,0x22,0x22,0x14,0x08,
- 0x00,0x22,0x22,0x22,0x2a,0x2a,0x36,0x22,
- 0x00,0x22,0x22,0x14,0x08,0x14,0x22,0x22,
- 0x00,0x22,0x22,0x14,0x08,0x08,0x08,0x08,
- 0x00,0x3e,0x02,0x04,0x08,0x10,0x20,0x3e,
- 0x00,0x3e,0x30,0x30,0x30,0x30,0x30,0x3e,
- 0x00,0x00,0x20,0x10,0x08,0x04,0x02,0x00,
- 0x00,0x3e,0x06,0x06,0x06,0x06,0x06,0x3e,
- 0x00,0x00,0x00,0x08,0x14,0x22,0x00,0x00,
- 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x3e,
- 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
- 0x00,0x08,0x08,0x08,0x08,0x08,0x00,0x08,
- 0x00,0x14,0x14,0x14,0x00,0x00,0x00,0x00,
- 0x00,0x14,0x14,0x3e,0x14,0x3e,0x14,0x14,
- 0x00,0x08,0x1e,0x28,0x1c,0x0a,0x3c,0x08,
- 0x00,0x30,0x32,0x04,0x08,0x10,0x26,0x06,
- 0x00,0x10,0x28,0x28,0x10,0x2a,0x24,0x1a,
- 0x00,0x08,0x08,0x08,0x00,0x00,0x00,0x00,
- 0x00,0x08,0x10,0x20,0x20,0x20,0x10,0x08,
- 0x00,0x08,0x04,0x02,0x02,0x02,0x04,0x08,
- 0x00,0x08,0x2a,0x1c,0x08,0x1c,0x2a,0x08,
- 0x00,0x00,0x08,0x08,0x3e,0x08,0x08,0x00,
- 0x00,0x00,0x00,0x00,0x00,0x08,0x08,0x10,
- 0x00,0x00,0x00,0x00,0x3e,0x00,0x00,0x00,
- 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x08,
- 0x00,0x00,0x02,0x04,0x08,0x10,0x20,0x00,
- 0x00,0x1c,0x22,0x26,0x2a,0x32,0x22,0x1c,
- 0x00,0x08,0x18,0x08,0x08,0x08,0x08,0x1c,
- 0x00,0x1c,0x22,0x02,0x0c,0x10,0x20,0x3e,
- 0x00,0x3e,0x02,0x04,0x0c,0x02,0x22,0x1c,
- 0x00,0x04,0x0c,0x14,0x24,0x3e,0x04,0x04,
- 0x00,0x3e,0x20,0x3c,0x02,0x02,0x22,0x1c,
- 0x00,0x0e,0x10,0x20,0x3c,0x22,0x22,0x1c,
- 0x00,0x3e,0x02,0x04,0x08,0x10,0x10,0x10,
- 0x00,0x1c,0x22,0x22,0x1c,0x22,0x22,0x1c,
- 0x00,0x1c,0x22,0x22,0x1e,0x02,0x04,0x38,
- 0x00,0x00,0x00,0x08,0x00,0x08,0x00,0x00,
- 0x00,0x00,0x00,0x08,0x00,0x08,0x08,0x10,
- 0x00,0x04,0x08,0x10,0x20,0x10,0x08,0x04,
- 0x00,0x00,0x00,0x3e,0x00,0x3e,0x00,0x00,
- 0x00,0x10,0x08,0x04,0x02,0x04,0x08,0x10,
- 0x00,0x1c,0x22,0x04,0x08,0x08,0x00,0x08,
- 0x80,0x9c,0xa2,0xaa,0xae,0xac,0xa0,0x9e,
- 0x80,0x88,0x94,0xa2,0xa2,0xbe,0xa2,0xa2,
- 0x80,0xbc,0xa2,0xa2,0xbc,0xa2,0xa2,0xbc,
- 0x80,0x9c,0xa2,0xa0,0xa0,0xa0,0xa2,0x9c,
- 0x80,0xbc,0xa2,0xa2,0xa2,0xa2,0xa2,0xbc,
- 0x80,0xbe,0xa0,0xa0,0xbc,0xa0,0xa0,0xbe,
- 0x80,0xbe,0xa0,0xa0,0xbc,0xa0,0xa0,0xa0,
- 0x80,0x9e,0xa0,0xa0,0xa0,0xa6,0xa2,0x9e,
- 0x80,0xa2,0xa2,0xa2,0xbe,0xa2,0xa2,0xa2,
- 0x80,0x9c,0x88,0x88,0x88,0x88,0x88,0x9c,
- 0x80,0x82,0x82,0x82,0x82,0x82,0xa2,0x9c,
- 0x80,0xa2,0xa4,0xa8,0xb0,0xa8,0xa4,0xa2,
- 0x80,0xa0,0xa0,0xa0,0xa0,0xa0,0xa0,0xbe,
- 0x80,0xa2,0xb6,0xaa,0xaa,0xa2,0xa2,0xa2,
- 0x80,0xa2,0xa2,0xb2,0xaa,0xa6,0xa2,0xa2,
- 0x80,0x9c,0xa2,0xa2,0xa2,0xa2,0xa2,0x9c,
- 0x80,0xbc,0xa2,0xa2,0xbc,0xa0,0xa0,0xa0,
- 0x80,0x9c,0xa2,0xa2,0xa2,0xaa,0xa4,0x9a,
- 0x80,0xbc,0xa2,0xa2,0xbc,0xa8,0xa4,0xa2,
- 0x80,0x9c,0xa2,0xa0,0x9c,0x82,0xa2,0x9c,
- 0x80,0xbe,0x88,0x88,0x88,0x88,0x88,0x88,
- 0x80,0xa2,0xa2,0xa2,0xa2,0xa2,0xa2,0x9c,
- 0x80,0xa2,0xa2,0xa2,0xa2,0xa2,0x94,0x88,
- 0x80,0xa2,0xa2,0xa2,0xaa,0xaa,0xb6,0xa2,
- 0x80,0xa2,0xa2,0x94,0x88,0x94,0xa2,0xa2,
- 0x80,0xa2,0xa2,0x94,0x88,0x88,0x88,0x88,
- 0x80,0xbe,0x82,0x84,0x88,0x90,0xa0,0xbe,
- 0x80,0xbe,0xb0,0xb0,0xb0,0xb0,0xb0,0xbe,
- 0x80,0x80,0xa0,0x90,0x88,0x84,0x82,0x80,
- 0x80,0xbe,0x86,0x86,0x86,0x86,0x86,0xbe,
- 0x80,0x80,0x80,0x88,0x94,0xa2,0x80,0x80,
- 0x80,0x80,0x80,0x80,0x80,0x80,0x80,0xbe,
- 0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,
- 0x80,0x88,0x88,0x88,0x88,0x88,0x80,0x88,
- 0x80,0x94,0x94,0x94,0x80,0x80,0x80,0x80,
- 0x80,0x94,0x94,0xbe,0x94,0xbe,0x94,0x94,
- 0x80,0x88,0x9e,0xa8,0x9c,0x8a,0xbc,0x88,
- 0x80,0xb0,0xb2,0x84,0x88,0x90,0xa6,0x86,
- 0x80,0x90,0xa8,0xa8,0x90,0xaa,0xa4,0x9a,
- 0x80,0x88,0x88,0x88,0x80,0x80,0x80,0x80,
- 0x80,0x88,0x90,0xa0,0xa0,0xa0,0x90,0x88,
- 0x80,0x88,0x84,0x82,0x82,0x82,0x84,0x88,
- 0x80,0x88,0xaa,0x9c,0x88,0x9c,0xaa,0x88,
- 0x80,0x80,0x88,0x88,0xbe,0x88,0x88,0x80,
- 0x80,0x80,0x80,0x80,0x80,0x88,0x88,0x90,
- 0x80,0x80,0x80,0x80,0xbe,0x80,0x80,0x80,
- 0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x88,
- 0x80,0x80,0x82,0x84,0x88,0x90,0xa0,0x80,
- 0x80,0x9c,0xa2,0xa6,0xaa,0xb2,0xa2,0x9c,
- 0x80,0x88,0x98,0x88,0x88,0x88,0x88,0x9c,
- 0x80,0x9c,0xa2,0x82,0x8c,0x90,0xa0,0xbe,
- 0x80,0xbe,0x82,0x84,0x8c,0x82,0xa2,0x9c,
- 0x80,0x84,0x8c,0x94,0xa4,0xbe,0x84,0x84,
- 0x80,0xbe,0xa0,0xbc,0x82,0x82,0xa2,0x9c,
- 0x80,0x8e,0x90,0xa0,0xbc,0xa2,0xa2,0x9c,
- 0x80,0xbe,0x82,0x84,0x88,0x90,0x90,0x90,
- 0x80,0x9c,0xa2,0xa2,0x9c,0xa2,0xa2,0x9c,
- 0x80,0x9c,0xa2,0xa2,0x9e,0x82,0x84,0xb8,
- 0x80,0x80,0x80,0x88,0x80,0x88,0x80,0x80,
- 0x80,0x80,0x80,0x88,0x80,0x88,0x88,0x90,
- 0x80,0x84,0x88,0x90,0xa0,0x90,0x88,0x84,
- 0x80,0x80,0x80,0xbe,0x80,0xbe,0x80,0x80,
- 0x80,0x90,0x88,0x84,0x82,0x84,0x88,0x90,
- 0x80,0x9c,0xa2,0x84,0x88,0x88,0x80,0x88,
- 0x00,0x1c,0x22,0x2a,0x2e,0x2c,0x20,0x1e,
- 0x00,0x08,0x14,0x22,0x22,0x3e,0x22,0x22,
- 0x00,0x3c,0x22,0x22,0x3c,0x22,0x22,0x3c,
- 0x00,0x1c,0x22,0x20,0x20,0x20,0x22,0x1c,
- 0x00,0x3c,0x22,0x22,0x22,0x22,0x22,0x3c,
- 0x00,0x3e,0x20,0x20,0x3c,0x20,0x20,0x3e,
- 0x00,0x3e,0x20,0x20,0x3c,0x20,0x20,0x20,
- 0x00,0x1e,0x20,0x20,0x20,0x26,0x22,0x1e,
- 0x00,0x22,0x22,0x22,0x3e,0x22,0x22,0x22,
- 0x00,0x1c,0x08,0x08,0x08,0x08,0x08,0x1c,
- 0x00,0x02,0x02,0x02,0x02,0x02,0x22,0x1c,
- 0x00,0x22,0x24,0x28,0x30,0x28,0x24,0x22,
- 0x00,0x20,0x20,0x20,0x20,0x20,0x20,0x3e,
- 0x00,0x22,0x36,0x2a,0x2a,0x22,0x22,0x22,
- 0x00,0x22,0x22,0x32,0x2a,0x26,0x22,0x22,
- 0x00,0x1c,0x22,0x22,0x22,0x22,0x22,0x1c,
- 0x00,0x3c,0x22,0x22,0x3c,0x20,0x20,0x20,
- 0x00,0x1c,0x22,0x22,0x22,0x2a,0x24,0x1a,
- 0x00,0x3c,0x22,0x22,0x3c,0x28,0x24,0x22,
- 0x00,0x1c,0x22,0x20,0x1c,0x02,0x22,0x1c,
- 0x00,0x3e,0x08,0x08,0x08,0x08,0x08,0x08,
- 0x00,0x22,0x22,0x22,0x22,0x22,0x22,0x1c,
- 0x00,0x22,0x22,0x22,0x22,0x22,0x14,0x08,
- 0x00,0x22,0x22,0x22,0x2a,0x2a,0x36,0x22,
- 0x00,0x22,0x22,0x14,0x08,0x14,0x22,0x22,
- 0x00,0x22,0x22,0x14,0x08,0x08,0x08,0x08,
- 0x00,0x3e,0x02,0x04,0x08,0x10,0x20,0x3e,
- 0x00,0x3e,0x30,0x30,0x30,0x30,0x30,0x3e,
- 0x00,0x00,0x20,0x10,0x08,0x04,0x02,0x00,
- 0x00,0x3e,0x06,0x06,0x06,0x06,0x06,0x3e,
- 0x00,0x00,0x00,0x08,0x14,0x22,0x00,0x00,
- 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x3e,
- 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
- 0x00,0x08,0x08,0x08,0x08,0x08,0x00,0x08,
- 0x00,0x14,0x14,0x14,0x00,0x00,0x00,0x00,
- 0x00,0x14,0x14,0x3e,0x14,0x3e,0x14,0x14,
- 0x00,0x08,0x1e,0x28,0x1c,0x0a,0x3c,0x08,
- 0x00,0x30,0x32,0x04,0x08,0x10,0x26,0x06,
- 0x00,0x10,0x28,0x28,0x10,0x2a,0x24,0x1a,
- 0x00,0x08,0x08,0x08,0x00,0x00,0x00,0x00,
- 0x00,0x08,0x10,0x20,0x20,0x20,0x10,0x08,
- 0x00,0x08,0x04,0x02,0x02,0x02,0x04,0x08,
- 0x00,0x08,0x2a,0x1c,0x08,0x1c,0x2a,0x08,
- 0x00,0x00,0x08,0x08,0x3e,0x08,0x08,0x00,
- 0x00,0x00,0x00,0x00,0x00,0x08,0x08,0x10,
- 0x00,0x00,0x00,0x00,0x3e,0x00,0x00,0x00,
- 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x08,
- 0x00,0x00,0x02,0x04,0x08,0x10,0x20,0x00,
- 0x00,0x1c,0x22,0x26,0x2a,0x32,0x22,0x1c,
- 0x00,0x08,0x18,0x08,0x08,0x08,0x08,0x1c,
- 0x00,0x1c,0x22,0x02,0x0c,0x10,0x20,0x3e,
- 0x00,0x3e,0x02,0x04,0x0c,0x02,0x22,0x1c,
- 0x00,0x04,0x0c,0x14,0x24,0x3e,0x04,0x04,
- 0x00,0x3e,0x20,0x3c,0x02,0x02,0x22,0x1c,
- 0x00,0x0e,0x10,0x20,0x3c,0x22,0x22,0x1c,
- 0x00,0x3e,0x02,0x04,0x08,0x10,0x10,0x10,
- 0x00,0x1c,0x22,0x22,0x1c,0x22,0x22,0x1c,
- 0x00,0x1c,0x22,0x22,0x1e,0x02,0x04,0x38,
- 0x00,0x00,0x00,0x08,0x00,0x08,0x00,0x00,
- 0x00,0x00,0x00,0x08,0x00,0x08,0x08,0x10,
- 0x00,0x04,0x08,0x10,0x20,0x10,0x08,0x04,
- 0x00,0x00,0x00,0x3e,0x00,0x3e,0x00,0x00,
- 0x00,0x10,0x08,0x04,0x02,0x04,0x08,0x10,
- 0x00,0x1c,0x22,0x04,0x08,0x08,0x00,0x08,
- 0x80,0x9c,0xa2,0xaa,0xae,0xac,0xa0,0x9e,
- 0x80,0x88,0x94,0xa2,0xa2,0xbe,0xa2,0xa2,
- 0x80,0xbc,0xa2,0xa2,0xbc,0xa2,0xa2,0xbc,
- 0x80,0x9c,0xa2,0xa0,0xa0,0xa0,0xa2,0x9c,
- 0x80,0xbc,0xa2,0xa2,0xa2,0xa2,0xa2,0xbc,
- 0x80,0xbe,0xa0,0xa0,0xbc,0xa0,0xa0,0xbe,
- 0x80,0xbe,0xa0,0xa0,0xbc,0xa0,0xa0,0xa0,
- 0x80,0x9e,0xa0,0xa0,0xa0,0xa6,0xa2,0x9e,
- 0x80,0xa2,0xa2,0xa2,0xbe,0xa2,0xa2,0xa2,
- 0x80,0x9c,0x88,0x88,0x88,0x88,0x88,0x9c,
- 0x80,0x82,0x82,0x82,0x82,0x82,0xa2,0x9c,
- 0x80,0xa2,0xa4,0xa8,0xb0,0xa8,0xa4,0xa2,
- 0x80,0xa0,0xa0,0xa0,0xa0,0xa0,0xa0,0xbe,
- 0x80,0xa2,0xb6,0xaa,0xaa,0xa2,0xa2,0xa2,
- 0x80,0xa2,0xa2,0xb2,0xaa,0xa6,0xa2,0xa2,
- 0x80,0x9c,0xa2,0xa2,0xa2,0xa2,0xa2,0x9c,
- 0x80,0xbc,0xa2,0xa2,0xbc,0xa0,0xa0,0xa0,
- 0x80,0x9c,0xa2,0xa2,0xa2,0xaa,0xa4,0x9a,
- 0x80,0xbc,0xa2,0xa2,0xbc,0xa8,0xa4,0xa2,
- 0x80,0x9c,0xa2,0xa0,0x9c,0x82,0xa2,0x9c,
- 0x80,0xbe,0x88,0x88,0x88,0x88,0x88,0x88,
- 0x80,0xa2,0xa2,0xa2,0xa2,0xa2,0xa2,0x9c,
- 0x80,0xa2,0xa2,0xa2,0xa2,0xa2,0x94,0x88,
- 0x80,0xa2,0xa2,0xa2,0xaa,0xaa,0xb6,0xa2,
- 0x80,0xa2,0xa2,0x94,0x88,0x94,0xa2,0xa2,
- 0x80,0xa2,0xa2,0x94,0x88,0x88,0x88,0x88,
- 0x80,0xbe,0x82,0x84,0x88,0x90,0xa0,0xbe,
- 0x80,0xbe,0xb0,0xb0,0xb0,0xb0,0xb0,0xbe,
- 0x80,0x80,0xa0,0x90,0x88,0x84,0x82,0x80,
- 0x80,0xbe,0x86,0x86,0x86,0x86,0x86,0xbe,
- 0x80,0x80,0x80,0x88,0x94,0xa2,0x80,0x80,
- 0x80,0x80,0x80,0x80,0x80,0x80,0x80,0xbe,
- 0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,
- 0x80,0x88,0x88,0x88,0x88,0x88,0x80,0x88,
- 0x80,0x94,0x94,0x94,0x80,0x80,0x80,0x80,
- 0x80,0x94,0x94,0xbe,0x94,0xbe,0x94,0x94,
- 0x80,0x88,0x9e,0xa8,0x9c,0x8a,0xbc,0x88,
- 0x80,0xb0,0xb2,0x84,0x88,0x90,0xa6,0x86,
- 0x80,0x90,0xa8,0xa8,0x90,0xaa,0xa4,0x9a,
- 0x80,0x88,0x88,0x88,0x80,0x80,0x80,0x80,
- 0x80,0x88,0x90,0xa0,0xa0,0xa0,0x90,0x88,
- 0x80,0x88,0x84,0x82,0x82,0x82,0x84,0x88,
- 0x80,0x88,0xaa,0x9c,0x88,0x9c,0xaa,0x88,
- 0x80,0x80,0x88,0x88,0xbe,0x88,0x88,0x80,
- 0x80,0x80,0x80,0x80,0x80,0x88,0x88,0x90,
- 0x80,0x80,0x80,0x80,0xbe,0x80,0x80,0x80,
- 0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x88,
- 0x80,0x80,0x82,0x84,0x88,0x90,0xa0,0x80,
- 0x80,0x9c,0xa2,0xa6,0xaa,0xb2,0xa2,0x9c,
- 0x80,0x88,0x98,0x88,0x88,0x88,0x88,0x9c,
- 0x80,0x9c,0xa2,0x82,0x8c,0x90,0xa0,0xbe,
- 0x80,0xbe,0x82,0x84,0x8c,0x82,0xa2,0x9c,
- 0x80,0x84,0x8c,0x94,0xa4,0xbe,0x84,0x84,
- 0x80,0xbe,0xa0,0xbc,0x82,0x82,0xa2,0x9c,
- 0x80,0x8e,0x90,0xa0,0xbc,0xa2,0xa2,0x9c,
- 0x80,0xbe,0x82,0x84,0x88,0x90,0x90,0x90,
- 0x80,0x9c,0xa2,0xa2,0x9c,0xa2,0xa2,0x9c,
- 0x80,0x9c,0xa2,0xa2,0x9e,0x82,0x84,0xb8,
- 0x80,0x80,0x80,0x88,0x80,0x88,0x80,0x80,
- 0x80,0x80,0x80,0x88,0x80,0x88,0x88,0x90,
- 0x80,0x84,0x88,0x90,0xa0,0x90,0x88,0x84,
- 0x80,0x80,0x80,0xbe,0x80,0xbe,0x80,0x80,
- 0x80,0x90,0x88,0x84,0x82,0x84,0x88,0x90,
- 0x80,0x9c,0xa2,0x84,0x88,0x88,0x80,0x88
+ 0x00, 0x1c, 0x22, 0x2a, 0x2e, 0x2c, 0x20, 0x1e,
+ 0x00, 0x08, 0x14, 0x22, 0x22, 0x3e, 0x22, 0x22,
+ 0x00, 0x3c, 0x22, 0x22, 0x3c, 0x22, 0x22, 0x3c,
+ 0x00, 0x1c, 0x22, 0x20, 0x20, 0x20, 0x22, 0x1c,
+ 0x00, 0x3c, 0x22, 0x22, 0x22, 0x22, 0x22, 0x3c,
+ 0x00, 0x3e, 0x20, 0x20, 0x3c, 0x20, 0x20, 0x3e,
+ 0x00, 0x3e, 0x20, 0x20, 0x3c, 0x20, 0x20, 0x20,
+ 0x00, 0x1e, 0x20, 0x20, 0x20, 0x26, 0x22, 0x1e,
+ 0x00, 0x22, 0x22, 0x22, 0x3e, 0x22, 0x22, 0x22,
+ 0x00, 0x1c, 0x08, 0x08, 0x08, 0x08, 0x08, 0x1c,
+ 0x00, 0x02, 0x02, 0x02, 0x02, 0x02, 0x22, 0x1c,
+ 0x00, 0x22, 0x24, 0x28, 0x30, 0x28, 0x24, 0x22,
+ 0x00, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3e,
+ 0x00, 0x22, 0x36, 0x2a, 0x2a, 0x22, 0x22, 0x22,
+ 0x00, 0x22, 0x22, 0x32, 0x2a, 0x26, 0x22, 0x22,
+ 0x00, 0x1c, 0x22, 0x22, 0x22, 0x22, 0x22, 0x1c,
+ 0x00, 0x3c, 0x22, 0x22, 0x3c, 0x20, 0x20, 0x20,
+ 0x00, 0x1c, 0x22, 0x22, 0x22, 0x2a, 0x24, 0x1a,
+ 0x00, 0x3c, 0x22, 0x22, 0x3c, 0x28, 0x24, 0x22,
+ 0x00, 0x1c, 0x22, 0x20, 0x1c, 0x02, 0x22, 0x1c,
+ 0x00, 0x3e, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
+ 0x00, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x1c,
+ 0x00, 0x22, 0x22, 0x22, 0x22, 0x22, 0x14, 0x08,
+ 0x00, 0x22, 0x22, 0x22, 0x2a, 0x2a, 0x36, 0x22,
+ 0x00, 0x22, 0x22, 0x14, 0x08, 0x14, 0x22, 0x22,
+ 0x00, 0x22, 0x22, 0x14, 0x08, 0x08, 0x08, 0x08,
+ 0x00, 0x3e, 0x02, 0x04, 0x08, 0x10, 0x20, 0x3e,
+ 0x00, 0x3e, 0x30, 0x30, 0x30, 0x30, 0x30, 0x3e,
+ 0x00, 0x00, 0x20, 0x10, 0x08, 0x04, 0x02, 0x00,
+ 0x00, 0x3e, 0x06, 0x06, 0x06, 0x06, 0x06, 0x3e,
+ 0x00, 0x00, 0x00, 0x08, 0x14, 0x22, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3e,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x08, 0x08, 0x08, 0x08, 0x08, 0x00, 0x08,
+ 0x00, 0x14, 0x14, 0x14, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x14, 0x14, 0x3e, 0x14, 0x3e, 0x14, 0x14,
+ 0x00, 0x08, 0x1e, 0x28, 0x1c, 0x0a, 0x3c, 0x08,
+ 0x00, 0x30, 0x32, 0x04, 0x08, 0x10, 0x26, 0x06,
+ 0x00, 0x10, 0x28, 0x28, 0x10, 0x2a, 0x24, 0x1a,
+ 0x00, 0x08, 0x08, 0x08, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x08, 0x10, 0x20, 0x20, 0x20, 0x10, 0x08,
+ 0x00, 0x08, 0x04, 0x02, 0x02, 0x02, 0x04, 0x08,
+ 0x00, 0x08, 0x2a, 0x1c, 0x08, 0x1c, 0x2a, 0x08,
+ 0x00, 0x00, 0x08, 0x08, 0x3e, 0x08, 0x08, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x08, 0x10,
+ 0x00, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08,
+ 0x00, 0x00, 0x02, 0x04, 0x08, 0x10, 0x20, 0x00,
+ 0x00, 0x1c, 0x22, 0x26, 0x2a, 0x32, 0x22, 0x1c,
+ 0x00, 0x08, 0x18, 0x08, 0x08, 0x08, 0x08, 0x1c,
+ 0x00, 0x1c, 0x22, 0x02, 0x0c, 0x10, 0x20, 0x3e,
+ 0x00, 0x3e, 0x02, 0x04, 0x0c, 0x02, 0x22, 0x1c,
+ 0x00, 0x04, 0x0c, 0x14, 0x24, 0x3e, 0x04, 0x04,
+ 0x00, 0x3e, 0x20, 0x3c, 0x02, 0x02, 0x22, 0x1c,
+ 0x00, 0x0e, 0x10, 0x20, 0x3c, 0x22, 0x22, 0x1c,
+ 0x00, 0x3e, 0x02, 0x04, 0x08, 0x10, 0x10, 0x10,
+ 0x00, 0x1c, 0x22, 0x22, 0x1c, 0x22, 0x22, 0x1c,
+ 0x00, 0x1c, 0x22, 0x22, 0x1e, 0x02, 0x04, 0x38,
+ 0x00, 0x00, 0x00, 0x08, 0x00, 0x08, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x08, 0x00, 0x08, 0x08, 0x10,
+ 0x00, 0x04, 0x08, 0x10, 0x20, 0x10, 0x08, 0x04,
+ 0x00, 0x00, 0x00, 0x3e, 0x00, 0x3e, 0x00, 0x00,
+ 0x00, 0x10, 0x08, 0x04, 0x02, 0x04, 0x08, 0x10,
+ 0x00, 0x1c, 0x22, 0x04, 0x08, 0x08, 0x00, 0x08,
+ 0x80, 0x9c, 0xa2, 0xaa, 0xae, 0xac, 0xa0, 0x9e,
+ 0x80, 0x88, 0x94, 0xa2, 0xa2, 0xbe, 0xa2, 0xa2,
+ 0x80, 0xbc, 0xa2, 0xa2, 0xbc, 0xa2, 0xa2, 0xbc,
+ 0x80, 0x9c, 0xa2, 0xa0, 0xa0, 0xa0, 0xa2, 0x9c,
+ 0x80, 0xbc, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xbc,
+ 0x80, 0xbe, 0xa0, 0xa0, 0xbc, 0xa0, 0xa0, 0xbe,
+ 0x80, 0xbe, 0xa0, 0xa0, 0xbc, 0xa0, 0xa0, 0xa0,
+ 0x80, 0x9e, 0xa0, 0xa0, 0xa0, 0xa6, 0xa2, 0x9e,
+ 0x80, 0xa2, 0xa2, 0xa2, 0xbe, 0xa2, 0xa2, 0xa2,
+ 0x80, 0x9c, 0x88, 0x88, 0x88, 0x88, 0x88, 0x9c,
+ 0x80, 0x82, 0x82, 0x82, 0x82, 0x82, 0xa2, 0x9c,
+ 0x80, 0xa2, 0xa4, 0xa8, 0xb0, 0xa8, 0xa4, 0xa2,
+ 0x80, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xbe,
+ 0x80, 0xa2, 0xb6, 0xaa, 0xaa, 0xa2, 0xa2, 0xa2,
+ 0x80, 0xa2, 0xa2, 0xb2, 0xaa, 0xa6, 0xa2, 0xa2,
+ 0x80, 0x9c, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0x9c,
+ 0x80, 0xbc, 0xa2, 0xa2, 0xbc, 0xa0, 0xa0, 0xa0,
+ 0x80, 0x9c, 0xa2, 0xa2, 0xa2, 0xaa, 0xa4, 0x9a,
+ 0x80, 0xbc, 0xa2, 0xa2, 0xbc, 0xa8, 0xa4, 0xa2,
+ 0x80, 0x9c, 0xa2, 0xa0, 0x9c, 0x82, 0xa2, 0x9c,
+ 0x80, 0xbe, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88,
+ 0x80, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0x9c,
+ 0x80, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0x94, 0x88,
+ 0x80, 0xa2, 0xa2, 0xa2, 0xaa, 0xaa, 0xb6, 0xa2,
+ 0x80, 0xa2, 0xa2, 0x94, 0x88, 0x94, 0xa2, 0xa2,
+ 0x80, 0xa2, 0xa2, 0x94, 0x88, 0x88, 0x88, 0x88,
+ 0x80, 0xbe, 0x82, 0x84, 0x88, 0x90, 0xa0, 0xbe,
+ 0x80, 0xbe, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xbe,
+ 0x80, 0x80, 0xa0, 0x90, 0x88, 0x84, 0x82, 0x80,
+ 0x80, 0xbe, 0x86, 0x86, 0x86, 0x86, 0x86, 0xbe,
+ 0x80, 0x80, 0x80, 0x88, 0x94, 0xa2, 0x80, 0x80,
+ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0xbe,
+ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+ 0x80, 0x88, 0x88, 0x88, 0x88, 0x88, 0x80, 0x88,
+ 0x80, 0x94, 0x94, 0x94, 0x80, 0x80, 0x80, 0x80,
+ 0x80, 0x94, 0x94, 0xbe, 0x94, 0xbe, 0x94, 0x94,
+ 0x80, 0x88, 0x9e, 0xa8, 0x9c, 0x8a, 0xbc, 0x88,
+ 0x80, 0xb0, 0xb2, 0x84, 0x88, 0x90, 0xa6, 0x86,
+ 0x80, 0x90, 0xa8, 0xa8, 0x90, 0xaa, 0xa4, 0x9a,
+ 0x80, 0x88, 0x88, 0x88, 0x80, 0x80, 0x80, 0x80,
+ 0x80, 0x88, 0x90, 0xa0, 0xa0, 0xa0, 0x90, 0x88,
+ 0x80, 0x88, 0x84, 0x82, 0x82, 0x82, 0x84, 0x88,
+ 0x80, 0x88, 0xaa, 0x9c, 0x88, 0x9c, 0xaa, 0x88,
+ 0x80, 0x80, 0x88, 0x88, 0xbe, 0x88, 0x88, 0x80,
+ 0x80, 0x80, 0x80, 0x80, 0x80, 0x88, 0x88, 0x90,
+ 0x80, 0x80, 0x80, 0x80, 0xbe, 0x80, 0x80, 0x80,
+ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x88,
+ 0x80, 0x80, 0x82, 0x84, 0x88, 0x90, 0xa0, 0x80,
+ 0x80, 0x9c, 0xa2, 0xa6, 0xaa, 0xb2, 0xa2, 0x9c,
+ 0x80, 0x88, 0x98, 0x88, 0x88, 0x88, 0x88, 0x9c,
+ 0x80, 0x9c, 0xa2, 0x82, 0x8c, 0x90, 0xa0, 0xbe,
+ 0x80, 0xbe, 0x82, 0x84, 0x8c, 0x82, 0xa2, 0x9c,
+ 0x80, 0x84, 0x8c, 0x94, 0xa4, 0xbe, 0x84, 0x84,
+ 0x80, 0xbe, 0xa0, 0xbc, 0x82, 0x82, 0xa2, 0x9c,
+ 0x80, 0x8e, 0x90, 0xa0, 0xbc, 0xa2, 0xa2, 0x9c,
+ 0x80, 0xbe, 0x82, 0x84, 0x88, 0x90, 0x90, 0x90,
+ 0x80, 0x9c, 0xa2, 0xa2, 0x9c, 0xa2, 0xa2, 0x9c,
+ 0x80, 0x9c, 0xa2, 0xa2, 0x9e, 0x82, 0x84, 0xb8,
+ 0x80, 0x80, 0x80, 0x88, 0x80, 0x88, 0x80, 0x80,
+ 0x80, 0x80, 0x80, 0x88, 0x80, 0x88, 0x88, 0x90,
+ 0x80, 0x84, 0x88, 0x90, 0xa0, 0x90, 0x88, 0x84,
+ 0x80, 0x80, 0x80, 0xbe, 0x80, 0xbe, 0x80, 0x80,
+ 0x80, 0x90, 0x88, 0x84, 0x82, 0x84, 0x88, 0x90,
+ 0x80, 0x9c, 0xa2, 0x84, 0x88, 0x88, 0x80, 0x88,
+ 0x00, 0x1c, 0x22, 0x2a, 0x2e, 0x2c, 0x20, 0x1e,
+ 0x00, 0x08, 0x14, 0x22, 0x22, 0x3e, 0x22, 0x22,
+ 0x00, 0x3c, 0x22, 0x22, 0x3c, 0x22, 0x22, 0x3c,
+ 0x00, 0x1c, 0x22, 0x20, 0x20, 0x20, 0x22, 0x1c,
+ 0x00, 0x3c, 0x22, 0x22, 0x22, 0x22, 0x22, 0x3c,
+ 0x00, 0x3e, 0x20, 0x20, 0x3c, 0x20, 0x20, 0x3e,
+ 0x00, 0x3e, 0x20, 0x20, 0x3c, 0x20, 0x20, 0x20,
+ 0x00, 0x1e, 0x20, 0x20, 0x20, 0x26, 0x22, 0x1e,
+ 0x00, 0x22, 0x22, 0x22, 0x3e, 0x22, 0x22, 0x22,
+ 0x00, 0x1c, 0x08, 0x08, 0x08, 0x08, 0x08, 0x1c,
+ 0x00, 0x02, 0x02, 0x02, 0x02, 0x02, 0x22, 0x1c,
+ 0x00, 0x22, 0x24, 0x28, 0x30, 0x28, 0x24, 0x22,
+ 0x00, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3e,
+ 0x00, 0x22, 0x36, 0x2a, 0x2a, 0x22, 0x22, 0x22,
+ 0x00, 0x22, 0x22, 0x32, 0x2a, 0x26, 0x22, 0x22,
+ 0x00, 0x1c, 0x22, 0x22, 0x22, 0x22, 0x22, 0x1c,
+ 0x00, 0x3c, 0x22, 0x22, 0x3c, 0x20, 0x20, 0x20,
+ 0x00, 0x1c, 0x22, 0x22, 0x22, 0x2a, 0x24, 0x1a,
+ 0x00, 0x3c, 0x22, 0x22, 0x3c, 0x28, 0x24, 0x22,
+ 0x00, 0x1c, 0x22, 0x20, 0x1c, 0x02, 0x22, 0x1c,
+ 0x00, 0x3e, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
+ 0x00, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x1c,
+ 0x00, 0x22, 0x22, 0x22, 0x22, 0x22, 0x14, 0x08,
+ 0x00, 0x22, 0x22, 0x22, 0x2a, 0x2a, 0x36, 0x22,
+ 0x00, 0x22, 0x22, 0x14, 0x08, 0x14, 0x22, 0x22,
+ 0x00, 0x22, 0x22, 0x14, 0x08, 0x08, 0x08, 0x08,
+ 0x00, 0x3e, 0x02, 0x04, 0x08, 0x10, 0x20, 0x3e,
+ 0x00, 0x3e, 0x30, 0x30, 0x30, 0x30, 0x30, 0x3e,
+ 0x00, 0x00, 0x20, 0x10, 0x08, 0x04, 0x02, 0x00,
+ 0x00, 0x3e, 0x06, 0x06, 0x06, 0x06, 0x06, 0x3e,
+ 0x00, 0x00, 0x00, 0x08, 0x14, 0x22, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3e,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x08, 0x08, 0x08, 0x08, 0x08, 0x00, 0x08,
+ 0x00, 0x14, 0x14, 0x14, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x14, 0x14, 0x3e, 0x14, 0x3e, 0x14, 0x14,
+ 0x00, 0x08, 0x1e, 0x28, 0x1c, 0x0a, 0x3c, 0x08,
+ 0x00, 0x30, 0x32, 0x04, 0x08, 0x10, 0x26, 0x06,
+ 0x00, 0x10, 0x28, 0x28, 0x10, 0x2a, 0x24, 0x1a,
+ 0x00, 0x08, 0x08, 0x08, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x08, 0x10, 0x20, 0x20, 0x20, 0x10, 0x08,
+ 0x00, 0x08, 0x04, 0x02, 0x02, 0x02, 0x04, 0x08,
+ 0x00, 0x08, 0x2a, 0x1c, 0x08, 0x1c, 0x2a, 0x08,
+ 0x00, 0x00, 0x08, 0x08, 0x3e, 0x08, 0x08, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x08, 0x10,
+ 0x00, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08,
+ 0x00, 0x00, 0x02, 0x04, 0x08, 0x10, 0x20, 0x00,
+ 0x00, 0x1c, 0x22, 0x26, 0x2a, 0x32, 0x22, 0x1c,
+ 0x00, 0x08, 0x18, 0x08, 0x08, 0x08, 0x08, 0x1c,
+ 0x00, 0x1c, 0x22, 0x02, 0x0c, 0x10, 0x20, 0x3e,
+ 0x00, 0x3e, 0x02, 0x04, 0x0c, 0x02, 0x22, 0x1c,
+ 0x00, 0x04, 0x0c, 0x14, 0x24, 0x3e, 0x04, 0x04,
+ 0x00, 0x3e, 0x20, 0x3c, 0x02, 0x02, 0x22, 0x1c,
+ 0x00, 0x0e, 0x10, 0x20, 0x3c, 0x22, 0x22, 0x1c,
+ 0x00, 0x3e, 0x02, 0x04, 0x08, 0x10, 0x10, 0x10,
+ 0x00, 0x1c, 0x22, 0x22, 0x1c, 0x22, 0x22, 0x1c,
+ 0x00, 0x1c, 0x22, 0x22, 0x1e, 0x02, 0x04, 0x38,
+ 0x00, 0x00, 0x00, 0x08, 0x00, 0x08, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x08, 0x00, 0x08, 0x08, 0x10,
+ 0x00, 0x04, 0x08, 0x10, 0x20, 0x10, 0x08, 0x04,
+ 0x00, 0x00, 0x00, 0x3e, 0x00, 0x3e, 0x00, 0x00,
+ 0x00, 0x10, 0x08, 0x04, 0x02, 0x04, 0x08, 0x10,
+ 0x00, 0x1c, 0x22, 0x04, 0x08, 0x08, 0x00, 0x08,
+ 0x80, 0x9c, 0xa2, 0xaa, 0xae, 0xac, 0xa0, 0x9e,
+ 0x80, 0x88, 0x94, 0xa2, 0xa2, 0xbe, 0xa2, 0xa2,
+ 0x80, 0xbc, 0xa2, 0xa2, 0xbc, 0xa2, 0xa2, 0xbc,
+ 0x80, 0x9c, 0xa2, 0xa0, 0xa0, 0xa0, 0xa2, 0x9c,
+ 0x80, 0xbc, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xbc,
+ 0x80, 0xbe, 0xa0, 0xa0, 0xbc, 0xa0, 0xa0, 0xbe,
+ 0x80, 0xbe, 0xa0, 0xa0, 0xbc, 0xa0, 0xa0, 0xa0,
+ 0x80, 0x9e, 0xa0, 0xa0, 0xa0, 0xa6, 0xa2, 0x9e,
+ 0x80, 0xa2, 0xa2, 0xa2, 0xbe, 0xa2, 0xa2, 0xa2,
+ 0x80, 0x9c, 0x88, 0x88, 0x88, 0x88, 0x88, 0x9c,
+ 0x80, 0x82, 0x82, 0x82, 0x82, 0x82, 0xa2, 0x9c,
+ 0x80, 0xa2, 0xa4, 0xa8, 0xb0, 0xa8, 0xa4, 0xa2,
+ 0x80, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xbe,
+ 0x80, 0xa2, 0xb6, 0xaa, 0xaa, 0xa2, 0xa2, 0xa2,
+ 0x80, 0xa2, 0xa2, 0xb2, 0xaa, 0xa6, 0xa2, 0xa2,
+ 0x80, 0x9c, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0x9c,
+ 0x80, 0xbc, 0xa2, 0xa2, 0xbc, 0xa0, 0xa0, 0xa0,
+ 0x80, 0x9c, 0xa2, 0xa2, 0xa2, 0xaa, 0xa4, 0x9a,
+ 0x80, 0xbc, 0xa2, 0xa2, 0xbc, 0xa8, 0xa4, 0xa2,
+ 0x80, 0x9c, 0xa2, 0xa0, 0x9c, 0x82, 0xa2, 0x9c,
+ 0x80, 0xbe, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88,
+ 0x80, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0x9c,
+ 0x80, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0x94, 0x88,
+ 0x80, 0xa2, 0xa2, 0xa2, 0xaa, 0xaa, 0xb6, 0xa2,
+ 0x80, 0xa2, 0xa2, 0x94, 0x88, 0x94, 0xa2, 0xa2,
+ 0x80, 0xa2, 0xa2, 0x94, 0x88, 0x88, 0x88, 0x88,
+ 0x80, 0xbe, 0x82, 0x84, 0x88, 0x90, 0xa0, 0xbe,
+ 0x80, 0xbe, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xbe,
+ 0x80, 0x80, 0xa0, 0x90, 0x88, 0x84, 0x82, 0x80,
+ 0x80, 0xbe, 0x86, 0x86, 0x86, 0x86, 0x86, 0xbe,
+ 0x80, 0x80, 0x80, 0x88, 0x94, 0xa2, 0x80, 0x80,
+ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0xbe,
+ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+ 0x80, 0x88, 0x88, 0x88, 0x88, 0x88, 0x80, 0x88,
+ 0x80, 0x94, 0x94, 0x94, 0x80, 0x80, 0x80, 0x80,
+ 0x80, 0x94, 0x94, 0xbe, 0x94, 0xbe, 0x94, 0x94,
+ 0x80, 0x88, 0x9e, 0xa8, 0x9c, 0x8a, 0xbc, 0x88,
+ 0x80, 0xb0, 0xb2, 0x84, 0x88, 0x90, 0xa6, 0x86,
+ 0x80, 0x90, 0xa8, 0xa8, 0x90, 0xaa, 0xa4, 0x9a,
+ 0x80, 0x88, 0x88, 0x88, 0x80, 0x80, 0x80, 0x80,
+ 0x80, 0x88, 0x90, 0xa0, 0xa0, 0xa0, 0x90, 0x88,
+ 0x80, 0x88, 0x84, 0x82, 0x82, 0x82, 0x84, 0x88,
+ 0x80, 0x88, 0xaa, 0x9c, 0x88, 0x9c, 0xaa, 0x88,
+ 0x80, 0x80, 0x88, 0x88, 0xbe, 0x88, 0x88, 0x80,
+ 0x80, 0x80, 0x80, 0x80, 0x80, 0x88, 0x88, 0x90,
+ 0x80, 0x80, 0x80, 0x80, 0xbe, 0x80, 0x80, 0x80,
+ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x88,
+ 0x80, 0x80, 0x82, 0x84, 0x88, 0x90, 0xa0, 0x80,
+ 0x80, 0x9c, 0xa2, 0xa6, 0xaa, 0xb2, 0xa2, 0x9c,
+ 0x80, 0x88, 0x98, 0x88, 0x88, 0x88, 0x88, 0x9c,
+ 0x80, 0x9c, 0xa2, 0x82, 0x8c, 0x90, 0xa0, 0xbe,
+ 0x80, 0xbe, 0x82, 0x84, 0x8c, 0x82, 0xa2, 0x9c,
+ 0x80, 0x84, 0x8c, 0x94, 0xa4, 0xbe, 0x84, 0x84,
+ 0x80, 0xbe, 0xa0, 0xbc, 0x82, 0x82, 0xa2, 0x9c,
+ 0x80, 0x8e, 0x90, 0xa0, 0xbc, 0xa2, 0xa2, 0x9c,
+ 0x80, 0xbe, 0x82, 0x84, 0x88, 0x90, 0x90, 0x90,
+ 0x80, 0x9c, 0xa2, 0xa2, 0x9c, 0xa2, 0xa2, 0x9c,
+ 0x80, 0x9c, 0xa2, 0xa2, 0x9e, 0x82, 0x84, 0xb8,
+ 0x80, 0x80, 0x80, 0x88, 0x80, 0x88, 0x80, 0x80,
+ 0x80, 0x80, 0x80, 0x88, 0x80, 0x88, 0x88, 0x90,
+ 0x80, 0x84, 0x88, 0x90, 0xa0, 0x90, 0x88, 0x84,
+ 0x80, 0x80, 0x80, 0xbe, 0x80, 0xbe, 0x80, 0x80,
+ 0x80, 0x90, 0x88, 0x84, 0x82, 0x84, 0x88, 0x90,
+ 0x80, 0x9c, 0xa2, 0x84, 0x88, 0x88, 0x80, 0x88
];
// public domain ROM (http://a2go.applearchives.com/roms/)
@@ -1120,92 +1134,92 @@ const APPLEIIGO_LZG = `TFpHAAAwAAAABYxwdy2NARUZHjRBUFBMRUlJR08gUk9NMS4wADQfNB80H
/// Disk II
///
- const NUM_DRIVES = 2;
- const NUM_TRACKS = 35;
- const TRACK_SIZE = 0x1880;
- const SECTOR_SIZE = 383;
-
- const DISKII_PROM = [
- 0xA2,0x20,0xA0,0x00,0xA2,0x03,0x86,0x3C,0x8A,0x0A,0x24,0x3C,0xF0,0x10,0x05,0x3C
- ,0x49,0xFF,0x29,0x7E,0xB0,0x08,0x4A,0xD0,0xFB,0x98,0x9D,0x56,0x03,0xC8,0xE8,0x10
- ,0xE5,0x20,0x58,0xFF,0xBA,0xBD,0x00,0x01,0x0A,0x0A,0x0A,0x0A,0x85,0x2B,0xAA,0xBD
- ,0x8E,0xC0,0xBD,0x8C,0xC0,0xBD,0x8A,0xC0,0xBD,0x89,0xC0,0xA0,0x50,0xBD,0x80,0xC0
- ,0x98,0x29,0x03,0x0A,0x05,0x2B,0xAA,0xBD,0x81,0xC0,0xA9,0x56,
- /*0x20,0xA8,0xFC,*/0xa9,0x00,0xea,0x88
- ,0x10,0xEB,0x85,0x26,0x85,0x3D,0x85,0x41,0xA9,0x08,0x85,0x27,0x18,0x08,0xBD,0x8C
- ,0xC0,0x10,0xFB,0x49,0xD5,0xD0,0xF7,0xBD,0x8C,0xC0,0x10,0xFB,0xC9,0xAA,0xD0,0xF3
- ,0xEA,0xBD,0x8C,0xC0,0x10,0xFB,0xC9,0x96,0xF0,0x09,0x28,0x90,0xDF,0x49,0xAD,0xF0
- ,0x25,0xD0,0xD9,0xA0,0x03,0x85,0x40,0xBD,0x8C,0xC0,0x10,0xFB,0x2A,0x85,0x3C,0xBD
- ,0x8C,0xC0,0x10,0xFB,0x25,0x3C,0x88,0xD0,0xEC,0x28,0xC5,0x3D,0xD0,0xBE,0xA5,0x40
- ,0xC5,0x41,0xD0,0xB8,0xB0,0xB7,0xA0,0x56,0x84,0x3C,0xBC,0x8C,0xC0,0x10,0xFB,0x59
- ,0xD6,0x02,0xA4,0x3C,0x88,0x99,0x00,0x03,0xD0,0xEE,0x84,0x3C,0xBC,0x8C,0xC0,0x10
- ,0xFB,0x59,0xD6,0x02,0xA4,0x3C,0x91,0x26,0xC8,0xD0,0xEF,0xBC,0x8C,0xC0,0x10,0xFB
- ,0x59,0xD6,0x02,0xD0,0x87,0xA0,0x00,0xA2,0x56,0xCA,0x30,0xFB,0xB1,0x26,0x5E,0x00
- ,0x03,0x2A,0x5E,0x00,0x03,0x2A,0x91,0x26,0xC8,0xD0,0xEE,0xE6,0x27,0xE6,0x3D,0xA5
- ,0x3D,0xCD,0x00,0x08,0xA6,0x2B,0x90,0xDB,0x4C,0x01,0x08,0x00,0x00,0x00,0x00,0x00
- ];
+const NUM_DRIVES = 2;
+const NUM_TRACKS = 35;
+const TRACK_SIZE = 0x1880;
+const SECTOR_SIZE = 383;
+
+const DISKII_PROM = [
+ 0xA2, 0x20, 0xA0, 0x00, 0xA2, 0x03, 0x86, 0x3C, 0x8A, 0x0A, 0x24, 0x3C, 0xF0, 0x10, 0x05, 0x3C
+ , 0x49, 0xFF, 0x29, 0x7E, 0xB0, 0x08, 0x4A, 0xD0, 0xFB, 0x98, 0x9D, 0x56, 0x03, 0xC8, 0xE8, 0x10
+ , 0xE5, 0x20, 0x58, 0xFF, 0xBA, 0xBD, 0x00, 0x01, 0x0A, 0x0A, 0x0A, 0x0A, 0x85, 0x2B, 0xAA, 0xBD
+ , 0x8E, 0xC0, 0xBD, 0x8C, 0xC0, 0xBD, 0x8A, 0xC0, 0xBD, 0x89, 0xC0, 0xA0, 0x50, 0xBD, 0x80, 0xC0
+ , 0x98, 0x29, 0x03, 0x0A, 0x05, 0x2B, 0xAA, 0xBD, 0x81, 0xC0, 0xA9, 0x56,
+ /*0x20,0xA8,0xFC,*/0xa9, 0x00, 0xea, 0x88
+ , 0x10, 0xEB, 0x85, 0x26, 0x85, 0x3D, 0x85, 0x41, 0xA9, 0x08, 0x85, 0x27, 0x18, 0x08, 0xBD, 0x8C
+ , 0xC0, 0x10, 0xFB, 0x49, 0xD5, 0xD0, 0xF7, 0xBD, 0x8C, 0xC0, 0x10, 0xFB, 0xC9, 0xAA, 0xD0, 0xF3
+ , 0xEA, 0xBD, 0x8C, 0xC0, 0x10, 0xFB, 0xC9, 0x96, 0xF0, 0x09, 0x28, 0x90, 0xDF, 0x49, 0xAD, 0xF0
+ , 0x25, 0xD0, 0xD9, 0xA0, 0x03, 0x85, 0x40, 0xBD, 0x8C, 0xC0, 0x10, 0xFB, 0x2A, 0x85, 0x3C, 0xBD
+ , 0x8C, 0xC0, 0x10, 0xFB, 0x25, 0x3C, 0x88, 0xD0, 0xEC, 0x28, 0xC5, 0x3D, 0xD0, 0xBE, 0xA5, 0x40
+ , 0xC5, 0x41, 0xD0, 0xB8, 0xB0, 0xB7, 0xA0, 0x56, 0x84, 0x3C, 0xBC, 0x8C, 0xC0, 0x10, 0xFB, 0x59
+ , 0xD6, 0x02, 0xA4, 0x3C, 0x88, 0x99, 0x00, 0x03, 0xD0, 0xEE, 0x84, 0x3C, 0xBC, 0x8C, 0xC0, 0x10
+ , 0xFB, 0x59, 0xD6, 0x02, 0xA4, 0x3C, 0x91, 0x26, 0xC8, 0xD0, 0xEF, 0xBC, 0x8C, 0xC0, 0x10, 0xFB
+ , 0x59, 0xD6, 0x02, 0xD0, 0x87, 0xA0, 0x00, 0xA2, 0x56, 0xCA, 0x30, 0xFB, 0xB1, 0x26, 0x5E, 0x00
+ , 0x03, 0x2A, 0x5E, 0x00, 0x03, 0x2A, 0x91, 0x26, 0xC8, 0xD0, 0xEE, 0xE6, 0x27, 0xE6, 0x3D, 0xA5
+ , 0x3D, 0xCD, 0x00, 0x08, 0xA6, 0x2B, 0x90, 0xDB, 0x4C, 0x01, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00
+];
class DiskIIState {
- data : Uint8Array[];
- track : number = 0;
- read_mode : boolean = true;
- write_protect : boolean = false;
- motor : boolean = false;
- track_index : number = 0;
+ data: Uint8Array[];
+ track: number = 0;
+ read_mode: boolean = true;
+ write_protect: boolean = false;
+ motor: boolean = false;
+ track_index: number = 0;
}
class DiskII extends DiskIIState implements SlotDevice, SavesState {
- emu : AppleII;
- track_data : Uint8Array;
-
- constructor(emu : AppleII, image : Uint8Array) {
- super();
- this.emu = emu;
- this.data = new Array(NUM_TRACKS);
- for (var i=0; i>1];
- else
- this.track_data = null;
- }
-
- toLongString() {
- return "Track: " + (this.track / 2) +
+ emu: AppleII;
+ track_data: Uint8Array;
+
+ constructor(emu: AppleII, image: Uint8Array) {
+ super();
+ this.emu = emu;
+ this.data = new Array(NUM_TRACKS);
+ for (var i = 0; i < NUM_TRACKS; i++) {
+ var ofs = i * 16 * 256;
+ this.data[i] = nibblizeTrack(254, i, image.slice(ofs, ofs + 16 * 256));
+ }
+ }
+
+ saveState(): DiskIIState {
+ var s = {
+ data: new Array(NUM_TRACKS),
+ track: this.track,
+ read_mode: this.read_mode,
+ write_protect: this.write_protect,
+ motor: this.motor,
+ track_index: this.track_index
+ };
+ for (var i = 0; i < NUM_TRACKS; i++)
+ s.data[i] = this.data[i].slice(0);
+ return s;
+ }
+
+ loadState(s: DiskIIState) {
+ for (var i = 0; i < NUM_TRACKS; i++)
+ this.data[i].set(s.data[i]);
+ this.track = s.track;
+ this.read_mode = s.read_mode;
+ this.write_protect = s.write_protect;
+ this.motor = s.motor;
+ this.track_index = s.track_index;
+ if ((this.track & 1) == 0)
+ this.track_data = this.data[this.track >> 1];
+ else
+ this.track_data = null;
+ }
+
+ toLongString() {
+ return "Track: " + (this.track / 2) +
"\nOffset: " + (this.track_index) +
"\nMode: " + (this.read_mode ? "READ" : "WRITE") +
"\nMotor: " + this.motor +
"\nData: " + (this.track_data ? hex(this.track_data[this.track_index]) : '-') +
"\n";
- }
-
- read_latch() : number {
+ }
+
+ read_latch(): number {
this.track_index = (this.track_index + 1) % TRACK_SIZE;
if (this.track_data) {
return (this.track_data[this.track_index] & 0xff);
@@ -1218,16 +1232,14 @@ class DiskII extends DiskIIState implements SlotDevice, SavesState
if (this.track_data != null)
this.track_data[this.track_index] = value;
}
-
- readROM(address) { return DISKII_PROM[address]; }
- readConst(address) { return DISKII_PROM[address]; }
- read(address) { return this.doIO(address, 0); }
+
+ readROM(address) { return DISKII_PROM[address]; }
+ readConst(address) { return DISKII_PROM[address]; }
+ read(address) { return this.doIO(address, 0); }
write(address, value) { this.doIO(address, value); }
- doIO(address, value) : number
- {
- switch (address & 0x0f)
- {
+ doIO(address, value): number {
+ switch (address & 0x0f) {
/*
* Turn motor phases 0 to 3 on. Turning on the previous phase + 1
* increments the track position, turning on the previous phase - 1
@@ -1244,69 +1256,66 @@ class DiskII extends DiskIIState implements SlotDevice, SavesState
phase = (address >> 1) & 3;
// if new phase is even and current phase is odd
- if (phase == ((new_track - 1) & 3))
- {
+ if (phase == ((new_track - 1) & 3)) {
if (new_track > 0)
new_track--;
} else
- if (phase == ((new_track + 1) & 3))
- {
- if (new_track < NUM_TRACKS*2-1)
- new_track++;
- }
- if ((new_track & 1) == 0)
- {
- this.track_data = this.data[new_track>>1];
- console.log('track', new_track/2);
+ if (phase == ((new_track + 1) & 3)) {
+ if (new_track < NUM_TRACKS * 2 - 1)
+ new_track++;
+ }
+ if ((new_track & 1) == 0) {
+ this.track_data = this.data[new_track >> 1];
+ console.log('track', new_track / 2);
} else
this.track_data = null;
this.track = new_track;
break;
- /*
- * Turn drive motor off.
- */
+ /*
+ * Turn drive motor off.
+ */
case 0x8:
this.motor = false;
break;
- /*
- * Turn drive motor on.
- */
+ /*
+ * Turn drive motor on.
+ */
case 0x9:
this.motor = true;
- break;
- /*
- * Select drive 1.
- */
+ break;
+ /*
+ * Select drive 1.
+ */
case 0xa:
//drive = 0;
break;
- /*
- * Select drive 2.
- */
+ /*
+ * Select drive 2.
+ */
case 0xb:
//drive = 1;
break;
- /*
- * Select write mode.
- */
+ /*
+ * Select write mode.
+ */
case 0xf:
this.read_mode = false;
- /*
- * Read a disk byte if read mode is active.
- */
+ /*
+ * Read a disk byte if read mode is active.
+ */
case 0xC:
if (this.read_mode)
return this.read_latch();
break;
- /*
- * Select read mode and read the write protect status.
- */
+ /*
+ * Select read mode and read the write protect status.
+ */
case 0xE:
this.read_mode = true;
- /*
- * Write a disk byte if write mode is active and the disk is not
- * write protected.
- */
+ /*
+ * Write a disk byte if write mode is active and the disk is not
+ * write protected.
+ */
case 0xD:
if (value >= 0 && !this.read_mode && !this.write_protect)
this.write_latch(value);
@@ -1322,27 +1331,27 @@ class DiskII extends DiskIIState implements SlotDevice, SavesState
/* --------------- TRACK CONVERSION ROUTINES ---------------------- */
- /*
- * Normal byte (lower six bits only) -> disk byte translation table.
- */
- const byte_translation = [
- 0x96, 0x97, 0x9a, 0x9b, 0x9d, 0x9e, 0x9f, 0xa6,
- 0xa7, 0xab, 0xac, 0xad, 0xae, 0xaf, 0xb2, 0xb3,
- 0xb4, 0xb5, 0xb6, 0xb7, 0xb9, 0xba, 0xbb, 0xbc,
- 0xbd, 0xbe, 0xbf, 0xcb, 0xcd, 0xce, 0xcf, 0xd3,
- 0xd6, 0xd7, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde,
- 0xdf, 0xe5, 0xe6, 0xe7, 0xe9, 0xea, 0xeb, 0xec,
- 0xed, 0xee, 0xef, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6,
- 0xf7, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff
- ];
+/*
+ * Normal byte (lower six bits only) -> disk byte translation table.
+ */
+const byte_translation = [
+ 0x96, 0x97, 0x9a, 0x9b, 0x9d, 0x9e, 0x9f, 0xa6,
+ 0xa7, 0xab, 0xac, 0xad, 0xae, 0xaf, 0xb2, 0xb3,
+ 0xb4, 0xb5, 0xb6, 0xb7, 0xb9, 0xba, 0xbb, 0xbc,
+ 0xbd, 0xbe, 0xbf, 0xcb, 0xcd, 0xce, 0xcf, 0xd3,
+ 0xd6, 0xd7, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde,
+ 0xdf, 0xe5, 0xe6, 0xe7, 0xe9, 0xea, 0xeb, 0xec,
+ 0xed, 0xee, 0xef, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6,
+ 0xf7, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff
+];
- /*
- * Sector skewing table.
- */
+/*
+ * Sector skewing table.
+ */
- const skewing_table = [
- 0,7,14,6,13,5,12,4,11,3,10,2,9,1,8,15
- ];
+const skewing_table = [
+ 0, 7, 14, 6, 13, 5, 12, 4, 11, 3, 10, 2, 9, 1, 8, 15
+];
/*
* Encode a 256-byte sector as SECTOR_SIZE disk bytes as follows:
@@ -1356,149 +1365,143 @@ class DiskII extends DiskIIState implements SlotDevice, SavesState
* 343 data block bytes
* 3 data trailer bytes
*/
- function nibblizeSector(vol, trk, sector, inn, in_ofs, out, i)
- {
- var loop, checksum, prev_value, value;
- var sector_buffer = new Uint8Array(258);
- value = 0;
-
- /*
- * Step 1: write 6 sync bytes (0xff's). Normally these would be
- * written as 10-bit bytes with two extra zero bits, but for the
- * purpose of emulation normal 8-bit bytes will do, since the
- * emulated drive will always be in sync.
- */
- for (loop = 0; loop < 14; loop++)
- out[i++] = 0xff;
-
- /*
- * Step 2: write the 3-byte address header (0xd5 0xaa 0x96).
- */
- out[i++] = 0xd5;
- out[i++] = 0xaa;
- out[i++] = 0x96;
-
- /*
- * Step 3: write the address block. Use 4-and-4 encoding to convert
- * the volume, track and sector and checksum into 2 disk bytes each.
- * The checksum is a simple exclusive OR of the first three values.
- */
- out[i++] = ((vol >> 1) | 0xaa);
- out[i++] = (vol | 0xaa);
- checksum = vol;
- out[i++] = ((trk >> 1) | 0xaa);
- out[i++] = (trk | 0xaa);
- checksum ^= trk;
- out[i++] = ((sector >> 1) | 0xaa);
- out[i++] = (sector | 0xaa);
- checksum ^= sector;
- out[i++] = ((checksum >> 1) | 0xaa);
- out[i++] = (checksum | 0xaa);
-
- /*
- * Step 4: write the 3-byte address trailer (0xde 0xaa 0xeb).
- */
- out[i++] = (0xde);
- out[i++] = (0xaa);
- out[i++] = (0xeb);
-
- /*
- * Step 5: write another 6 sync bytes.
- */
- for (loop = 0; loop < 6; loop++)
- out[i++] = (0xff);
-
- /*
- * Step 6: write the 3-byte data header.
- */
- out[i++] = (0xd5);
- out[i++] = (0xaa);
- out[i++] = (0xad);
-
- /*
- * Step 7: read the next 256-byte sector from the old disk image file,
- * and add two zero bytes to bring the number of bytes up to a multiple
- * of 3.
- */
- for (loop = 0; loop < 256; loop++)
- sector_buffer[loop] = inn[loop + in_ofs] & 0xff;
- sector_buffer[256] = 0;
- sector_buffer[257] = 0;
-
- /*
- * Step 8: write the first 86 disk bytes of the data block, which
- * encodes the bottom two bits of each sector byte into six-bit
- * values as follows:
- *
- * disk byte n, bit 0 = sector byte n, bit 1
- * disk byte n, bit 1 = sector byte n, bit 0
- * disk byte n, bit 2 = sector byte n + 86, bit 1
- * disk byte n, bit 3 = sector byte n + 86, bit 0
- * disk byte n, bit 4 = sector byte n + 172, bit 1
- * disk byte n, bit 5 = sector byte n + 172, bit 0
- *
- * The scheme allows each pair of bits to be shifted to the right out
- * of the disk byte, then shifted to the left into the sector byte.
- *
- * Before the 6-bit value is translated to a disk byte, it is exclusive
- * ORed with the previous 6-bit value, hence the values written are
- * really a running checksum.
- */
- prev_value = 0;
- for (loop = 0; loop < 86; loop++)
- {
- value = (sector_buffer[loop] & 0x01) << 1;
- value |= (sector_buffer[loop] & 0x02) >> 1;
- value |= (sector_buffer[loop + 86] & 0x01) << 3;
- value |= (sector_buffer[loop + 86] & 0x02) << 1;
- value |= (sector_buffer[loop + 172] & 0x01) << 5;
- value |= (sector_buffer[loop + 172] & 0x02) << 3;
- out[i++] = (byte_translation[value ^ prev_value]);
- prev_value = value;
- }
-
- /*
- * Step 9: write the last 256 disk bytes of the data block, which
- * encodes the top six bits of each sector byte. Again, each value
- * is exclusive ORed with the previous value to create a running
- * checksum (the first value is exclusive ORed with the last value of
- * the previous step).
- */
-
- for (loop = 0; loop < 256; loop++)
- {
- value = (sector_buffer[loop] >> 2);
- out[i++] = (byte_translation[value ^ prev_value]);
- prev_value = value;
- }
+function nibblizeSector(vol, trk, sector, inn, in_ofs, out, i) {
+ var loop, checksum, prev_value, value;
+ var sector_buffer = new Uint8Array(258);
+ value = 0;
+
+ /*
+ * Step 1: write 6 sync bytes (0xff's). Normally these would be
+ * written as 10-bit bytes with two extra zero bits, but for the
+ * purpose of emulation normal 8-bit bytes will do, since the
+ * emulated drive will always be in sync.
+ */
+ for (loop = 0; loop < 14; loop++)
+ out[i++] = 0xff;
+
+ /*
+ * Step 2: write the 3-byte address header (0xd5 0xaa 0x96).
+ */
+ out[i++] = 0xd5;
+ out[i++] = 0xaa;
+ out[i++] = 0x96;
+
+ /*
+ * Step 3: write the address block. Use 4-and-4 encoding to convert
+ * the volume, track and sector and checksum into 2 disk bytes each.
+ * The checksum is a simple exclusive OR of the first three values.
+ */
+ out[i++] = ((vol >> 1) | 0xaa);
+ out[i++] = (vol | 0xaa);
+ checksum = vol;
+ out[i++] = ((trk >> 1) | 0xaa);
+ out[i++] = (trk | 0xaa);
+ checksum ^= trk;
+ out[i++] = ((sector >> 1) | 0xaa);
+ out[i++] = (sector | 0xaa);
+ checksum ^= sector;
+ out[i++] = ((checksum >> 1) | 0xaa);
+ out[i++] = (checksum | 0xaa);
+
+ /*
+ * Step 4: write the 3-byte address trailer (0xde 0xaa 0xeb).
+ */
+ out[i++] = (0xde);
+ out[i++] = (0xaa);
+ out[i++] = (0xeb);
+
+ /*
+ * Step 5: write another 6 sync bytes.
+ */
+ for (loop = 0; loop < 6; loop++)
+ out[i++] = (0xff);
- /*
- * Step 10: write the last value as the checksum.
- */
- out[i++] = (byte_translation[value]);
+ /*
+ * Step 6: write the 3-byte data header.
+ */
+ out[i++] = (0xd5);
+ out[i++] = (0xaa);
+ out[i++] = (0xad);
- /*
- * Step 11: write the 3-byte data trailer.
- */
- out[i++] = (0xde);
- out[i++] = (0xaa);
- out[i++] = (0xeb);
+ /*
+ * Step 7: read the next 256-byte sector from the old disk image file,
+ * and add two zero bytes to bring the number of bytes up to a multiple
+ * of 3.
+ */
+ for (loop = 0; loop < 256; loop++)
+ sector_buffer[loop] = inn[loop + in_ofs] & 0xff;
+ sector_buffer[256] = 0;
+ sector_buffer[257] = 0;
+ /*
+ * Step 8: write the first 86 disk bytes of the data block, which
+ * encodes the bottom two bits of each sector byte into six-bit
+ * values as follows:
+ *
+ * disk byte n, bit 0 = sector byte n, bit 1
+ * disk byte n, bit 1 = sector byte n, bit 0
+ * disk byte n, bit 2 = sector byte n + 86, bit 1
+ * disk byte n, bit 3 = sector byte n + 86, bit 0
+ * disk byte n, bit 4 = sector byte n + 172, bit 1
+ * disk byte n, bit 5 = sector byte n + 172, bit 0
+ *
+ * The scheme allows each pair of bits to be shifted to the right out
+ * of the disk byte, then shifted to the left into the sector byte.
+ *
+ * Before the 6-bit value is translated to a disk byte, it is exclusive
+ * ORed with the previous 6-bit value, hence the values written are
+ * really a running checksum.
+ */
+ prev_value = 0;
+ for (loop = 0; loop < 86; loop++) {
+ value = (sector_buffer[loop] & 0x01) << 1;
+ value |= (sector_buffer[loop] & 0x02) >> 1;
+ value |= (sector_buffer[loop + 86] & 0x01) << 3;
+ value |= (sector_buffer[loop + 86] & 0x02) << 1;
+ value |= (sector_buffer[loop + 172] & 0x01) << 5;
+ value |= (sector_buffer[loop + 172] & 0x02) << 3;
+ out[i++] = (byte_translation[value ^ prev_value]);
+ prev_value = value;
}
- function nibblizeTrack(vol, trk, inn)
- {
- var out = new Uint8Array(TRACK_SIZE);
- var out_pos = 0;
- for (var sector = 0; sector < 16; sector++) {
- nibblizeSector(vol, trk, sector,
- inn, skewing_table[sector] << 8,
- out, out_pos);
- out_pos += SECTOR_SIZE;
- }
- while (out_pos < TRACK_SIZE)
- out[out_pos++] = (0xff);
- return out;
+ /*
+ * Step 9: write the last 256 disk bytes of the data block, which
+ * encodes the top six bits of each sector byte. Again, each value
+ * is exclusive ORed with the previous value to create a running
+ * checksum (the first value is exclusive ORed with the last value of
+ * the previous step).
+ */
+
+ for (loop = 0; loop < 256; loop++) {
+ value = (sector_buffer[loop] >> 2);
+ out[i++] = (byte_translation[value ^ prev_value]);
+ prev_value = value;
}
+ /*
+ * Step 10: write the last value as the checksum.
+ */
+ out[i++] = (byte_translation[value]);
+
+ /*
+ * Step 11: write the 3-byte data trailer.
+ */
+ out[i++] = (0xde);
+ out[i++] = (0xaa);
+ out[i++] = (0xeb);
+}
+
+function nibblizeTrack(vol, trk, inn) {
+ var out = new Uint8Array(TRACK_SIZE);
+ var out_pos = 0;
+ for (var sector = 0; sector < 16; sector++) {
+ nibblizeSector(vol, trk, sector,
+ inn, skewing_table[sector] << 8,
+ out, out_pos);
+ out_pos += SECTOR_SIZE;
+ }
+ while (out_pos < TRACK_SIZE)
+ out[out_pos++] = (0xff);
+ return out;
+}
diff --git a/src/platform/apple2.ts b/src/platform/apple2.ts
index ef6e5273a..cbf0af488 100644
--- a/src/platform/apple2.ts
+++ b/src/platform/apple2.ts
@@ -21,6 +21,7 @@ const APPLE2_PRESETS: Preset[] = [
{ id: 'conway.a', name: "Conway's Game of Life" },
{ id: 'lz4fh.a', name: "LZ4FH Decompressor" },
{ id: 'deltamod.dasm', name: "Delta Modulation Audio" },
+ { id: 'paddles.dasm', name: "Paddles/Joystick" },
// {id:'zap.dasm', name:"ZAP!"},
// {id:'tb_6502.s', name:'Tom Bombem (assembler game)'},
{ id: 'dos33bin.a', name: "DOS 3.3 Binary" },