From 86c395ccdac2be3302ca098eca849f161506aa4f Mon Sep 17 00:00:00 2001 From: Will Scullin Date: Sat, 7 Dec 2013 11:20:22 -0800 Subject: [PATCH] Joystick and graphics fixes Better greenscreen support, joystick improvements and fixes. --- apple2js.html | 16 ++++-- js/apple2io.js | 14 +++-- js/canvas2.js | 98 +++++++++++++++++++++----------- js/canvas2e.js | 150 ++++++++++++++++++++++++++++++++----------------- 4 files changed, 183 insertions(+), 95 deletions(-) diff --git a/apple2js.html b/apple2js.html index 666b83e..1d5ca66 100644 --- a/apple2js.html +++ b/apple2js.html @@ -15,7 +15,7 @@ Apple ][js - An Apple 2 Emulator in JavaScript - + @@ -778,8 +778,10 @@ function _mousemove(evt) { if (gamepad) { return; } - var x = evt.offsetX / $(this).width(), - y = evt.offsetY / $(this).height(), + var s = $("#screen"); + var offset = s.offset(); + var x = (evt.pageX - offset.left) / s.width(), + y = (evt.pageY - offset.top) / s.height(), z = x; if (swapXY) { @@ -806,7 +808,13 @@ function pauseRun(b) { $(function() { hashtag = document.location.hash; - $("button,input[type=button],a.button").button(); + $("button,input[type=button],a.button").button().focus(function() { + // Crazy hack required by Chrome + var self = this; + window.setTimeout(function() { + self.blur(); + }, 1) + }); var canvas = document.getElementById("screen"); var context = canvas.getContext('2d'); diff --git a/js/apple2io.js b/js/apple2io.js index 507a1d4..fe4a470 100644 --- a/js/apple2io.js +++ b/js/apple2io.js @@ -205,7 +205,11 @@ function Apple2IO(cpu, callbacks) case LOC.STROBE: _key &= 0x7f; if (_buffer.length > 0) { - _key = _buffer.shift().charCodeAt(0) | 0x80; + var val = _buffer.shift(); + if (val == '\n') { + val = '\r'; + } + _key = val.charCodeAt(0) | 0x80; } result = _keyDown ? 0x80 : 0x00; break; @@ -222,16 +226,16 @@ function Apple2IO(cpu, callbacks) result = _button[2] ? 0x80 : 0; break; case LOC.PADDLE0: - result = (delta < (_paddle[0] * 2560) ? 0x80 : 0x00); + result = (delta < (_paddle[0] * 2756) ? 0x80 : 0x00); break; case LOC.PADDLE1: - result = (delta < (_paddle[1] * 2560) ? 0x80 : 0x00); + result = (delta < (_paddle[1] * 2756) ? 0x80 : 0x00); break; case LOC.PADDLE2: - result = (delta < (_paddle[2] * 2560) ? 0x80 : 0x00); + result = (delta < (_paddle[2] * 2756) ? 0x80 : 0x00); break; case LOC.PADDLE3: - result = (delta < (_paddle[3] * 2560) ? 0x80 : 0x00); + result = (delta < (_paddle[3] * 2756) ? 0x80 : 0x00); break; case LOC.PDLTRIG: _trigger = cpu.cycles(); diff --git a/js/canvas2.js b/js/canvas2.js index 6ac8ed7..eb9f695 100644 --- a/js/canvas2.js +++ b/js/canvas2.js @@ -10,6 +10,7 @@ * implied warranty. */ +/*jshint browser:true */ /*globals allocMemPages: false, charset: false, base64_encode: false, base64_decode: false */ @@ -40,7 +41,7 @@ function LoresPage(page) var _black = [0x00,0x00,0x00]; var _white = [0xff,0xff,0xff]; - var _green = [0x00,0xff,0x00]; + var _green = [0x00,0xff,0x88]; var _colors = [ [0x00,0x00,0x00], // 0 Black 0000 0 0 @@ -56,7 +57,7 @@ function LoresPage(page) [0x40,0x40,0x40], // 10 Gray 2 1010 10 [0xff,0x96,0xbf], // 11 Pink 1011 11 [0x2f,0xbc,0x1a], // 12 Light Green 0110 6 - [0xb9,0xd0,0x60], // 13 Yellow 0111 7 + [0xbf,0xd3,0x5a], // 13 Yellow 0111 7 [0x6f,0xe8,0xbf], // 14 Aqua 1110 14 [0xff,0xff,0xff] // 15 White 1111 15 @@ -127,7 +128,7 @@ function LoresPage(page) return; var data = pages[_page].data, fore, back; - off = (col * 7 + row * 280 * 8) * 4; + off = (col * 14 + row * 560 * 8) * 4; if (textMode || (mixedMode && row > 19)) { if (val & 0x80 || ((val & 0x40) && _blink)) { @@ -142,24 +143,47 @@ function LoresPage(page) b <<= 1; for (idx = 0; idx < 7; idx++) { color = (b & 0x80) ? fore : back; - data[off + 0] = color[0]; - data[off + 1] = color[1]; - data[off + 2] = color[2]; + data[off + 0] = data[off + 4] = color[0]; + data[off + 1] = data[off + 5] = color[1]; + data[off + 2] = data[off + 6] = color[2]; b <<= 1; - off += 4; + off += 8; } - off += 273 * 4; + off += 546 * 4; } } else { - for (jdx = 0; jdx < 8; jdx++) { - color = _colors[(jdx < 4) ? (val & 0x0f) : (val >> 4)]; - for (idx = 0; idx < 7; idx++) { - data[off + 0] = color[0]; - data[off + 1] = color[1]; - data[off + 2] = color[2]; - off += 4; + if (_greenMode) { + fore = _green; + back = _black; + for (jdx = 0; jdx < 8; jdx++) { + b = (jdx < 4) ? (val & 0x0f) : (val >> 4); + b |= (b << 4); + b |= (b << 8); + if (col & 0x1) { + b <<= 2; + } + for (idx = 0; idx < 14; idx++) { + color = (b & 0x8000) ? fore : back; + data[off + 0] = color[0]; + data[off + 1] = color[1]; + data[off + 2] = color[2]; + b <<= 1; + off += 4; + } + off += 546 * 4; + } + } else { + for (jdx = 0; jdx < 8; jdx++) { + b = (jdx < 4) ? (val & 0x0f) : (val >> 4); + color = _colors[b]; + for (idx = 0; idx < 7; idx++) { + data[off + 0] = data[off + 4] = color[0]; + data[off + 1] = data[off + 5] = color[1]; + data[off + 2] = data[off + 6] = color[2]; + off += 8; + } + off += 546 * 4; } - off += 273 * 4; } } } @@ -211,6 +235,7 @@ function LoresPage(page) function HiresPage(page) { var _page = page; + var orangeCol = [0xff, 0x65, 0x00]; var greenCol = [0x00, 0xff, 0x00]; var blueCol = [0x09, 0x2a, 0xff]; @@ -221,7 +246,8 @@ function HiresPage(page) var _buffer = []; var _refreshing = false; - var _green = false; + var _green = [0x00, 0xff, 0x80]; + var _greenMode = false; function _init() { _buffer = allocMemPages(0x20); @@ -241,6 +267,9 @@ function HiresPage(page) return _buffer[base]; }, write: function(page, off, val) { + function dim(c) { + return [c[0] * 0.75, c[1] * 0.75, c[2] * 0.75]; + } var addr = (page << 8) | off, base = addr - 0x2000 * _page; if (_buffer[base] === val && !_refreshing) @@ -267,7 +296,7 @@ function HiresPage(page) var data = pages[_page].data, dy = rowa * 8 + rowb, - dx = col * 7 - 1, + dx = col * 14 - 2, b0 = col > 0 ? _buffer[base - 1] : 0, b2 = col < 39 ? _buffer[base + 1] : 0; val |= (b2 & 0x3) << 7; @@ -277,34 +306,37 @@ function HiresPage(page) oddCol = (hbs ? orangeCol : greenCol), evenCol = (hbs ? blueCol : violetCol); - off = dx * 4 + dy * 280 * 4; - for (var idx = 0; idx < 9; idx++, off += 4) { + off = dx * 4 + dy * 560 * 4; + for (var idx = 0; idx < 9; idx++, off += 8) { val >>= 1; if (v1) { - if (_green) { - color = greenCol; + if (_greenMode) { + color = _green; } else if (v0 || v2) { color = whiteCol; } else { color = odd ? oddCol : evenCol; } } else { - if (_green) { + if (_greenMode) { color = blackCol; - } else if (odd && v0 && v2) { - color = evenCol; + } else if (odd && v2 && v0) { + color = v0 ? dim(evenCol) : evenCol; } else if (!odd && v0 && v2) { - color = oddCol; + color = v2 ? dim(oddCol) : oddCol; } else { color = blackCol; } } - if (dx > -1 && dx < 280) { + if (dx > -1 && dx < 560) { data[off + 0] = color[0]; data[off + 1] = color[1]; data[off + 2] = color[2]; + data[off + 4] = color[0]; + data[off + 5] = color[1]; + data[off + 6] = color[2]; } dx++; @@ -324,7 +356,7 @@ function HiresPage(page) _refreshing = false; }, green: function(on) { - _green = on; + _greenMode = on; this.refresh(); }, blit: function() { @@ -333,13 +365,13 @@ function HiresPage(page) getState: function() { return { page: _page, - green: _green, + green: _greenMode, buffer: base64_encode(_buffer) }; }, setState: function(state) { _page = state.page; - _green = state.green; + _greenMode = state.green; _buffer = base64_decode(state.buffer); this.refresh(); @@ -389,9 +421,9 @@ function VideoModes(gr,hgr,gr2,hgr2) { setContext: function(c) { context = c; - pages[1] = context.createImageData(280, 192); - pages[2] = context.createImageData(280, 192); - for (var idx = 0; idx < 280 * 192 * 4; idx++) { + pages[1] = context.createImageData(560, 192); + pages[2] = context.createImageData(560, 192); + for (var idx = 0; idx < 560 * 192 * 4; idx++) { pages[1].data[idx] = 0xff; pages[2].data[idx] = 0xff; } diff --git a/js/canvas2e.js b/js/canvas2e.js index 97cdf62..02948fa 100644 --- a/js/canvas2e.js +++ b/js/canvas2e.js @@ -10,12 +10,10 @@ * implied warranty. */ +/*jshint browser:true */ /*globals allocMemPages: false, charset: false, base64_encode: false, base64_decode: false */ /*exported LoresPage, HiresPage, VideoModes */ -var GREEN = [0x00,0xff,0x00]; -// var WHITE = [0xff,0xff,0xff]; -var MONO = GREEN; var textMode = true; var mixedMode = false; @@ -47,7 +45,7 @@ function LoresPage(page) var _black = [0x00,0x00,0x00]; var _white = [0xff,0xff,0xff]; - var _green = [0x00,0xff,0x00]; + var _green = [0x00,0xff,0x80]; var _colors = [ [0x00,0x00,0x00], // 0 Black 0000 0 0 @@ -165,7 +163,7 @@ function LoresPage(page) if (textMode || (mixedMode && row > 19)) { var b; var flash = ((val & 0xc0) == 0x40) && - _blink && !_80colMode; + _blink && !_80colMode && !altCharMode; fore = flash ? _black : (_greenMode ? _green : _white); back = flash ? _white : _black; @@ -194,12 +192,9 @@ function LoresPage(page) b = charset[val * 8 + jdx]; for (idx = 0; idx < 7; idx++) { color = (b & 0x01) ? back : fore; - data[off + 0] = color[0]; - data[off + 1] = color[1]; - data[off + 2] = color[2]; - data[off + 4] = color[0]; - data[off + 5] = color[1]; - data[off + 6] = color[2]; + data[off + 0] = data[off + 4] = color[0]; + data[off + 1] = data[off + 5] = color[1]; + data[off + 2] = data[off + 6] = color[2]; b >>= 1; off += 8; } @@ -209,33 +204,73 @@ function LoresPage(page) } else { if (_80colMode) { off = (col * 14 + (bank ? 0 : 1) * 7 + row * 560 * 8) * 4; - for (jdx = 0; jdx < 8; jdx++) { - color = _colors[(jdx < 4) ? - (val & 0x0f) : (val >> 4)]; - for (idx = 0; idx < 7; idx++) { - data[off + 0] = color[0]; - data[off + 1] = color[1]; - data[off + 2] = color[2]; - off += 4; + if (_greenMode) { + fore = _green; + back = _black; + for (jdx = 0; jdx < 8; jdx++) { + b = (jdx < 4) ? (val & 0x0f) : (val >> 4); + b |= (b << 4); + if (bank & 0x1) { + b <<= 1; + } + for (idx = 0; idx < 7; idx++) { + color = (b & 0x80) ? fore : back; + data[off + 0] = color[0]; + data[off + 1] = color[1]; + data[off + 2] = color[2]; + b <<= 1; + off += 4; + } + off += 546 * 4; + } + } else { + for (jdx = 0; jdx < 8; jdx++) { + color = _colors[(jdx < 4) ? + (val & 0x0f) : (val >> 4)]; + for (idx = 0; idx < 7; idx++) { + data[off + 0] = color[0]; + data[off + 1] = color[1]; + data[off + 2] = color[2]; + off += 4; + } + off += 553 * 4; } - off += 553 * 4; } } else { off = (col * 14 + row * 560 * 8) * 4; - for (jdx = 0; jdx < 8; jdx++) { - color = _colors[(jdx < 4) ? - (val & 0x0f) : (val >> 4)]; - for (idx = 0; idx < 7; idx++) { - data[off + 0] = color[0]; - data[off + 1] = color[1]; - data[off + 2] = color[2]; - data[off + 4] = color[0]; - data[off + 5] = color[1]; - data[off + 6] = color[2]; - off += 8; + if (_greenMode) { + fore = _green; + back = _black; + for (jdx = 0; jdx < 8; jdx++) { + b = (jdx < 4) ? (val & 0x0f) : (val >> 4); + b |= (b << 4); + b |= (b << 8); + if (col & 0x1) { + b <<= 2; + } + for (idx = 0; idx < 14; idx++) { + color = (b & 0x8000) ? fore : back; + data[off + 0] = color[0]; + data[off + 1] = color[1]; + data[off + 2] = color[2]; + b <<= 1; + off += 4; + } + off += 546 * 4; + } + } else { + for (jdx = 0; jdx < 8; jdx++) { + color = _colors[(jdx < 4) ? + (val & 0x0f) : (val >> 4)]; + for (idx = 0; idx < 7; idx++) { + data[off + 0] = data[off + 4] = color[0]; + data[off + 1] = data[off + 5] = color[1]; + data[off + 2] = data[off + 6] = color[2]; + off += 8; + } + off += 546 * 4; } - off += 546 * 4; } } } @@ -267,6 +302,7 @@ function LoresPage(page) }, green: function(on) { _greenMode = on; + this.refresh(); }, blit: function() { context.putImageData(pages[_page], 0, 0); @@ -274,14 +310,14 @@ function LoresPage(page) getState: function() { return { page: _page, - green: _green, + green: _greenMode, buffer: [base64_encode(_buffer[0]), base64_encode(_buffer[1])] }; }, setState: function(state) { _page = state.page; - _green = state.green; + _greenMode = state.green; _buffer[0] = base64_decode(state.buffer[0]); _buffer[1] = base64_decode(state.buffer[1]); @@ -350,7 +386,8 @@ function HiresPage(page) var _buffer = []; var _refreshing = false; - var _green = false; + var _green = [0x00, 0xff, 0x80]; + var _greenMode = false; var _garish = false; function _init() { @@ -408,6 +445,9 @@ function HiresPage(page) return _buffer[bank][base]; }, _write: function(page, off, val, bank) { + function dim(c) { + return [c[0] * 0.75, c[1] * 0.75, c[2] * 0.75]; + } var addr = (page << 8) | off, base = addr - 0x2000 * _page, idx, jdx; if (_buffer[bank][base] == val && !_refreshing) @@ -462,18 +502,22 @@ function HiresPage(page) off = dx * 4 + dy * 280 * 4; for (idx = 0; idx < 7; idx++) { - var dcolor = _green ? MONO : dcolors[r4[c[idx]]]; + var dcolor = _greenMode ? _green : dcolors[r4[c[idx]]]; var bits = c[idx] | (c[idx + 1] << 4); for (jdx = 0; jdx < 4; jdx++, off += 4) { - var saturate = !MONO && (_garish || bits & 0xe); - if (bits & 0x1 || saturate) { + if (bits & 0x1) { data[off + 0] = dcolor[0]; data[off + 1] = dcolor[1]; data[off + 2] = dcolor[2]; + } else if (_greenMode) { + data[off + 0] = 0; + data[off + 1] = 0; + data[off + 2] = 0; } else { - data[off + 0] = 0; // dcolor[0] >> 1; - data[off + 1] = 0; // dcolor[1] >> 1; - data[off + 2] = 0; // dcolor[2] >> 1; + dcolor = dim(dcolor); + data[off + 0] = dcolor[0]; + data[off + 1] = dcolor[1]; + data[off + 2] = dcolor[2]; } bits >>= 1; } @@ -492,20 +536,20 @@ function HiresPage(page) for (idx = 0; idx < 9; idx++, off += 8) { val >>= 1; if (v1) { - if (_green) { - color = MONO; + if (_greenMode) { + color = _green; } else if (v0 || v2) { color = whiteCol; } else { color = odd ? oddCol : evenCol; } } else { - if (_green) { + if (_greenMode) { color = blackCol; - } else if (odd && v2 && (_garish || v0)) { - color = evenCol; - } else if (!odd && v0 && (_garish || v2)) { - color = oddCol; + } else if (odd && v2 && v0) { + color = v0 ? dim(evenCol) : evenCol; + } else if (!odd && v0 && v2) { + color = v2 ? dim(oddCol) : oddCol; } else { color = blackCol; } @@ -542,12 +586,12 @@ function HiresPage(page) _refreshing = false; }, green: function(on) { - _green = on; + _greenMode = on; this.refresh(); }, garish: function(on) { - _garish = on; - this.refresh(); + // _garish = on; + // this.refresh(); }, blit: function() { context.putImageData(pages[_page], 0, 0); @@ -555,7 +599,7 @@ function HiresPage(page) getState: function() { return { page: _page, - green: _green, + green: _greenMode, garish: _garish, buffer: [base64_encode(_buffer[0]), base64_encode(_buffer[1])] @@ -563,7 +607,7 @@ function HiresPage(page) }, setState: function(state) { _page = state.page; - _green = state.green; + _greenMode = state.green; _garish = state.garish; _buffer[0] = base64_decode(state.buffer[0]); _buffer[1] = base64_decode(state.buffer[1]);