mirror of
https://github.com/whscullin/apple2js.git
synced 2024-01-12 14:14:38 +00:00
VideoModes refactor part 1 (#60)
* Unify behaviors * remove vestigial overdrawing * handle bank 1 lores writes better * carry over last bit when shifted * Cleanup.
This commit is contained in:
parent
f53e50117e
commit
5c7e335aad
308
js/canvas.ts
308
js/canvas.ts
@ -1,4 +1,4 @@
|
|||||||
/* Copyright 2010-2019 Will Scullin <scullin@scullinsteel.com>
|
/* Copyright 2010-2021 Will Scullin <scullin@scullinsteel.com>
|
||||||
*
|
*
|
||||||
* Permission to use, copy, modify, distribute, and sell this software and its
|
* Permission to use, copy, modify, distribute, and sell this software and its
|
||||||
* documentation for any purpose is hereby granted without fee, provided that
|
* documentation for any purpose is hereby granted without fee, provided that
|
||||||
@ -38,21 +38,38 @@ let highColorHGRMode = false;
|
|||||||
let highColorTextMode = false;
|
let highColorTextMode = false;
|
||||||
let oneSixtyMode = false;
|
let oneSixtyMode = false;
|
||||||
|
|
||||||
function dim(c: Color): Color {
|
const tmpCanvas = document.createElement('canvas');
|
||||||
|
const tmpContext = tmpCanvas.getContext('2d');
|
||||||
|
|
||||||
|
const buildScreen = (mainData: ImageData, mixData?: ImageData | null) => {
|
||||||
|
if (!tmpContext) {
|
||||||
|
throw new Error('No 2d context');
|
||||||
|
}
|
||||||
|
|
||||||
|
const { width, height } = { width: 560, height: 192 };
|
||||||
|
const { x, y } = _80colMode ? { x: 0, y: 0 } : { x: 0, y: 0 };
|
||||||
|
|
||||||
|
tmpCanvas.width = width;
|
||||||
|
tmpCanvas.height = height;
|
||||||
|
tmpContext.fillStyle = 'rgba(0,0,0,1)';
|
||||||
|
tmpContext.fillRect(0, 0, width, height);
|
||||||
|
|
||||||
|
if (mixData) {
|
||||||
|
tmpContext.putImageData(mainData, x, y, 0, 0, 560, 160);
|
||||||
|
tmpContext.putImageData(mixData, x, y, 0, 160, 560, 32);
|
||||||
|
} else {
|
||||||
|
tmpContext.putImageData(mainData, x, y);
|
||||||
|
}
|
||||||
|
return tmpCanvas;
|
||||||
|
};
|
||||||
|
|
||||||
|
const dim = (c: Color): Color => {
|
||||||
return [
|
return [
|
||||||
c[0] * 0.75 & 0xff,
|
c[0] * 0.75 & 0xff,
|
||||||
c[1] * 0.75 & 0xff,
|
c[1] * 0.75 & 0xff,
|
||||||
c[2] * 0.75 & 0xff
|
c[2] * 0.75 & 0xff
|
||||||
];
|
];
|
||||||
}
|
};
|
||||||
|
|
||||||
function rowToBase(row: number) {
|
|
||||||
const ab = (row >> 3) & 3;
|
|
||||||
const cd = (row >> 1) & 0x3;
|
|
||||||
const e = row & 1;
|
|
||||||
return (cd << 8) | (e << 7) | (ab << 5) | (ab << 3);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// hires colors
|
// hires colors
|
||||||
const orangeCol: Color = [255, 106, 60];
|
const orangeCol: Color = [255, 106, 60];
|
||||||
@ -125,7 +142,7 @@ const dcolors: Color[] = [
|
|||||||
];
|
];
|
||||||
|
|
||||||
const notDirty: Region = {
|
const notDirty: Region = {
|
||||||
top: 385,
|
top: 193,
|
||||||
bottom: -1,
|
bottom: -1,
|
||||||
left: 561,
|
left: 561,
|
||||||
right: -1
|
right: -1
|
||||||
@ -153,34 +170,38 @@ export class LoresPage2D implements LoresPage {
|
|||||||
constructor(private page: number,
|
constructor(private page: number,
|
||||||
private readonly charset: rom,
|
private readonly charset: rom,
|
||||||
private readonly e: boolean) {
|
private readonly e: boolean) {
|
||||||
this.imageData = new ImageData(560, 384);
|
this.imageData = new ImageData(560, 192);
|
||||||
for (let idx = 0; idx < 560 * 384 * 4; idx++) {
|
for (let idx = 0; idx < 560 * 192 * 4; idx++) {
|
||||||
this.imageData.data[idx] = 0xff;
|
this.imageData.data[idx] = 0xff;
|
||||||
}
|
}
|
||||||
this._buffer[0] = allocMemPages(0x4);
|
this._buffer[0] = allocMemPages(0x4);
|
||||||
this._buffer[1] = allocMemPages(0x4);
|
this._buffer[1] = allocMemPages(0x4);
|
||||||
}
|
}
|
||||||
|
|
||||||
_drawPixel(data: Uint8ClampedArray, off: number, color: Color) {
|
private _drawPixel(data: Uint8ClampedArray, off: number, color: Color) {
|
||||||
const c0 = color[0], c1 = color[1], c2 = color[2];
|
const c0 = color[0], c1 = color[1], c2 = color[2];
|
||||||
data[off + 0] = data[off + 4] = c0;
|
data[off + 0] = data[off + 4] = c0;
|
||||||
data[off + 1] = data[off + 5] = c1;
|
data[off + 1] = data[off + 5] = c1;
|
||||||
data[off + 2] = data[off + 6] = c2;
|
data[off + 2] = data[off + 6] = c2;
|
||||||
const nextOff = off + 560 * 4;
|
|
||||||
data[nextOff] = data[nextOff + 4] = c0;
|
|
||||||
data[nextOff + 1] = data[nextOff + 5] = c1;
|
|
||||||
data[nextOff + 2] = data[nextOff + 6] = c2;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
_drawHalfPixel(data: Uint8ClampedArray, off: number, color: Color) {
|
private _drawHalfPixel(data: Uint8ClampedArray, off: number, color: Color) {
|
||||||
const c0 = color[0], c1 = color[1], c2 = color[2];
|
const c0 = color[0], c1 = color[1], c2 = color[2];
|
||||||
data[off + 0] = c0;
|
data[off + 0] = c0;
|
||||||
data[off + 1] = c1;
|
data[off + 1] = c1;
|
||||||
data[off + 2] = c2;
|
data[off + 2] = c2;
|
||||||
const nextOff = off + 560 * 4;
|
}
|
||||||
data[nextOff] = c0;
|
|
||||||
data[nextOff + 1] = c1;
|
private _checkInverse(val: byte) {
|
||||||
data[nextOff + 2] = c2;
|
let inverse = false;
|
||||||
|
if (this.e) {
|
||||||
|
if (!_80colMode && !altCharMode) {
|
||||||
|
inverse = ((val & 0xc0) == 0x40) && this._blink;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
inverse = !((val & 0x80) || (val & 0x40) && this._blink);
|
||||||
|
}
|
||||||
|
return inverse;
|
||||||
}
|
}
|
||||||
|
|
||||||
bank0(): MemoryPages {
|
bank0(): MemoryPages {
|
||||||
@ -203,17 +224,18 @@ export class LoresPage2D implements LoresPage {
|
|||||||
|
|
||||||
// These are used by both bank 0 and 1
|
// These are used by both bank 0 and 1
|
||||||
|
|
||||||
_start() {
|
private _start() {
|
||||||
return (0x04 * this.page);
|
return (0x04 * this.page);
|
||||||
}
|
}
|
||||||
_end() { return (0x04 * this.page) + 0x03; }
|
|
||||||
|
|
||||||
_read(page: byte, off: byte, bank: bank) {
|
private _end() { return (0x04 * this.page) + 0x03; }
|
||||||
|
|
||||||
|
private _read(page: byte, off: byte, bank: bank) {
|
||||||
const addr = (page << 8) | off, base = addr & 0x3FF;
|
const addr = (page << 8) | off, base = addr & 0x3FF;
|
||||||
return this._buffer[bank][base];
|
return this._buffer[bank][base];
|
||||||
}
|
}
|
||||||
|
|
||||||
_write(page: byte, off: byte, val: byte, bank: bank) {
|
private _write(page: byte, off: byte, val: byte, bank: bank) {
|
||||||
const addr = (page << 8) | off;
|
const addr = (page << 8) | off;
|
||||||
const base = addr & 0x3FF;
|
const base = addr & 0x3FF;
|
||||||
let fore, back;
|
let fore, back;
|
||||||
@ -234,58 +256,55 @@ export class LoresPage2D implements LoresPage {
|
|||||||
|
|
||||||
const data = this.imageData.data;
|
const data = this.imageData.data;
|
||||||
if ((row < 24) && (col < 40)) {
|
if ((row < 24) && (col < 40)) {
|
||||||
let y = row << 4;
|
let y = row << 3;
|
||||||
if (y < this.dirty.top) { this.dirty.top = y; }
|
if (y < this.dirty.top) { this.dirty.top = y; }
|
||||||
y += 16;
|
y += 8;
|
||||||
if (y > this.dirty.bottom) { this.dirty.bottom = y; }
|
if (y > this.dirty.bottom) { this.dirty.bottom = y; }
|
||||||
let x = col * 14;
|
let x = col * 14;
|
||||||
if (x < this.dirty.left) { this.dirty.left = x; }
|
if (x < this.dirty.left) { this.dirty.left = x; }
|
||||||
x += 14;
|
x += 14;
|
||||||
if (x > this.dirty.right) { this.dirty.right = x; }
|
if (x > this.dirty.right) { this.dirty.right = x; }
|
||||||
|
|
||||||
let color;
|
|
||||||
if (textMode || hiresMode || (mixedMode && row > 19)) {
|
if (textMode || hiresMode || (mixedMode && row > 19)) {
|
||||||
let inverse;
|
|
||||||
if (this.e) {
|
|
||||||
if (!_80colMode && !altCharMode) {
|
|
||||||
inverse = ((val & 0xc0) == 0x40) && this._blink;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
inverse = !((val & 0x80) || (val & 0x40) && this._blink);
|
|
||||||
}
|
|
||||||
|
|
||||||
fore = inverse ? blackCol : whiteCol;
|
|
||||||
back = inverse ? whiteCol : blackCol;
|
|
||||||
|
|
||||||
if (_80colMode) {
|
if (_80colMode) {
|
||||||
|
const inverse = this._checkInverse(val);
|
||||||
|
|
||||||
|
fore = inverse ? blackCol : whiteCol;
|
||||||
|
back = inverse ? whiteCol : blackCol;
|
||||||
|
|
||||||
if (!enhanced) {
|
if (!enhanced) {
|
||||||
val = (val >= 0x40 && val < 0x60) ? val - 0x40 : val;
|
val = (val >= 0x40 && val < 0x60) ? val - 0x40 : val;
|
||||||
} else if (!altCharMode) {
|
} else if (!altCharMode) {
|
||||||
val = (val >= 0x40 && val < 0x80) ? val - 0x40 : val;
|
val = (val >= 0x40 && val < 0x80) ? val - 0x40 : val;
|
||||||
}
|
}
|
||||||
|
|
||||||
off = (col * 14 + (bank ? 0 : 1) * 7 + row * 560 * 8 * 2) * 4;
|
let offset = (col * 14 + (bank ? 0 : 1) * 7 + row * 560 * 8) * 4;
|
||||||
|
|
||||||
for (let jdx = 0; jdx < 8; jdx++) {
|
for (let jdx = 0; jdx < 8; jdx++) {
|
||||||
let b = this.charset[val * 8 + jdx];
|
let b = this.charset[val * 8 + jdx];
|
||||||
for (let idx = 0; idx < 7; idx++) {
|
for (let idx = 0; idx < 7; idx++) {
|
||||||
color = (b & 0x01) ? back : fore;
|
const color = (b & 0x01) ? back : fore;
|
||||||
this._drawHalfPixel(data, off, color);
|
this._drawHalfPixel(data, offset, color);
|
||||||
b >>= 1;
|
b >>= 1;
|
||||||
off += 4;
|
offset += 4;
|
||||||
}
|
}
|
||||||
off += 553 * 4 + 560 * 4;
|
offset += 553 * 4;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
val = this._buffer[0][base];
|
val = this._buffer[0][base];
|
||||||
|
|
||||||
|
const inverse = this._checkInverse(val);
|
||||||
|
|
||||||
|
fore = inverse ? blackCol : whiteCol;
|
||||||
|
back = inverse ? whiteCol : blackCol;
|
||||||
|
|
||||||
if (!enhanced) {
|
if (!enhanced) {
|
||||||
val = (val >= 0x40 && val < 0x60) ? val - 0x40 : val;
|
val = (val >= 0x40 && val < 0x60) ? val - 0x40 : val;
|
||||||
} else if (!altCharMode) {
|
} else if (!altCharMode) {
|
||||||
val = (val >= 0x40 && val < 0x80) ? val - 0x40 : val;
|
val = (val >= 0x40 && val < 0x80) ? val - 0x40 : val;
|
||||||
}
|
}
|
||||||
|
|
||||||
off = (col * 14 + row * 560 * 8 * 2) * 4;
|
let offset = (col * 14 + row * 560 * 8) * 4;
|
||||||
|
|
||||||
if (highColorTextMode) {
|
if (highColorTextMode) {
|
||||||
fore = _colors[this._buffer[1][base] >> 4];
|
fore = _colors[this._buffer[1][base] >> 4];
|
||||||
@ -296,12 +315,12 @@ export class LoresPage2D implements LoresPage {
|
|||||||
for (let jdx = 0; jdx < 8; jdx++) {
|
for (let jdx = 0; jdx < 8; jdx++) {
|
||||||
let b = this.charset[val * 8 + jdx];
|
let b = this.charset[val * 8 + jdx];
|
||||||
for (let idx = 0; idx < 7; idx++) {
|
for (let idx = 0; idx < 7; idx++) {
|
||||||
color = (b & 0x01) ? back : fore;
|
const color = (b & 0x01) ? back : fore;
|
||||||
this._drawPixel(data, off, color);
|
this._drawPixel(data, offset, color);
|
||||||
b >>= 1;
|
b >>= 1;
|
||||||
off += 8;
|
offset += 8;
|
||||||
}
|
}
|
||||||
off += 546 * 4 + 560 * 4;
|
offset += 546 * 4;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
const colorMode = mixedMode && !textMode && !this._monoMode;
|
const colorMode = mixedMode && !textMode && !this._monoMode;
|
||||||
@ -318,6 +337,7 @@ export class LoresPage2D implements LoresPage {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for (let idx = 0; idx < 7; idx++) {
|
for (let idx = 0; idx < 7; idx++) {
|
||||||
|
let color;
|
||||||
if (colorMode) {
|
if (colorMode) {
|
||||||
if (b & 0x80) {
|
if (b & 0x80) {
|
||||||
if ((b & 0x1c0) != 0x80) {
|
if ((b & 0x1c0) != 0x80) {
|
||||||
@ -332,11 +352,11 @@ export class LoresPage2D implements LoresPage {
|
|||||||
} else {
|
} else {
|
||||||
color = (b & 0x80) ? fore : back;
|
color = (b & 0x80) ? fore : back;
|
||||||
}
|
}
|
||||||
this._drawPixel(data, off, color);
|
this._drawPixel(data, offset, color);
|
||||||
b <<= 1;
|
b <<= 1;
|
||||||
off += 8;
|
offset += 8;
|
||||||
}
|
}
|
||||||
off += 546 * 4 + 560 * 4;
|
offset += 546 * 4;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -345,7 +365,7 @@ export class LoresPage2D implements LoresPage {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (_80colMode && !an3) {
|
if (_80colMode && !an3) {
|
||||||
off = (col * 14 + (bank ? 0 : 1) * 7 + row * 560 * 8 * 2) * 4;
|
let offset = (col * 14 + (bank ? 0 : 1) * 7 + row * 560 * 8) * 4;
|
||||||
if (this._monoMode) {
|
if (this._monoMode) {
|
||||||
fore = whiteCol;
|
fore = whiteCol;
|
||||||
back = blackCol;
|
back = blackCol;
|
||||||
@ -356,29 +376,29 @@ export class LoresPage2D implements LoresPage {
|
|||||||
b <<= 1;
|
b <<= 1;
|
||||||
}
|
}
|
||||||
for (let idx = 0; idx < 7; idx++) {
|
for (let idx = 0; idx < 7; idx++) {
|
||||||
color = (b & 0x80) ? fore : back;
|
const color = (b & 0x80) ? fore : back;
|
||||||
this._drawHalfPixel(data, off, color);
|
this._drawHalfPixel(data, offset, color);
|
||||||
b <<= 1;
|
b <<= 1;
|
||||||
off += 4;
|
offset += 4;
|
||||||
}
|
}
|
||||||
off += 553 * 4 + 560 * 4;
|
offset += 553 * 4;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (bank & 0x1) {
|
if (bank & 0x1) {
|
||||||
val = ((val & 0x77) << 1) | ((val & 0x88) >> 3);
|
val = ((val & 0x77) << 1) | ((val & 0x88) >> 3);
|
||||||
}
|
}
|
||||||
for (let jdx = 0; jdx < 8; jdx++) {
|
for (let jdx = 0; jdx < 8; jdx++) {
|
||||||
color = _colors[(jdx < 4) ?
|
const color = _colors[(jdx < 4) ?
|
||||||
(val & 0x0f) : (val >> 4)];
|
(val & 0x0f) : (val >> 4)];
|
||||||
for (let idx = 0; idx < 7; idx++) {
|
for (let idx = 0; idx < 7; idx++) {
|
||||||
this._drawHalfPixel(data, off, color);
|
this._drawHalfPixel(data, offset, color);
|
||||||
off += 4;
|
off += 4;
|
||||||
}
|
}
|
||||||
off += 553 * 4 + 560 * 4;
|
offset += 553 * 4;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
off = (col * 14 + row * 560 * 8 * 2) * 4;
|
let offset = (col * 14 + row * 560 * 8) * 4;
|
||||||
|
|
||||||
if (this._monoMode) {
|
if (this._monoMode) {
|
||||||
fore = whiteCol;
|
fore = whiteCol;
|
||||||
@ -391,21 +411,21 @@ export class LoresPage2D implements LoresPage {
|
|||||||
b <<= 2;
|
b <<= 2;
|
||||||
}
|
}
|
||||||
for (let idx = 0; idx < 14; idx++) {
|
for (let idx = 0; idx < 14; idx++) {
|
||||||
color = (b & 0x8000) ? fore : back;
|
const color = (b & 0x8000) ? fore : back;
|
||||||
this._drawHalfPixel(data, off, color);
|
this._drawHalfPixel(data, offset, color);
|
||||||
b <<= 1;
|
b <<= 1;
|
||||||
off += 4;
|
offset += 4;
|
||||||
}
|
}
|
||||||
off += 546 * 4 + 560 * 4;
|
offset += 546 * 4;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
for (let jdx = 0; jdx < 8; jdx++) {
|
for (let jdx = 0; jdx < 8; jdx++) {
|
||||||
color = _colors[(jdx < 4) ? (val & 0x0f) : (val >> 4)];
|
const color = _colors[(jdx < 4) ? (val & 0x0f) : (val >> 4)];
|
||||||
for (let idx = 0; idx < 7; idx++) {
|
for (let idx = 0; idx < 7; idx++) {
|
||||||
this._drawPixel(data, off, color);
|
this._drawPixel(data, offset, color);
|
||||||
off += 8;
|
offset += 8;
|
||||||
}
|
}
|
||||||
off += 546 * 4 + 560 * 4;
|
offset += 546 * 4;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -425,6 +445,10 @@ export class LoresPage2D implements LoresPage {
|
|||||||
this._refreshing = false;
|
this._refreshing = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
mono(on: boolean) {
|
||||||
|
this._monoMode = on;
|
||||||
|
}
|
||||||
|
|
||||||
blink() {
|
blink() {
|
||||||
let addr = 0x400 * this.page;
|
let addr = 0x400 * this.page;
|
||||||
this._refreshing = true;
|
this._refreshing = true;
|
||||||
@ -438,11 +462,6 @@ export class LoresPage2D implements LoresPage {
|
|||||||
this._refreshing = false;
|
this._refreshing = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
mono(on: boolean) {
|
|
||||||
this._monoMode = on;
|
|
||||||
this.refresh();
|
|
||||||
}
|
|
||||||
|
|
||||||
start() {
|
start() {
|
||||||
setInterval(() => this.blink(), 267);
|
setInterval(() => this.blink(), 267);
|
||||||
return this._start();
|
return this._start();
|
||||||
@ -470,6 +489,7 @@ export class LoresPage2D implements LoresPage {
|
|||||||
]
|
]
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
setState(state: GraphicsState) {
|
setState(state: GraphicsState) {
|
||||||
this.page = state.page;
|
this.page = state.page;
|
||||||
this._monoMode = state.mono;
|
this._monoMode = state.mono;
|
||||||
@ -479,6 +499,13 @@ export class LoresPage2D implements LoresPage {
|
|||||||
this.refresh();
|
this.refresh();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private rowToBase(row: number) {
|
||||||
|
const ab = (row >> 3) & 3;
|
||||||
|
const cd = (row >> 1) & 0x3;
|
||||||
|
const e = row & 1;
|
||||||
|
return (cd << 8) | (e << 7) | (ab << 5) | (ab << 3);
|
||||||
|
}
|
||||||
|
|
||||||
private mapCharCode(charCode: byte) {
|
private mapCharCode(charCode: byte) {
|
||||||
charCode &= 0x7F;
|
charCode &= 0x7F;
|
||||||
if (charCode < 0x20) {
|
if (charCode < 0x20) {
|
||||||
@ -494,7 +521,7 @@ export class LoresPage2D implements LoresPage {
|
|||||||
let buffer = '', line, charCode;
|
let buffer = '', line, charCode;
|
||||||
let row, col, base;
|
let row, col, base;
|
||||||
for (row = 0; row < 24; row++) {
|
for (row = 0; row < 24; row++) {
|
||||||
base = rowToBase(row);
|
base = this.rowToBase(row);
|
||||||
line = '';
|
line = '';
|
||||||
if (this.e && _80colMode) {
|
if (this.e && _80colMode) {
|
||||||
for (col = 0; col < 80; col++) {
|
for (col = 0; col < 80; col++) {
|
||||||
@ -522,72 +549,56 @@ export class LoresPage2D implements LoresPage {
|
|||||||
|
|
||||||
export class HiresPage2D implements HiresPage {
|
export class HiresPage2D implements HiresPage {
|
||||||
public imageData: ImageData;
|
public imageData: ImageData;
|
||||||
dirty: Region = {...notDirty}
|
dirty: Region = {...notDirty};
|
||||||
|
|
||||||
private _buffer: memory[] = [];
|
private _buffer: memory[] = [];
|
||||||
private _refreshing = false;
|
private _refreshing = false;
|
||||||
|
|
||||||
private _monoMode = false;
|
private _monoMode = false;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private page: number) {
|
private page: number) {
|
||||||
this.imageData = new ImageData(560, 384);
|
this.imageData = new ImageData(560, 192);
|
||||||
for (let idx = 0; idx < 560 * 384 * 4; idx++) {
|
for (let idx = 0; idx < 560 * 192 * 4; idx++) {
|
||||||
this.imageData.data[idx] = 0xff;
|
this.imageData.data[idx] = 0xff;
|
||||||
}
|
}
|
||||||
this._buffer[0] = allocMemPages(0x20);
|
this._buffer[0] = allocMemPages(0x20);
|
||||||
this._buffer[1] = allocMemPages(0x20);
|
this._buffer[1] = allocMemPages(0x20);
|
||||||
}
|
}
|
||||||
|
|
||||||
_drawPixel(data: Uint8ClampedArray, off: number, color: Color) {
|
private _drawPixel(data: Uint8ClampedArray, off: number, color: Color) {
|
||||||
const c0 = color[0], c1 = color[1], c2 = color[2];
|
const c0 = color[0], c1 = color[1], c2 = color[2];
|
||||||
|
|
||||||
data[off + 0] = data[off + 4] = c0;
|
data[off + 0] = data[off + 4] = c0;
|
||||||
data[off + 1] = data[off + 5] = c1;
|
data[off + 1] = data[off + 5] = c1;
|
||||||
data[off + 2] = data[off + 6] = c2;
|
data[off + 2] = data[off + 6] = c2;
|
||||||
const nextOff = off + 560 * 4;
|
|
||||||
data[nextOff] = data[nextOff + 4] = c0;
|
|
||||||
data[nextOff + 1] = data[nextOff + 5] = c1;
|
|
||||||
data[nextOff + 2] = data[nextOff + 6] = c2;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
_drawHalfPixel(data: Uint8ClampedArray, off: number, color: Color) {
|
private _drawHalfPixel(data: Uint8ClampedArray, off: number, color: Color) {
|
||||||
const c0 = color[0], c1 = color[1], c2 = color[2];
|
const c0 = color[0], c1 = color[1], c2 = color[2];
|
||||||
|
|
||||||
data[off + 0] = c0;
|
data[off + 0] = c0;
|
||||||
data[off + 1] = c1;
|
data[off + 1] = c1;
|
||||||
data[off + 2] = c2;
|
data[off + 2] = c2;
|
||||||
const nextOff = off + 560 * 4;
|
|
||||||
data[nextOff] = c0;
|
|
||||||
data[nextOff + 1] = c1;
|
|
||||||
data[nextOff + 2] = c2;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
// 160x192 pixels alternate 3 and 4 base pixels wide
|
// 160x192 pixels alternate 3 and 4 base pixels wide
|
||||||
//
|
//
|
||||||
|
|
||||||
_draw3Pixel(data: Uint8ClampedArray, off: number, color: Color) {
|
private _draw3Pixel(data: Uint8ClampedArray, off: number, color: Color) {
|
||||||
const c0 = color[0], c1 = color[1], c2 = color[2];
|
const c0 = color[0], c1 = color[1], c2 = color[2];
|
||||||
|
|
||||||
data[off + 0] = data[off + 4] = data[off + 8] = c0;
|
data[off + 0] = data[off + 4] = data[off + 8] = c0;
|
||||||
data[off + 1] = data[off + 5] = data[off + 9] = c1;
|
data[off + 1] = data[off + 5] = data[off + 9] = c1;
|
||||||
data[off + 2] = data[off + 6] = data[off + 10] = c2;
|
data[off + 2] = data[off + 6] = data[off + 10] = c2;
|
||||||
const nextOff = off + 560 * 4;
|
|
||||||
data[nextOff] = data[nextOff + 4] = data[nextOff + 8] = c0;
|
|
||||||
data[nextOff + 1] = data[nextOff + 5] = data[nextOff + 9] = c1;
|
|
||||||
data[nextOff + 2] = data[nextOff + 6] = data[nextOff + 10] = c2;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
_draw4Pixel(data: Uint8ClampedArray, off: number, color: Color) {
|
private _draw4Pixel(data: Uint8ClampedArray, off: number, color: Color) {
|
||||||
const c0 = color[0], c1 = color[1], c2 = color[2];
|
const c0 = color[0], c1 = color[1], c2 = color[2];
|
||||||
|
|
||||||
data[off + 0] = data[off + 4] = data[off + 8] = data[off + 12] = c0;
|
data[off + 0] = data[off + 4] = data[off + 8] = data[off + 12] = c0;
|
||||||
data[off + 1] = data[off + 5] = data[off + 9] = data[off + 13] = c1;
|
data[off + 1] = data[off + 5] = data[off + 9] = data[off + 13] = c1;
|
||||||
data[off + 2] = data[off + 6] = data[off + 10] = data[off + 14] = c2;
|
data[off + 2] = data[off + 6] = data[off + 10] = data[off + 14] = c2;
|
||||||
const nextOff = off + 560 * 4;
|
|
||||||
data[nextOff] = data[nextOff + 4] = data[nextOff + 8] = data[nextOff + 12] = c0;
|
|
||||||
data[nextOff + 1] = data[nextOff + 5] = data[nextOff + 9] = data[nextOff + 13] = c1;
|
|
||||||
data[nextOff + 2] = data[nextOff + 6] = data[nextOff + 10] = data[nextOff + 14] = c2;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bank0(): MemoryPages {
|
bank0(): MemoryPages {
|
||||||
@ -608,16 +619,16 @@ export class HiresPage2D implements HiresPage {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
_start() { return (0x20 * this.page); }
|
private _start() { return (0x20 * this.page); }
|
||||||
|
|
||||||
_end() { return (0x020 * this.page) + 0x1f; }
|
private _end() { return (0x020 * this.page) + 0x1f; }
|
||||||
|
|
||||||
_read(page: byte, off: byte, bank: bank) {
|
private _read(page: byte, off: byte, bank: bank) {
|
||||||
const addr = (page << 8) | off, base = addr & 0x1FFF;
|
const addr = (page << 8) | off, base = addr & 0x1FFF;
|
||||||
return this._buffer[bank][base];
|
return this._buffer[bank][base];
|
||||||
}
|
}
|
||||||
|
|
||||||
_write(page: byte, off: byte, val: byte, bank: bank) {
|
private _write(page: byte, off: byte, val: byte, bank: bank) {
|
||||||
const addr = (page << 8) | off;
|
const addr = (page << 8) | off;
|
||||||
const base = addr & 0x1FFF;
|
const base = addr & 0x1FFF;
|
||||||
|
|
||||||
@ -626,8 +637,6 @@ export class HiresPage2D implements HiresPage {
|
|||||||
}
|
}
|
||||||
this._buffer[bank][base] = val;
|
this._buffer[bank][base] = val;
|
||||||
|
|
||||||
let hbs = val & 0x80;
|
|
||||||
|
|
||||||
const col = (base % 0x80) % 0x28;
|
const col = (base % 0x80) % 0x28;
|
||||||
const adj = off - col;
|
const adj = off - col;
|
||||||
|
|
||||||
@ -641,14 +650,10 @@ export class HiresPage2D implements HiresPage {
|
|||||||
|
|
||||||
const data = this.imageData.data;
|
const data = this.imageData.data;
|
||||||
let dx, dy;
|
let dx, dy;
|
||||||
if ((rowa < 24) && (col < 40)) {
|
if ((rowa < 24) && (col < 40) && hiresMode) {
|
||||||
if (!hiresMode) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
let y = rowa << 4 | rowb << 1;
|
let y = rowa << 4 | rowb << 1;
|
||||||
if (y < this.dirty.top) { this.dirty.top = y; }
|
if (y < this.dirty.top) { this.dirty.top = y; }
|
||||||
y += 2;
|
y += 1;
|
||||||
if (y > this.dirty.bottom) { this.dirty.bottom = y; }
|
if (y > this.dirty.bottom) { this.dirty.bottom = y; }
|
||||||
let x = col * 14 - 2;
|
let x = col * 14 - 2;
|
||||||
if (x < this.dirty.left) { this.dirty.left = x; }
|
if (x < this.dirty.left) { this.dirty.left = x; }
|
||||||
@ -663,10 +668,10 @@ export class HiresPage2D implements HiresPage {
|
|||||||
const c4 = val >> 4;
|
const c4 = val >> 4;
|
||||||
|
|
||||||
dx = col * 2 + (bank ^ 1);
|
dx = col * 2 + (bank ^ 1);
|
||||||
off = dx * 28 + dy * 280 * 4 * 2;
|
const offset = dx * 28 + dy * 280 * 4;
|
||||||
|
|
||||||
this._draw3Pixel(data, off, dcolors[c3]);
|
this._draw3Pixel(data, offset, dcolors[c3]);
|
||||||
this._draw4Pixel(data, off + 12, dcolors[c4]);
|
this._draw4Pixel(data, offset + 12, dcolors[c4]);
|
||||||
} else if (doubleHiresMode) {
|
} else if (doubleHiresMode) {
|
||||||
val &= 0x7f;
|
val &= 0x7f;
|
||||||
|
|
||||||
@ -716,7 +721,7 @@ export class HiresPage2D implements HiresPage {
|
|||||||
hb[8] = b4 & 0x80;
|
hb[8] = b4 & 0x80;
|
||||||
}
|
}
|
||||||
dx = mcol * 14;
|
dx = mcol * 14;
|
||||||
off = dx * 4 + dy * 280 * 4 * 2;
|
let offset = dx * 4 + dy * 280 * 4;
|
||||||
|
|
||||||
let monoColor = null;
|
let monoColor = null;
|
||||||
if (this._monoMode || monoDHRMode) {
|
if (this._monoMode || monoDHRMode) {
|
||||||
@ -724,45 +729,45 @@ export class HiresPage2D implements HiresPage {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for (let idx = 1; idx < 8; idx++) {
|
for (let idx = 1; idx < 8; idx++) {
|
||||||
hbs = hb[idx];
|
const hbs = hb[idx];
|
||||||
const dcolor = dcolors[r4[c[idx]]];
|
const dcolor = dcolors[r4[c[idx]]];
|
||||||
let bits = c[idx - 1] | (c[idx] << 4) | (c[idx + 1] << 8);
|
let bits = c[idx - 1] | (c[idx] << 4) | (c[idx + 1] << 8);
|
||||||
for (let jdx = 0; jdx < 4; jdx++, off += 4) {
|
for (let jdx = 0; jdx < 4; jdx++, offset += 4) {
|
||||||
if (monoColor) {
|
if (monoColor) {
|
||||||
if (bits & 0x10) {
|
if (bits & 0x10) {
|
||||||
this._drawHalfPixel(data, off, monoColor);
|
this._drawHalfPixel(data, offset, monoColor);
|
||||||
} else {
|
} else {
|
||||||
this._drawHalfPixel(data, off, blackCol);
|
this._drawHalfPixel(data, offset, blackCol);
|
||||||
}
|
}
|
||||||
} else if (mixedDHRMode) {
|
} else if (mixedDHRMode) {
|
||||||
if (hbs) {
|
if (hbs) {
|
||||||
this._drawHalfPixel(data, off, dcolor);
|
this._drawHalfPixel(data, offset, dcolor);
|
||||||
} else {
|
} else {
|
||||||
if (bits & 0x10) {
|
if (bits & 0x10) {
|
||||||
this._drawHalfPixel(data, off, whiteCol);
|
this._drawHalfPixel(data, offset, whiteCol);
|
||||||
} else {
|
} else {
|
||||||
this._drawHalfPixel(data, off, blackCol);
|
this._drawHalfPixel(data, offset, blackCol);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (colorDHRMode) {
|
} else if (colorDHRMode) {
|
||||||
this._drawHalfPixel(data, off, dcolor);
|
this._drawHalfPixel(data, offset, dcolor);
|
||||||
} else if (
|
} else if (
|
||||||
((c[idx] != c[idx - 1]) && (c[idx] != c[idx + 1])) &&
|
((c[idx] != c[idx - 1]) && (c[idx] != c[idx + 1])) &&
|
||||||
(((bits & 0x1c) == 0x1c) ||
|
(((bits & 0x1c) == 0x1c) ||
|
||||||
((bits & 0x70) == 0x70) ||
|
((bits & 0x70) == 0x70) ||
|
||||||
((bits & 0x38) == 0x38))
|
((bits & 0x38) == 0x38))
|
||||||
) {
|
) {
|
||||||
this._drawHalfPixel(data, off, whiteCol);
|
this._drawHalfPixel(data, offset, whiteCol);
|
||||||
} else if (
|
} else if (
|
||||||
(bits & 0x38) ||
|
(bits & 0x38) ||
|
||||||
(c[idx] == c[idx + 1]) ||
|
(c[idx] == c[idx + 1]) ||
|
||||||
(c[idx] == c[idx - 1])
|
(c[idx] == c[idx - 1])
|
||||||
) {
|
) {
|
||||||
this._drawHalfPixel(data, off, dcolor);
|
this._drawHalfPixel(data, offset, dcolor);
|
||||||
} else if (bits & 0x28) {
|
} else if (bits & 0x28) {
|
||||||
this._drawHalfPixel(data, off, dim(dcolor));
|
this._drawHalfPixel(data, offset, dim(dcolor));
|
||||||
} else {
|
} else {
|
||||||
this._drawHalfPixel(data, off, blackCol);
|
this._drawHalfPixel(data, offset, blackCol);
|
||||||
}
|
}
|
||||||
bits >>= 1;
|
bits >>= 1;
|
||||||
}
|
}
|
||||||
@ -779,7 +784,7 @@ export class HiresPage2D implements HiresPage {
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
val = this._buffer[0][base];
|
val = this._buffer[0][base];
|
||||||
hbs = val & 0x80;
|
const hbs = val & 0x80;
|
||||||
val &= 0x7f;
|
val &= 0x7f;
|
||||||
dx = col * 14 - 2;
|
dx = col * 14 - 2;
|
||||||
b0 = col > 0 ? this._buffer[0][base - 1] : 0;
|
b0 = col > 0 ? this._buffer[0][base - 1] : 0;
|
||||||
@ -791,11 +796,11 @@ export class HiresPage2D implements HiresPage {
|
|||||||
const oddCol = (hbs ? orangeCol : greenCol);
|
const oddCol = (hbs ? orangeCol : greenCol);
|
||||||
const evenCol = (hbs ? blueCol : violetCol);
|
const evenCol = (hbs ? blueCol : violetCol);
|
||||||
|
|
||||||
off = dx * 4 + dy * 280 * 4 * 2;
|
let offset = dx * 4 + dy * 280 * 4;
|
||||||
|
|
||||||
const monoColor = this._monoMode ? whiteCol : null;
|
const monoColor = this._monoMode ? whiteCol : null;
|
||||||
|
|
||||||
for (let idx = 0; idx < 9; idx++, off += 8) {
|
for (let idx = 0; idx < 9; idx++, offset += 8) {
|
||||||
val >>= 1;
|
val >>= 1;
|
||||||
|
|
||||||
if (v1) {
|
if (v1) {
|
||||||
@ -823,7 +828,7 @@ export class HiresPage2D implements HiresPage {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (dx > -1 && dx < 560) {
|
if (dx > -1 && dx < 560) {
|
||||||
this._drawPixel(data, off, color);
|
this._drawPixel(data, offset, color);
|
||||||
}
|
}
|
||||||
dx += 2;
|
dx += 2;
|
||||||
|
|
||||||
@ -852,7 +857,6 @@ export class HiresPage2D implements HiresPage {
|
|||||||
|
|
||||||
mono(on: boolean) {
|
mono(on: boolean) {
|
||||||
this._monoMode = on;
|
this._monoMode = on;
|
||||||
this.refresh();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
start() {
|
start() {
|
||||||
@ -1069,21 +1073,18 @@ export class VideoModes2D implements VideoModes {
|
|||||||
if (!this._context) {
|
if (!this._context) {
|
||||||
throw new Error('No 2D context');
|
throw new Error('No 2D context');
|
||||||
}
|
}
|
||||||
|
let blitted = false;
|
||||||
|
|
||||||
if (mainDirty.bottom !== -1 || (mixDirty && mixDirty.bottom !== -1)) {
|
if (mainDirty.bottom !== -1 || (mixDirty && mixDirty.bottom !== -1)) {
|
||||||
if (mixData) {
|
const imageData = buildScreen(mainData, mixData);
|
||||||
this._context.putImageData(
|
this._context.drawImage(
|
||||||
mainData, this._left, this._top, 0, 0, 560, 320
|
imageData,
|
||||||
);
|
0, 0, 560, 192,
|
||||||
this._context.putImageData(
|
this._left, this._top, 560, 384
|
||||||
mixData, this._left, this._top, 0, 320, 560, 64
|
);
|
||||||
);
|
blitted = true;
|
||||||
} else {
|
|
||||||
this._context.putImageData(mainData, this._top, this._left);
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
} else {
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
return blitted;
|
||||||
}
|
}
|
||||||
|
|
||||||
blit(altData?: ImageData) {
|
blit(altData?: ImageData) {
|
||||||
@ -1094,7 +1095,7 @@ export class VideoModes2D implements VideoModes {
|
|||||||
if (altData) {
|
if (altData) {
|
||||||
blitted = this.updateImage(
|
blitted = this.updateImage(
|
||||||
altData,
|
altData,
|
||||||
{top: 0, left: 0, right: 560, bottom: 384}
|
{ top: 0, left: 0, right: 560, bottom: 192 }
|
||||||
);
|
);
|
||||||
} else if (hiresMode && !textMode) {
|
} else if (hiresMode && !textMode) {
|
||||||
blitted = this.updateImage(
|
blitted = this.updateImage(
|
||||||
@ -1110,10 +1111,11 @@ export class VideoModes2D implements VideoModes {
|
|||||||
}
|
}
|
||||||
hgr.dirty = {...notDirty};
|
hgr.dirty = {...notDirty};
|
||||||
gr.dirty = {...notDirty};
|
gr.dirty = {...notDirty};
|
||||||
|
|
||||||
return blitted;
|
return blitted;
|
||||||
}
|
}
|
||||||
|
|
||||||
getState() {
|
getState(): VideoModesState {
|
||||||
return {
|
return {
|
||||||
grs: [this._grs[0].getState(), this._grs[1].getState()],
|
grs: [this._grs[0].getState(), this._grs[1].getState()],
|
||||||
hgrs: [this._hgrs[0].getState(), this._hgrs[1].getState()],
|
hgrs: [this._hgrs[0].getState(), this._hgrs[1].getState()],
|
||||||
@ -1124,7 +1126,7 @@ export class VideoModes2D implements VideoModes {
|
|||||||
_80colMode: _80colMode,
|
_80colMode: _80colMode,
|
||||||
altCharMode: altCharMode,
|
altCharMode: altCharMode,
|
||||||
an3: an3
|
an3: an3
|
||||||
} as VideoModesState;
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
setState(state: VideoModesState) {
|
setState(state: VideoModesState) {
|
||||||
@ -1140,6 +1142,7 @@ export class VideoModes2D implements VideoModes {
|
|||||||
this._grs[1].setState(state.grs[1]);
|
this._grs[1].setState(state.grs[1]);
|
||||||
this._hgrs[0].setState(state.hgrs[0]);
|
this._hgrs[0].setState(state.hgrs[0]);
|
||||||
this._hgrs[1].setState(state.hgrs[1]);
|
this._hgrs[1].setState(state.hgrs[1]);
|
||||||
|
this._refresh();
|
||||||
}
|
}
|
||||||
|
|
||||||
mono(on: boolean) {
|
mono(on: boolean) {
|
||||||
@ -1147,6 +1150,7 @@ export class VideoModes2D implements VideoModes {
|
|||||||
this._grs[1].mono(on);
|
this._grs[1].mono(on);
|
||||||
this._hgrs[0].mono(on);
|
this._hgrs[0].mono(on);
|
||||||
this._hgrs[1].mono(on);
|
this._hgrs[1].mono(on);
|
||||||
|
this._refresh();
|
||||||
}
|
}
|
||||||
|
|
||||||
getText() {
|
getText() {
|
||||||
|
213
js/gl.ts
213
js/gl.ts
@ -40,7 +40,7 @@ const tmpContext = tmpCanvas.getContext('2d');
|
|||||||
|
|
||||||
const buildScreen = (mainData: ImageData, mixData?: ImageData | null) => {
|
const buildScreen = (mainData: ImageData, mixData?: ImageData | null) => {
|
||||||
if (!tmpContext) {
|
if (!tmpContext) {
|
||||||
throw new Error('No webgl context');
|
throw new Error('No 2d context');
|
||||||
}
|
}
|
||||||
|
|
||||||
const details = screenEmu.C.NTSC_DETAILS;
|
const details = screenEmu.C.NTSC_DETAILS;
|
||||||
@ -66,7 +66,7 @@ const whiteCol: Color = [255, 255, 255];
|
|||||||
const blackCol: Color = [0, 0, 0];
|
const blackCol: Color = [0, 0, 0];
|
||||||
|
|
||||||
const notDirty: Region = {
|
const notDirty: Region = {
|
||||||
top: 385,
|
top: 193,
|
||||||
bottom: -1,
|
bottom: -1,
|
||||||
left: 561,
|
left: 561,
|
||||||
right: -1
|
right: -1
|
||||||
@ -84,8 +84,8 @@ export class LoresPageGL implements LoresPage {
|
|||||||
// $80-$FF normal
|
// $80-$FF normal
|
||||||
|
|
||||||
private _buffer: memory[] = [];
|
private _buffer: memory[] = [];
|
||||||
private _monoMode = false;
|
|
||||||
private _refreshing = false;
|
private _refreshing = false;
|
||||||
|
private _monoMode = false;
|
||||||
private _blink = false;
|
private _blink = false;
|
||||||
|
|
||||||
dirty: Region = {...notDirty}
|
dirty: Region = {...notDirty}
|
||||||
@ -102,20 +102,32 @@ export class LoresPageGL implements LoresPage {
|
|||||||
this._buffer[1] = allocMemPages(0x4);
|
this._buffer[1] = allocMemPages(0x4);
|
||||||
}
|
}
|
||||||
|
|
||||||
_drawPixel(data: Uint8ClampedArray, off: number, color: Color) {
|
private _drawPixel(data: Uint8ClampedArray, off: number, color: Color) {
|
||||||
const c0 = color[0], c1 = color[1], c2 = color[2];
|
const c0 = color[0], c1 = color[1], c2 = color[2];
|
||||||
data[off + 0] = data[off + 4] = c0;
|
data[off + 0] = data[off + 4] = c0;
|
||||||
data[off + 1] = data[off + 5] = c1;
|
data[off + 1] = data[off + 5] = c1;
|
||||||
data[off + 2] = data[off + 6] = c2;
|
data[off + 2] = data[off + 6] = c2;
|
||||||
}
|
}
|
||||||
|
|
||||||
_drawHalfPixel(data: Uint8ClampedArray, off: number, color: Color) {
|
private _drawHalfPixel(data: Uint8ClampedArray, off: number, color: Color) {
|
||||||
const c0 = color[0], c1 = color[1], c2 = color[2];
|
const c0 = color[0], c1 = color[1], c2 = color[2];
|
||||||
data[off + 0] = c0;
|
data[off + 0] = c0;
|
||||||
data[off + 1] = c1;
|
data[off + 1] = c1;
|
||||||
data[off + 2] = c2;
|
data[off + 2] = c2;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private _checkInverse(val: byte) {
|
||||||
|
let inverse = false;
|
||||||
|
if (this.e) {
|
||||||
|
if (!_80colMode && !altCharMode) {
|
||||||
|
inverse = ((val & 0xc0) == 0x40) && this._blink;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
inverse = !((val & 0x80) || (val & 0x40) && this._blink);
|
||||||
|
}
|
||||||
|
return inverse;
|
||||||
|
}
|
||||||
|
|
||||||
bank0(): MemoryPages {
|
bank0(): MemoryPages {
|
||||||
return {
|
return {
|
||||||
start: () => this._start(),
|
start: () => this._start(),
|
||||||
@ -157,10 +169,6 @@ export class LoresPageGL implements LoresPage {
|
|||||||
}
|
}
|
||||||
this._buffer[bank][base] = val;
|
this._buffer[bank][base] = val;
|
||||||
|
|
||||||
if (!_80colMode && bank === 1) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const col = (base % 0x80) % 0x28;
|
const col = (base % 0x80) % 0x28;
|
||||||
const adj = off - col;
|
const adj = off - col;
|
||||||
|
|
||||||
@ -182,19 +190,12 @@ export class LoresPageGL implements LoresPage {
|
|||||||
if (x > this.dirty.right) { this.dirty.right = x; }
|
if (x > this.dirty.right) { this.dirty.right = x; }
|
||||||
|
|
||||||
if (textMode || hiresMode || (mixedMode && row > 19)) {
|
if (textMode || hiresMode || (mixedMode && row > 19)) {
|
||||||
let inverse;
|
|
||||||
if (this.e) {
|
|
||||||
if (!_80colMode && !altCharMode) {
|
|
||||||
inverse = ((val & 0xc0) == 0x40) && this._blink;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
inverse = !((val & 0x80) || (val & 0x40) && this._blink);
|
|
||||||
}
|
|
||||||
|
|
||||||
fore = inverse ? blackCol : whiteCol;
|
|
||||||
back = inverse ? whiteCol : blackCol;
|
|
||||||
|
|
||||||
if (_80colMode) {
|
if (_80colMode) {
|
||||||
|
const inverse = this._checkInverse(val);
|
||||||
|
|
||||||
|
fore = inverse ? blackCol : whiteCol;
|
||||||
|
back = inverse ? whiteCol : blackCol;
|
||||||
|
|
||||||
if (!enhanced) {
|
if (!enhanced) {
|
||||||
val = (val >= 0x40 && val < 0x60) ? val - 0x40 : val;
|
val = (val >= 0x40 && val < 0x60) ? val - 0x40 : val;
|
||||||
} else if (!altCharMode) {
|
} else if (!altCharMode) {
|
||||||
@ -213,9 +214,14 @@ export class LoresPageGL implements LoresPage {
|
|||||||
}
|
}
|
||||||
offset += 553 * 4;
|
offset += 553 * 4;
|
||||||
}
|
}
|
||||||
} else {
|
} else if (bank === 0) {
|
||||||
val = this._buffer[0][base];
|
val = this._buffer[0][base];
|
||||||
|
|
||||||
|
const inverse = this._checkInverse(val);
|
||||||
|
|
||||||
|
fore = inverse ? blackCol : whiteCol;
|
||||||
|
back = inverse ? whiteCol : blackCol;
|
||||||
|
|
||||||
if (!enhanced) {
|
if (!enhanced) {
|
||||||
val = (val >= 0x40 && val < 0x60) ? val - 0x40 : val;
|
val = (val >= 0x40 && val < 0x60) ? val - 0x40 : val;
|
||||||
} else if (!altCharMode) {
|
} else if (!altCharMode) {
|
||||||
@ -267,7 +273,7 @@ export class LoresPageGL implements LoresPage {
|
|||||||
}
|
}
|
||||||
offset += 553 * 4;
|
offset += 553 * 4;
|
||||||
}
|
}
|
||||||
} else {
|
} else if (bank === 0) {
|
||||||
let offset = (col * 14 + row * 560 * 8) * 4;
|
let offset = (col * 14 + row * 560 * 8) * 4;
|
||||||
for (let jdx = 0; jdx < 8; jdx++) {
|
for (let jdx = 0; jdx < 8; jdx++) {
|
||||||
let b = (jdx < 4) ? (val & 0x0f) : (val >> 4);
|
let b = (jdx < 4) ? (val & 0x0f) : (val >> 4);
|
||||||
@ -348,6 +354,7 @@ export class LoresPageGL implements LoresPage {
|
|||||||
|
|
||||||
setState(state: GraphicsState) {
|
setState(state: GraphicsState) {
|
||||||
this.page = state.page;
|
this.page = state.page;
|
||||||
|
this._monoMode = state.mono;
|
||||||
this._buffer[0] = new Uint8Array(state.buffer[0]);
|
this._buffer[0] = new Uint8Array(state.buffer[0]);
|
||||||
this._buffer[1] = new Uint8Array(state.buffer[1]);
|
this._buffer[1] = new Uint8Array(state.buffer[1]);
|
||||||
|
|
||||||
@ -403,19 +410,13 @@ export class LoresPageGL implements LoresPage {
|
|||||||
***************************************************************************/
|
***************************************************************************/
|
||||||
|
|
||||||
export class HiresPageGL implements HiresPage {
|
export class HiresPageGL implements HiresPage {
|
||||||
|
public imageData: ImageData;
|
||||||
|
dirty: Region = {...notDirty};
|
||||||
|
|
||||||
private _buffer: memory[] = [];
|
private _buffer: memory[] = [];
|
||||||
private _refreshing = false;
|
private _refreshing = false;
|
||||||
private _monoMode = false;
|
private _monoMode = false;
|
||||||
|
|
||||||
dirty: Region = {
|
|
||||||
top: 193,
|
|
||||||
bottom: -1,
|
|
||||||
left: 561,
|
|
||||||
right: -1
|
|
||||||
};
|
|
||||||
imageData: ImageData;
|
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private page: number) {
|
private page: number) {
|
||||||
this.imageData = new ImageData(560, 192);
|
this.imageData = new ImageData(560, 192);
|
||||||
@ -426,7 +427,7 @@ export class HiresPageGL implements HiresPage {
|
|||||||
this._buffer[1] = allocMemPages(0x20);
|
this._buffer[1] = allocMemPages(0x20);
|
||||||
}
|
}
|
||||||
|
|
||||||
_drawPixel(data: Uint8ClampedArray, off: number, color: Color) {
|
private _drawPixel(data: Uint8ClampedArray, off: number, color: Color) {
|
||||||
const c0 = color[0], c1 = color[1], c2 = color[2];
|
const c0 = color[0], c1 = color[1], c2 = color[2];
|
||||||
|
|
||||||
data[off + 0] = data[off + 4] = c0;
|
data[off + 0] = data[off + 4] = c0;
|
||||||
@ -434,7 +435,7 @@ export class HiresPageGL implements HiresPage {
|
|||||||
data[off + 2] = data[off + 6] = c2;
|
data[off + 2] = data[off + 6] = c2;
|
||||||
}
|
}
|
||||||
|
|
||||||
_drawHalfPixel(data: Uint8ClampedArray, off: number, color: Color) {
|
private _drawHalfPixel(data: Uint8ClampedArray, off: number, color: Color) {
|
||||||
const c0 = color[0], c1 = color[1], c2 = color[2];
|
const c0 = color[0], c1 = color[1], c2 = color[2];
|
||||||
|
|
||||||
data[off + 0] = c0;
|
data[off + 0] = c0;
|
||||||
@ -460,16 +461,16 @@ export class HiresPageGL implements HiresPage {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
_start() { return (0x20 * this.page); }
|
private _start() { return (0x20 * this.page); }
|
||||||
|
|
||||||
_end() { return (0x020 * this.page) + 0x1f; }
|
private _end() { return (0x020 * this.page) + 0x1f; }
|
||||||
|
|
||||||
_read(page: byte, off: byte, bank: bank) {
|
private _read(page: byte, off: byte, bank: bank) {
|
||||||
const addr = (page << 8) | off, base = addr & 0x1FFF;
|
const addr = (page << 8) | off, base = addr & 0x1FFF;
|
||||||
return this._buffer[bank][base];
|
return this._buffer[bank][base];
|
||||||
}
|
}
|
||||||
|
|
||||||
_write(page: byte, off: byte, val: byte, bank: bank) {
|
private _write(page: byte, off: byte, val: byte, bank: bank) {
|
||||||
const addr = (page << 8) | off;
|
const addr = (page << 8) | off;
|
||||||
const base = addr & 0x1FFF;
|
const base = addr & 0x1FFF;
|
||||||
|
|
||||||
@ -478,8 +479,6 @@ export class HiresPageGL implements HiresPage {
|
|||||||
}
|
}
|
||||||
this._buffer[bank][base] = val;
|
this._buffer[bank][base] = val;
|
||||||
|
|
||||||
// let hbs = val & 0x80;
|
|
||||||
|
|
||||||
const col = (base % 0x80) % 0x28;
|
const col = (base % 0x80) % 0x28;
|
||||||
const adj = off - col;
|
const adj = off - col;
|
||||||
|
|
||||||
@ -492,116 +491,51 @@ export class HiresPageGL implements HiresPage {
|
|||||||
rowb = base >> 10;
|
rowb = base >> 10;
|
||||||
|
|
||||||
const data = this.imageData.data;
|
const data = this.imageData.data;
|
||||||
let dx, dy;
|
if ((rowa < 24) && (col < 40) && hiresMode) {
|
||||||
if ((rowa < 24) && (col < 40)) {
|
|
||||||
if (!hiresMode) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
let y = rowa << 3 | rowb;
|
let y = rowa << 3 | rowb;
|
||||||
if (y < this.dirty.top) { this.dirty.top = y; }
|
if (y < this.dirty.top) { this.dirty.top = y; }
|
||||||
y += 1;
|
y += 1;
|
||||||
if (y > this.dirty.bottom) { this.dirty.bottom = y; }
|
if (y > this.dirty.bottom) { this.dirty.bottom = y; }
|
||||||
let x = col * 14 - 2;
|
let x = col * 14 - 2;
|
||||||
if (x < this.dirty.left) { this.dirty.left = x; }
|
if (x < this.dirty.left) { this.dirty.left = x; }
|
||||||
x += 18;
|
x += 14;
|
||||||
if (x > this.dirty.right) { this.dirty.right = x; }
|
if (x > this.dirty.right) { this.dirty.right = x; }
|
||||||
|
|
||||||
dy = rowa << 3 | rowb;
|
const dy = rowa << 3 | rowb;
|
||||||
let bz, b0, b1, b2, b3, b4, c, hb;
|
|
||||||
if (doubleHiresMode) {
|
if (doubleHiresMode) {
|
||||||
val &= 0x7f;
|
const dx = col * 14 + (bank ? 0 : 7);
|
||||||
|
|
||||||
// Every 4 bytes is 7 pixels
|
|
||||||
// 2 bytes per bank
|
|
||||||
|
|
||||||
// b0 b1 b2 b3
|
|
||||||
// c0 c1 c2 c3 c4 c5 c6
|
|
||||||
// 76543210 76543210 76543210 76543210
|
|
||||||
// 1111222 2333344 4455556 6667777
|
|
||||||
|
|
||||||
const mod = col % 2, mcol = col - mod, baseOff = base - mod;
|
|
||||||
bz = this._buffer[0][baseOff - 1];
|
|
||||||
b0 = this._buffer[1][baseOff];
|
|
||||||
b1 = this._buffer[0][baseOff];
|
|
||||||
b2 = this._buffer[1][baseOff + 1];
|
|
||||||
b3 = this._buffer[0][baseOff + 1];
|
|
||||||
b4 = this._buffer[1][baseOff + 2];
|
|
||||||
c = [
|
|
||||||
0,
|
|
||||||
((b0 & 0x0f) >> 0), // 0
|
|
||||||
((b0 & 0x70) >> 4) | ((b1 & 0x01) << 3), // 1
|
|
||||||
((b1 & 0x1e) >> 1), // 2
|
|
||||||
((b1 & 0x60) >> 5) | ((b2 & 0x03) << 2), // 3
|
|
||||||
((b2 & 0x3c) >> 2), // 4
|
|
||||||
((b2 & 0x40) >> 6) | ((b3 & 0x07) << 1), // 5
|
|
||||||
((b3 & 0x78) >> 3), // 6
|
|
||||||
0
|
|
||||||
], // 7
|
|
||||||
hb = [
|
|
||||||
0,
|
|
||||||
b0 & 0x80, // 0
|
|
||||||
b0 & 0x80, // 1
|
|
||||||
b1 & 0x80, // 2
|
|
||||||
b2 & 0x80, // 3
|
|
||||||
b2 & 0x80, // 4
|
|
||||||
b3 & 0x80, // 5
|
|
||||||
b3 & 0x80, // 6
|
|
||||||
0
|
|
||||||
]; // 7
|
|
||||||
if (col > 0) {
|
|
||||||
c[0] = (bz & 0x78) >> 3;
|
|
||||||
hb[0] = bz & 0x80;
|
|
||||||
}
|
|
||||||
if (col < 39) {
|
|
||||||
c[8] = b4 & 0x0f;
|
|
||||||
hb[8] = b4 & 0x80;
|
|
||||||
}
|
|
||||||
dx = mcol * 14;
|
|
||||||
let offset = dx * 4 + dy * 280 * 4 * 2;
|
let offset = dx * 4 + dy * 280 * 4 * 2;
|
||||||
|
|
||||||
for (let idx = 1; idx < 8; idx++) {
|
let bits = val;
|
||||||
// hbs = hb[idx];
|
for (let jdx = 0; jdx < 7; jdx++, offset += 4) {
|
||||||
let bits = c[idx - 1] | (c[idx] << 4) | (c[idx + 1] << 8);
|
if (bits & 0x01) {
|
||||||
for (let jdx = 0; jdx < 4; jdx++, offset += 4) {
|
this._drawHalfPixel(data, offset, whiteCol);
|
||||||
if (bits & 0x10) {
|
|
||||||
this._drawHalfPixel(data, offset, whiteCol);
|
|
||||||
} else {
|
|
||||||
this._drawHalfPixel(data, offset, blackCol);
|
|
||||||
}
|
|
||||||
bits >>= 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
val = this._buffer[0][base];
|
|
||||||
const hbs = val & 0x80;
|
|
||||||
val &= 0x7f;
|
|
||||||
dx = col * 14 - 2;
|
|
||||||
b0 = col > 0 ? this._buffer[0][base - 1] : 0;
|
|
||||||
b2 = col < 39 ? this._buffer[0][base + 1] : 0;
|
|
||||||
val |= (b2 & 0x3) << 7;
|
|
||||||
let v1 = b0 & 0x40,
|
|
||||||
v2 = val & 0x1,
|
|
||||||
color;
|
|
||||||
|
|
||||||
let offset = dx * 4 + dy * 560 * 4 + (hbs ? 4 : 0);
|
|
||||||
|
|
||||||
for (let idx = 0; idx < 9; idx++, offset += 8) {
|
|
||||||
val >>= 1;
|
|
||||||
|
|
||||||
if (v1) {
|
|
||||||
color = whiteCol;
|
|
||||||
} else {
|
} else {
|
||||||
color = blackCol;
|
this._drawHalfPixel(data, offset, blackCol);
|
||||||
}
|
}
|
||||||
|
bits >>= 1;
|
||||||
if (dx > -1 && dx < 560) {
|
}
|
||||||
this._drawPixel(data, offset, color);
|
} else if (bank === 0) {
|
||||||
|
const hbs = val & 0x80;
|
||||||
|
const dx = col * 14;
|
||||||
|
let offset = dx * 4 + dy * 560 * 4;
|
||||||
|
if (hbs) {
|
||||||
|
const val0 = this._buffer[bank][base - 1] || 0;
|
||||||
|
if (val0 & 0x40) {
|
||||||
|
this._drawHalfPixel(data, offset, whiteCol);
|
||||||
|
} else {
|
||||||
|
this._drawHalfPixel(data, offset, blackCol);
|
||||||
}
|
}
|
||||||
dx += 2;
|
offset += 4;
|
||||||
|
}
|
||||||
v1 = v2;
|
let bits = val;
|
||||||
v2 = val & 0x01;
|
for (let idx = 0; idx < 7; idx++, offset += 8) {
|
||||||
|
if (bits & 0x01) {
|
||||||
|
this._drawPixel(data, offset, whiteCol);
|
||||||
|
} else {
|
||||||
|
this._drawPixel(data, offset, blackCol);
|
||||||
|
}
|
||||||
|
bits >>= 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -654,6 +588,7 @@ export class HiresPageGL implements HiresPage {
|
|||||||
|
|
||||||
setState(state: GraphicsState) {
|
setState(state: GraphicsState) {
|
||||||
this.page = state.page;
|
this.page = state.page;
|
||||||
|
this._monoMode = state.mono;
|
||||||
this._buffer[0] = new Uint8Array(state.buffer[0]);
|
this._buffer[0] = new Uint8Array(state.buffer[0]);
|
||||||
this._buffer[1] = new Uint8Array(state.buffer[1]);
|
this._buffer[1] = new Uint8Array(state.buffer[1]);
|
||||||
|
|
||||||
@ -885,12 +820,14 @@ export class VideoModesGL implements VideoModes {
|
|||||||
if (altData) {
|
if (altData) {
|
||||||
blitted = this.updateImage(
|
blitted = this.updateImage(
|
||||||
altData,
|
altData,
|
||||||
{ top: 0, left: 0, right: 560, bottom: 384 }
|
{ top: 0, left: 0, right: 560, bottom: 192 }
|
||||||
);
|
);
|
||||||
} else if (hiresMode && !textMode) {
|
} else if (hiresMode && !textMode) {
|
||||||
blitted = this.updateImage(
|
blitted = this.updateImage(
|
||||||
hgr.imageData, hgr.dirty,
|
hgr.imageData,
|
||||||
mixedMode ? gr.imageData : null, mixedMode ? gr.dirty : null,
|
hgr.dirty,
|
||||||
|
mixedMode ? gr.imageData : null,
|
||||||
|
mixedMode ? gr.dirty : null
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
blitted = this.updateImage(
|
blitted = this.updateImage(
|
||||||
|
21
js/mmu.ts
21
js/mmu.ts
@ -9,7 +9,6 @@
|
|||||||
* implied warranty.
|
* implied warranty.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { MemoryPages } from './types';
|
|
||||||
import CPU6502 from './cpu6502';
|
import CPU6502 from './cpu6502';
|
||||||
import RAM, { RAMState } from './ram';
|
import RAM, { RAMState } from './ram';
|
||||||
import ROM, { ROMState } from './roms/rom';
|
import ROM, { ROMState } from './roms/rom';
|
||||||
@ -95,17 +94,9 @@ const LOC = {
|
|||||||
READWRBSR1: 0x8b,
|
READWRBSR1: 0x8b,
|
||||||
};
|
};
|
||||||
|
|
||||||
class Switches implements MemoryPages {
|
class Switches implements Memory {
|
||||||
constructor(private mmu: MMU) {}
|
constructor(private mmu: MMU) {}
|
||||||
|
|
||||||
start() {
|
|
||||||
return 0xC0;
|
|
||||||
}
|
|
||||||
|
|
||||||
end() {
|
|
||||||
return 0xC0;
|
|
||||||
}
|
|
||||||
|
|
||||||
read(_page: byte, off: byte) {
|
read(_page: byte, off: byte) {
|
||||||
return this.mmu._access(off) || 0;
|
return this.mmu._access(off) || 0;
|
||||||
}
|
}
|
||||||
@ -115,19 +106,11 @@ class Switches implements MemoryPages {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class AuxRom implements MemoryPages {
|
class AuxRom implements Memory {
|
||||||
constructor(
|
constructor(
|
||||||
private readonly mmu: MMU,
|
private readonly mmu: MMU,
|
||||||
private readonly rom: ROM) { }
|
private readonly rom: ROM) { }
|
||||||
|
|
||||||
start() {
|
|
||||||
return 0xc1;
|
|
||||||
}
|
|
||||||
|
|
||||||
end() {
|
|
||||||
return 0xcf;
|
|
||||||
}
|
|
||||||
|
|
||||||
read(page: byte, off: byte) {
|
read(page: byte, off: byte) {
|
||||||
if (page == 0xc3) {
|
if (page == 0xc3) {
|
||||||
this.mmu._setIntc8rom(true);
|
this.mmu._setIntc8rom(true);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user