Joystick and graphics fixes

Better greenscreen support, joystick improvements and fixes.
This commit is contained in:
Will Scullin 2013-12-07 11:20:22 -08:00
parent cf8e2bb8cc
commit 86c395ccda
4 changed files with 183 additions and 95 deletions

View File

@ -15,7 +15,7 @@
<title>Apple ][js - An Apple 2 Emulator in JavaScript</title> <title>Apple ][js - An Apple 2 Emulator in JavaScript</title>
<meta name="viewport" content="width=device-width user-scalable=0" /> <meta name="viewport" content="width=device-width, user-scalable=no" />
<meta name="apple-mobile-web-app-capable" content="yes"> <meta name="apple-mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-title" content="Apple ][js"> <meta name="apple-mobile-web-app-title" content="Apple ][js">
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent"> <meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">
@ -778,8 +778,10 @@ function _mousemove(evt) {
if (gamepad) { if (gamepad) {
return; return;
} }
var x = evt.offsetX / $(this).width(), var s = $("#screen");
y = evt.offsetY / $(this).height(), var offset = s.offset();
var x = (evt.pageX - offset.left) / s.width(),
y = (evt.pageY - offset.top) / s.height(),
z = x; z = x;
if (swapXY) { if (swapXY) {
@ -806,7 +808,13 @@ function pauseRun(b) {
$(function() { $(function() {
hashtag = document.location.hash; 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 canvas = document.getElementById("screen");
var context = canvas.getContext('2d'); var context = canvas.getContext('2d');

View File

@ -205,7 +205,11 @@ function Apple2IO(cpu, callbacks)
case LOC.STROBE: case LOC.STROBE:
_key &= 0x7f; _key &= 0x7f;
if (_buffer.length > 0) { 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; result = _keyDown ? 0x80 : 0x00;
break; break;
@ -222,16 +226,16 @@ function Apple2IO(cpu, callbacks)
result = _button[2] ? 0x80 : 0; result = _button[2] ? 0x80 : 0;
break; break;
case LOC.PADDLE0: case LOC.PADDLE0:
result = (delta < (_paddle[0] * 2560) ? 0x80 : 0x00); result = (delta < (_paddle[0] * 2756) ? 0x80 : 0x00);
break; break;
case LOC.PADDLE1: case LOC.PADDLE1:
result = (delta < (_paddle[1] * 2560) ? 0x80 : 0x00); result = (delta < (_paddle[1] * 2756) ? 0x80 : 0x00);
break; break;
case LOC.PADDLE2: case LOC.PADDLE2:
result = (delta < (_paddle[2] * 2560) ? 0x80 : 0x00); result = (delta < (_paddle[2] * 2756) ? 0x80 : 0x00);
break; break;
case LOC.PADDLE3: case LOC.PADDLE3:
result = (delta < (_paddle[3] * 2560) ? 0x80 : 0x00); result = (delta < (_paddle[3] * 2756) ? 0x80 : 0x00);
break; break;
case LOC.PDLTRIG: case LOC.PDLTRIG:
_trigger = cpu.cycles(); _trigger = cpu.cycles();

View File

@ -10,6 +10,7 @@
* implied warranty. * implied warranty.
*/ */
/*jshint browser:true */
/*globals allocMemPages: false, /*globals allocMemPages: false,
charset: false, charset: false,
base64_encode: false, base64_decode: false */ base64_encode: false, base64_decode: false */
@ -40,7 +41,7 @@ function LoresPage(page)
var _black = [0x00,0x00,0x00]; var _black = [0x00,0x00,0x00];
var _white = [0xff,0xff,0xff]; var _white = [0xff,0xff,0xff];
var _green = [0x00,0xff,0x00]; var _green = [0x00,0xff,0x88];
var _colors = [ var _colors = [
[0x00,0x00,0x00], // 0 Black 0000 0 0 [0x00,0x00,0x00], // 0 Black 0000 0 0
@ -56,7 +57,7 @@ function LoresPage(page)
[0x40,0x40,0x40], // 10 Gray 2 1010 10 [0x40,0x40,0x40], // 10 Gray 2 1010 10
[0xff,0x96,0xbf], // 11 Pink 1011 11 [0xff,0x96,0xbf], // 11 Pink 1011 11
[0x2f,0xbc,0x1a], // 12 Light Green 0110 6 [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 [0x6f,0xe8,0xbf], // 14 Aqua 1110 14
[0xff,0xff,0xff] // 15 White 1111 15 [0xff,0xff,0xff] // 15 White 1111 15
@ -127,7 +128,7 @@ function LoresPage(page)
return; return;
var data = pages[_page].data, fore, back; 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 (textMode || (mixedMode && row > 19)) {
if (val & 0x80 || ((val & 0x40) && _blink)) { if (val & 0x80 || ((val & 0x40) && _blink)) {
@ -142,24 +143,47 @@ function LoresPage(page)
b <<= 1; b <<= 1;
for (idx = 0; idx < 7; idx++) { for (idx = 0; idx < 7; idx++) {
color = (b & 0x80) ? fore : back; color = (b & 0x80) ? fore : back;
data[off + 0] = color[0]; data[off + 0] = data[off + 4] = color[0];
data[off + 1] = color[1]; data[off + 1] = data[off + 5] = color[1];
data[off + 2] = color[2]; data[off + 2] = data[off + 6] = color[2];
b <<= 1; b <<= 1;
off += 4; off += 8;
} }
off += 273 * 4; off += 546 * 4;
} }
} else { } else {
for (jdx = 0; jdx < 8; jdx++) { if (_greenMode) {
color = _colors[(jdx < 4) ? (val & 0x0f) : (val >> 4)]; fore = _green;
for (idx = 0; idx < 7; idx++) { back = _black;
data[off + 0] = color[0]; for (jdx = 0; jdx < 8; jdx++) {
data[off + 1] = color[1]; b = (jdx < 4) ? (val & 0x0f) : (val >> 4);
data[off + 2] = color[2]; b |= (b << 4);
off += 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) function HiresPage(page)
{ {
var _page = page; var _page = page;
var orangeCol = [0xff, 0x65, 0x00]; var orangeCol = [0xff, 0x65, 0x00];
var greenCol = [0x00, 0xff, 0x00]; var greenCol = [0x00, 0xff, 0x00];
var blueCol = [0x09, 0x2a, 0xff]; var blueCol = [0x09, 0x2a, 0xff];
@ -221,7 +246,8 @@ function HiresPage(page)
var _buffer = []; var _buffer = [];
var _refreshing = false; var _refreshing = false;
var _green = false; var _green = [0x00, 0xff, 0x80];
var _greenMode = false;
function _init() { function _init() {
_buffer = allocMemPages(0x20); _buffer = allocMemPages(0x20);
@ -241,6 +267,9 @@ function HiresPage(page)
return _buffer[base]; return _buffer[base];
}, },
write: function(page, off, val) { 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, var addr = (page << 8) | off,
base = addr - 0x2000 * _page; base = addr - 0x2000 * _page;
if (_buffer[base] === val && !_refreshing) if (_buffer[base] === val && !_refreshing)
@ -267,7 +296,7 @@ function HiresPage(page)
var data = pages[_page].data, var data = pages[_page].data,
dy = rowa * 8 + rowb, dy = rowa * 8 + rowb,
dx = col * 7 - 1, dx = col * 14 - 2,
b0 = col > 0 ? _buffer[base - 1] : 0, b0 = col > 0 ? _buffer[base - 1] : 0,
b2 = col < 39 ? _buffer[base + 1] : 0; b2 = col < 39 ? _buffer[base + 1] : 0;
val |= (b2 & 0x3) << 7; val |= (b2 & 0x3) << 7;
@ -277,34 +306,37 @@ function HiresPage(page)
oddCol = (hbs ? orangeCol : greenCol), oddCol = (hbs ? orangeCol : greenCol),
evenCol = (hbs ? blueCol : violetCol); evenCol = (hbs ? blueCol : violetCol);
off = dx * 4 + dy * 280 * 4; off = dx * 4 + dy * 560 * 4;
for (var idx = 0; idx < 9; idx++, off += 4) { for (var idx = 0; idx < 9; idx++, off += 8) {
val >>= 1; val >>= 1;
if (v1) { if (v1) {
if (_green) { if (_greenMode) {
color = greenCol; color = _green;
} else if (v0 || v2) { } else if (v0 || v2) {
color = whiteCol; color = whiteCol;
} else { } else {
color = odd ? oddCol : evenCol; color = odd ? oddCol : evenCol;
} }
} else { } else {
if (_green) { if (_greenMode) {
color = blackCol; color = blackCol;
} else if (odd && v0 && v2) { } else if (odd && v2 && v0) {
color = evenCol; color = v0 ? dim(evenCol) : evenCol;
} else if (!odd && v0 && v2) { } else if (!odd && v0 && v2) {
color = oddCol; color = v2 ? dim(oddCol) : oddCol;
} else { } else {
color = blackCol; color = blackCol;
} }
} }
if (dx > -1 && dx < 280) { if (dx > -1 && dx < 560) {
data[off + 0] = color[0]; data[off + 0] = color[0];
data[off + 1] = color[1]; data[off + 1] = color[1];
data[off + 2] = color[2]; data[off + 2] = color[2];
data[off + 4] = color[0];
data[off + 5] = color[1];
data[off + 6] = color[2];
} }
dx++; dx++;
@ -324,7 +356,7 @@ function HiresPage(page)
_refreshing = false; _refreshing = false;
}, },
green: function(on) { green: function(on) {
_green = on; _greenMode = on;
this.refresh(); this.refresh();
}, },
blit: function() { blit: function() {
@ -333,13 +365,13 @@ function HiresPage(page)
getState: function() { getState: function() {
return { return {
page: _page, page: _page,
green: _green, green: _greenMode,
buffer: base64_encode(_buffer) buffer: base64_encode(_buffer)
}; };
}, },
setState: function(state) { setState: function(state) {
_page = state.page; _page = state.page;
_green = state.green; _greenMode = state.green;
_buffer = base64_decode(state.buffer); _buffer = base64_decode(state.buffer);
this.refresh(); this.refresh();
@ -389,9 +421,9 @@ function VideoModes(gr,hgr,gr2,hgr2) {
setContext: function(c) { setContext: function(c) {
context = c; context = c;
pages[1] = context.createImageData(280, 192); pages[1] = context.createImageData(560, 192);
pages[2] = context.createImageData(280, 192); pages[2] = context.createImageData(560, 192);
for (var idx = 0; idx < 280 * 192 * 4; idx++) { for (var idx = 0; idx < 560 * 192 * 4; idx++) {
pages[1].data[idx] = 0xff; pages[1].data[idx] = 0xff;
pages[2].data[idx] = 0xff; pages[2].data[idx] = 0xff;
} }

View File

@ -10,12 +10,10 @@
* implied warranty. * implied warranty.
*/ */
/*jshint browser:true */
/*globals allocMemPages: false, charset: false, base64_encode: false, base64_decode: false */ /*globals allocMemPages: false, charset: false, base64_encode: false, base64_decode: false */
/*exported LoresPage, HiresPage, VideoModes */ /*exported LoresPage, HiresPage, VideoModes */
var GREEN = [0x00,0xff,0x00];
// var WHITE = [0xff,0xff,0xff];
var MONO = GREEN;
var textMode = true; var textMode = true;
var mixedMode = false; var mixedMode = false;
@ -47,7 +45,7 @@ function LoresPage(page)
var _black = [0x00,0x00,0x00]; var _black = [0x00,0x00,0x00];
var _white = [0xff,0xff,0xff]; var _white = [0xff,0xff,0xff];
var _green = [0x00,0xff,0x00]; var _green = [0x00,0xff,0x80];
var _colors = [ var _colors = [
[0x00,0x00,0x00], // 0 Black 0000 0 0 [0x00,0x00,0x00], // 0 Black 0000 0 0
@ -165,7 +163,7 @@ function LoresPage(page)
if (textMode || (mixedMode && row > 19)) { if (textMode || (mixedMode && row > 19)) {
var b; var b;
var flash = ((val & 0xc0) == 0x40) && var flash = ((val & 0xc0) == 0x40) &&
_blink && !_80colMode; _blink && !_80colMode && !altCharMode;
fore = flash ? _black : (_greenMode ? _green : _white); fore = flash ? _black : (_greenMode ? _green : _white);
back = flash ? _white : _black; back = flash ? _white : _black;
@ -194,12 +192,9 @@ function LoresPage(page)
b = charset[val * 8 + jdx]; b = charset[val * 8 + jdx];
for (idx = 0; idx < 7; idx++) { for (idx = 0; idx < 7; idx++) {
color = (b & 0x01) ? back : fore; color = (b & 0x01) ? back : fore;
data[off + 0] = color[0]; data[off + 0] = data[off + 4] = color[0];
data[off + 1] = color[1]; data[off + 1] = data[off + 5] = color[1];
data[off + 2] = color[2]; data[off + 2] = data[off + 6] = color[2];
data[off + 4] = color[0];
data[off + 5] = color[1];
data[off + 6] = color[2];
b >>= 1; b >>= 1;
off += 8; off += 8;
} }
@ -209,33 +204,73 @@ function LoresPage(page)
} else { } else {
if (_80colMode) { if (_80colMode) {
off = (col * 14 + (bank ? 0 : 1) * 7 + row * 560 * 8) * 4; off = (col * 14 + (bank ? 0 : 1) * 7 + row * 560 * 8) * 4;
for (jdx = 0; jdx < 8; jdx++) { if (_greenMode) {
color = _colors[(jdx < 4) ? fore = _green;
(val & 0x0f) : (val >> 4)]; back = _black;
for (idx = 0; idx < 7; idx++) { for (jdx = 0; jdx < 8; jdx++) {
data[off + 0] = color[0]; b = (jdx < 4) ? (val & 0x0f) : (val >> 4);
data[off + 1] = color[1]; b |= (b << 4);
data[off + 2] = color[2]; if (bank & 0x1) {
off += 4; 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 { } else {
off = (col * 14 + row * 560 * 8) * 4; off = (col * 14 + row * 560 * 8) * 4;
for (jdx = 0; jdx < 8; jdx++) { if (_greenMode) {
color = _colors[(jdx < 4) ? fore = _green;
(val & 0x0f) : (val >> 4)]; back = _black;
for (idx = 0; idx < 7; idx++) { for (jdx = 0; jdx < 8; jdx++) {
data[off + 0] = color[0]; b = (jdx < 4) ? (val & 0x0f) : (val >> 4);
data[off + 1] = color[1]; b |= (b << 4);
data[off + 2] = color[2]; b |= (b << 8);
data[off + 4] = color[0]; if (col & 0x1) {
data[off + 5] = color[1]; b <<= 2;
data[off + 6] = color[2]; }
off += 8; 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) { green: function(on) {
_greenMode = on; _greenMode = on;
this.refresh();
}, },
blit: function() { blit: function() {
context.putImageData(pages[_page], 0, 0); context.putImageData(pages[_page], 0, 0);
@ -274,14 +310,14 @@ function LoresPage(page)
getState: function() { getState: function() {
return { return {
page: _page, page: _page,
green: _green, green: _greenMode,
buffer: [base64_encode(_buffer[0]), buffer: [base64_encode(_buffer[0]),
base64_encode(_buffer[1])] base64_encode(_buffer[1])]
}; };
}, },
setState: function(state) { setState: function(state) {
_page = state.page; _page = state.page;
_green = state.green; _greenMode = state.green;
_buffer[0] = base64_decode(state.buffer[0]); _buffer[0] = base64_decode(state.buffer[0]);
_buffer[1] = base64_decode(state.buffer[1]); _buffer[1] = base64_decode(state.buffer[1]);
@ -350,7 +386,8 @@ function HiresPage(page)
var _buffer = []; var _buffer = [];
var _refreshing = false; var _refreshing = false;
var _green = false; var _green = [0x00, 0xff, 0x80];
var _greenMode = false;
var _garish = false; var _garish = false;
function _init() { function _init() {
@ -408,6 +445,9 @@ function HiresPage(page)
return _buffer[bank][base]; return _buffer[bank][base];
}, },
_write: function(page, off, val, bank) { _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, var addr = (page << 8) | off, base = addr - 0x2000 * _page,
idx, jdx; idx, jdx;
if (_buffer[bank][base] == val && !_refreshing) if (_buffer[bank][base] == val && !_refreshing)
@ -462,18 +502,22 @@ function HiresPage(page)
off = dx * 4 + dy * 280 * 4; off = dx * 4 + dy * 280 * 4;
for (idx = 0; idx < 7; idx++) { 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); var bits = c[idx] | (c[idx + 1] << 4);
for (jdx = 0; jdx < 4; jdx++, off += 4) { for (jdx = 0; jdx < 4; jdx++, off += 4) {
var saturate = !MONO && (_garish || bits & 0xe); if (bits & 0x1) {
if (bits & 0x1 || saturate) {
data[off + 0] = dcolor[0]; data[off + 0] = dcolor[0];
data[off + 1] = dcolor[1]; data[off + 1] = dcolor[1];
data[off + 2] = dcolor[2]; data[off + 2] = dcolor[2];
} else if (_greenMode) {
data[off + 0] = 0;
data[off + 1] = 0;
data[off + 2] = 0;
} else { } else {
data[off + 0] = 0; // dcolor[0] >> 1; dcolor = dim(dcolor);
data[off + 1] = 0; // dcolor[1] >> 1; data[off + 0] = dcolor[0];
data[off + 2] = 0; // dcolor[2] >> 1; data[off + 1] = dcolor[1];
data[off + 2] = dcolor[2];
} }
bits >>= 1; bits >>= 1;
} }
@ -492,20 +536,20 @@ function HiresPage(page)
for (idx = 0; idx < 9; idx++, off += 8) { for (idx = 0; idx < 9; idx++, off += 8) {
val >>= 1; val >>= 1;
if (v1) { if (v1) {
if (_green) { if (_greenMode) {
color = MONO; color = _green;
} else if (v0 || v2) { } else if (v0 || v2) {
color = whiteCol; color = whiteCol;
} else { } else {
color = odd ? oddCol : evenCol; color = odd ? oddCol : evenCol;
} }
} else { } else {
if (_green) { if (_greenMode) {
color = blackCol; color = blackCol;
} else if (odd && v2 && (_garish || v0)) { } else if (odd && v2 && v0) {
color = evenCol; color = v0 ? dim(evenCol) : evenCol;
} else if (!odd && v0 && (_garish || v2)) { } else if (!odd && v0 && v2) {
color = oddCol; color = v2 ? dim(oddCol) : oddCol;
} else { } else {
color = blackCol; color = blackCol;
} }
@ -542,12 +586,12 @@ function HiresPage(page)
_refreshing = false; _refreshing = false;
}, },
green: function(on) { green: function(on) {
_green = on; _greenMode = on;
this.refresh(); this.refresh();
}, },
garish: function(on) { garish: function(on) {
_garish = on; // _garish = on;
this.refresh(); // this.refresh();
}, },
blit: function() { blit: function() {
context.putImageData(pages[_page], 0, 0); context.putImageData(pages[_page], 0, 0);
@ -555,7 +599,7 @@ function HiresPage(page)
getState: function() { getState: function() {
return { return {
page: _page, page: _page,
green: _green, green: _greenMode,
garish: _garish, garish: _garish,
buffer: [base64_encode(_buffer[0]), buffer: [base64_encode(_buffer[0]),
base64_encode(_buffer[1])] base64_encode(_buffer[1])]
@ -563,7 +607,7 @@ function HiresPage(page)
}, },
setState: function(state) { setState: function(state) {
_page = state.page; _page = state.page;
_green = state.green; _greenMode = state.green;
_garish = state.garish; _garish = state.garish;
_buffer[0] = base64_decode(state.buffer[0]); _buffer[0] = base64_decode(state.buffer[0]);
_buffer[1] = base64_decode(state.buffer[1]); _buffer[1] = base64_decode(state.buffer[1]);