mirror of
https://github.com/sehugg/8bitworkshop.git
synced 2024-06-01 20:41:36 +00:00
rename local storage keys; z80 tests
This commit is contained in:
parent
ab89dd8b47
commit
bd551f7311
|
@ -13,7 +13,7 @@
|
||||||
"test": "tests"
|
"test": "tests"
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"test": "mocha"
|
"test": "mocha --recursive"
|
||||||
},
|
},
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
|
|
1691
src/cpu/z80.js
Normal file
1691
src/cpu/z80.js
Normal file
File diff suppressed because one or more lines are too long
25
src/ui.js
25
src/ui.js
|
@ -1,5 +1,11 @@
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
|
// 8bitworkshop IDE user interface
|
||||||
|
|
||||||
|
var PRESETS; // presets array
|
||||||
|
var platform_id;
|
||||||
|
var platform; // platform object
|
||||||
|
|
||||||
var FileStore = function(storage, prefix) {
|
var FileStore = function(storage, prefix) {
|
||||||
var self = this;
|
var self = this;
|
||||||
this.saveFile = function(name, text) {
|
this.saveFile = function(name, text) {
|
||||||
|
@ -9,6 +15,17 @@ var FileStore = function(storage, prefix) {
|
||||||
return storage.getItem(prefix + name) || storage.getItem(name);
|
return storage.getItem(prefix + name) || storage.getItem(name);
|
||||||
}
|
}
|
||||||
this.getFiles = function(prefix2) {
|
this.getFiles = function(prefix2) {
|
||||||
|
// rename items for compatibility
|
||||||
|
for (var i = 0; i < storage.length; i++) {
|
||||||
|
var key = storage.key(i);
|
||||||
|
if (key.startsWith(prefix2) && platform_id == 'vcs') {
|
||||||
|
this.saveFile(key, storage.getItem(key));
|
||||||
|
storage.removeItem(key);
|
||||||
|
console.log("Renamed",key,'to',prefix+key);
|
||||||
|
i=-1; // reset loop
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// iterate over files with <platform>/<dir> prefix
|
||||||
var files = [];
|
var files = [];
|
||||||
for (var i = 0; i < storage.length; i++) {
|
for (var i = 0; i < storage.length; i++) {
|
||||||
var key = storage.key(i);
|
var key = storage.key(i);
|
||||||
|
@ -16,10 +33,6 @@ var FileStore = function(storage, prefix) {
|
||||||
var name = key.substring(prefix.length + prefix2.length);
|
var name = key.substring(prefix.length + prefix2.length);
|
||||||
files.push(name);
|
files.push(name);
|
||||||
}
|
}
|
||||||
else if (key.startsWith(prefix2)) {
|
|
||||||
var name = key.substring(prefix2.length);
|
|
||||||
files.push(name);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return files;
|
return files;
|
||||||
}
|
}
|
||||||
|
@ -29,8 +42,6 @@ var FileStore = function(storage, prefix) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 8bitworkshop IDE user interface
|
|
||||||
|
|
||||||
var worker = new Worker("./src/worker/workermain.js");
|
var worker = new Worker("./src/worker/workermain.js");
|
||||||
var current_output = null;
|
var current_output = null;
|
||||||
var current_preset_index = -1; // TODO: use URL
|
var current_preset_index = -1; // TODO: use URL
|
||||||
|
@ -41,8 +52,6 @@ var pcvisits;
|
||||||
var trace_pending_at_pc;
|
var trace_pending_at_pc;
|
||||||
var store;
|
var store;
|
||||||
|
|
||||||
var PRESETS, platform, platform_id;
|
|
||||||
|
|
||||||
var CODE = 'code1';
|
var CODE = 'code1';
|
||||||
var editor = CodeMirror(document.getElementById('editor'), {
|
var editor = CodeMirror(document.getElementById('editor'), {
|
||||||
mode: '6502',
|
mode: '6502',
|
||||||
|
|
24
src/worker/sdasz80.js
Normal file
24
src/worker/sdasz80.js
Normal file
File diff suppressed because one or more lines are too long
35
src/worker/sdcc.js
Normal file
35
src/worker/sdcc.js
Normal file
File diff suppressed because one or more lines are too long
|
@ -7,6 +7,8 @@ importScripts("plasm.js");
|
||||||
importScripts("cc65.js");
|
importScripts("cc65.js");
|
||||||
importScripts("ca65.js");
|
importScripts("ca65.js");
|
||||||
importScripts("ld65.js");
|
importScripts("ld65.js");
|
||||||
|
importScripts("z80asm.js");
|
||||||
|
importScripts("sdcc.js");
|
||||||
|
|
||||||
// shim out window and document objects for security
|
// shim out window and document objects for security
|
||||||
// https://github.com/mbostock/d3/issues/1053
|
// https://github.com/mbostock/d3/issues/1053
|
||||||
|
@ -58,6 +60,22 @@ var print_fn = function(s) {
|
||||||
//console.log(new Error().stack);
|
//console.log(new Error().stack);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// test.c(6) : warning 85: in function main unreferenced local variable : 'x'
|
||||||
|
var re_msvc = /(.*?)[(](\d+)[)]\s*:\s*(\w+)\s*(\d+):\s*(.*)/;
|
||||||
|
var msvc_errors;
|
||||||
|
|
||||||
|
function match_msvc(s) {
|
||||||
|
var matches = re_msvc.exec(s);
|
||||||
|
console.log(s, matches);
|
||||||
|
if (matches) {
|
||||||
|
errline = parseInt(matches[1]);
|
||||||
|
errors.push({
|
||||||
|
line:errline,
|
||||||
|
msg:matches[2]
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function parseDASMListing(code, unresolved) {
|
function parseDASMListing(code, unresolved) {
|
||||||
var errorMatch = /main.a [(](\d+)[)]: error: (.+)/;
|
var errorMatch = /main.a [(](\d+)[)]: error: (.+)/;
|
||||||
// 4 08ee a9 00 start lda #01workermain.js:23:5
|
// 4 08ee a9 00 start lda #01workermain.js:23:5
|
||||||
|
@ -167,14 +185,15 @@ function assembleACME(code) {
|
||||||
});
|
});
|
||||||
var FS = Module['FS'];
|
var FS = Module['FS'];
|
||||||
FS.writeFile("main.a", code);
|
FS.writeFile("main.a", code);
|
||||||
|
// TODO: --msvc
|
||||||
Module.callMain(["-o", "a.out", "-r", "a.rpt", "-l", "a.sym", "--setpc", "24576", "main.a"]);
|
Module.callMain(["-o", "a.out", "-r", "a.rpt", "-l", "a.sym", "--setpc", "24576", "main.a"]);
|
||||||
if (errors.length) {
|
if (errors.length) {
|
||||||
return {listing:{errors:errors}};
|
return {listing:{errors:errors}};
|
||||||
}
|
}
|
||||||
var aout = FS.readFile("a.out");
|
var aout = FS.readFile("a.out");
|
||||||
var alst = FS.readFile("a.rpt", {'encoding':'utf8'}); // TODO
|
var alst = FS.readFile("a.rpt", {'encoding':'utf8'});
|
||||||
var asym = FS.readFile("a.sym", {'encoding':'utf8'}); // TODO
|
var asym = FS.readFile("a.sym", {'encoding':'utf8'});
|
||||||
var listing = parseDASMListing(alst, {}); // TODO
|
var listing = parseDASMListing(alst, {});
|
||||||
return {
|
return {
|
||||||
exitstatus:Module.EXITSTATUS,
|
exitstatus:Module.EXITSTATUS,
|
||||||
output:aout,
|
output:aout,
|
||||||
|
@ -183,6 +202,13 @@ function assembleACME(code) {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function setupStdin(fs, code) {
|
||||||
|
var i = 0;
|
||||||
|
fs.init(
|
||||||
|
function() { return i<code.length ? code.charCodeAt(i++) : null; }
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
function compilePLASMA(code) {
|
function compilePLASMA(code) {
|
||||||
// stdout
|
// stdout
|
||||||
var outstr = "";
|
var outstr = "";
|
||||||
|
@ -212,12 +238,9 @@ function compilePLASMA(code) {
|
||||||
printErr:match_fn,
|
printErr:match_fn,
|
||||||
});
|
});
|
||||||
var FS = Module['FS'];
|
var FS = Module['FS'];
|
||||||
var i = 0;
|
|
||||||
var output = [];
|
var output = [];
|
||||||
FS.init(
|
setupStdin(FS, code);
|
||||||
function() { return i<code.length ? code.charCodeAt(i++) : null; }
|
//FS.writeFile("main.pla", code);
|
||||||
);
|
|
||||||
FS.writeFile("main.pla", code);
|
|
||||||
Module.callMain(["-A"]);
|
Module.callMain(["-A"]);
|
||||||
outstr = "INTERP = $e044\n" + outstr; // TODO
|
outstr = "INTERP = $e044\n" + outstr; // TODO
|
||||||
if (errors.length) {
|
if (errors.length) {
|
||||||
|
@ -333,11 +356,85 @@ function compileCC65(code, platform) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function assembleZ80ASM(code, platform) {
|
||||||
|
if (!platform)
|
||||||
|
platform = 'apple2'; // TODO
|
||||||
|
var Module = z80asm({
|
||||||
|
noInitialRun:true,
|
||||||
|
//logReadFiles:true,
|
||||||
|
print:print_fn,
|
||||||
|
printErr:print_fn,
|
||||||
|
//locateFile: function(s) { return "" + s; },
|
||||||
|
});
|
||||||
|
var FS = Module['FS'];
|
||||||
|
//setupFS(FS);
|
||||||
|
// changes for dialect
|
||||||
|
code = code.replace(".optsdcc -mz80","");
|
||||||
|
code = code.replace(/\tXREF /gi,"\tEXTERN ");
|
||||||
|
code = code.replace(/\tXDEF /gi,"\tPUBLIC ");
|
||||||
|
FS.writeFile("main.asm", code);
|
||||||
|
try {
|
||||||
|
Module.callMain(["-b", "-s", "-l", "-m", "main.asm"]);
|
||||||
|
try {
|
||||||
|
var aerr = FS.readFile("main.err", {'encoding':'utf8'}); // TODO
|
||||||
|
console.log("ERRORS", aerr); // TODO
|
||||||
|
// Warning at file 'test.asm' line 9: 'XREF' is deprecated, use 'EXTERN' instead
|
||||||
|
} catch (e) {
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
77 0000 ;test.c:5: return 0;
|
||||||
|
78 0000 21 00 00 ld hl,$0000
|
||||||
|
*/
|
||||||
|
var alst = FS.readFile("main.lst", {'encoding':'utf8'}); // TODO
|
||||||
|
/*
|
||||||
|
_main = 0000, G: test
|
||||||
|
l_main00101 = 0003, L: test
|
||||||
|
*/
|
||||||
|
var amap = FS.readFile("main.map", {'encoding':'utf8'}); // TODO
|
||||||
|
var aout = FS.readFile("main.bin", {'encoding':'binary'});
|
||||||
|
var listing = parseDASMListing(alst, {}); // TODO
|
||||||
|
return {
|
||||||
|
exitstatus:Module.EXITSTATUS,
|
||||||
|
output:aout,
|
||||||
|
listing:listing,
|
||||||
|
intermediate:{listing:alst, mapfile:amap},
|
||||||
|
};
|
||||||
|
} catch (e) {
|
||||||
|
throw Error(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function compileSDCC(code, platform) {
|
||||||
|
var SDCC = sdcc({
|
||||||
|
noInitialRun:true,
|
||||||
|
noFSInit:true,
|
||||||
|
//logReadFiles:true,
|
||||||
|
print:print_fn,
|
||||||
|
printErr:match_msvc,
|
||||||
|
//locateFile: function(s) { return "" + s; },
|
||||||
|
});
|
||||||
|
var FS = SDCC['FS'];
|
||||||
|
setupStdin(FS, code);
|
||||||
|
setupFS(FS);
|
||||||
|
//FS.writeFile("main.c", code, {encoding:'utf8'});
|
||||||
|
msvc_errors = [];
|
||||||
|
SDCC.callMain(['--vc', '--c1mode', '--std-sdcc99', '--fomit-frame-pointer',
|
||||||
|
'-mz80', '--asm=z80asm', '-o', 'test.asm']);
|
||||||
|
try {
|
||||||
|
var asmout = FS.readFile("test.asm", {encoding:'utf8'});
|
||||||
|
return assembleZ80ASM(asmout, platform, msvc_errors);
|
||||||
|
} catch(e) {
|
||||||
|
return {listing:{errors:msvc_errors}};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
var tools = {
|
var tools = {
|
||||||
'dasm': assembleDASM,
|
'dasm': assembleDASM,
|
||||||
'plasm': compilePLASMA,
|
'plasm': compilePLASMA,
|
||||||
'cc65': compileCC65,
|
'cc65': compileCC65,
|
||||||
'ca65': assemblelinkCA65,
|
'ca65': assemblelinkCA65,
|
||||||
|
'z80asm': assembleZ80ASM,
|
||||||
|
'sdcc': compileSDCC,
|
||||||
}
|
}
|
||||||
|
|
||||||
onmessage = function(e) {
|
onmessage = function(e) {
|
||||||
|
|
28
src/worker/z80asm.js
Normal file
28
src/worker/z80asm.js
Normal file
File diff suppressed because one or more lines are too long
|
@ -73,7 +73,7 @@ function compile(tool, code, callback, outlen) {
|
||||||
}
|
}
|
||||||
|
|
||||||
describe('Worker', function() {
|
describe('Worker', function() {
|
||||||
it('should compile DASM', function(done) {
|
it('should assemble DASM', function(done) {
|
||||||
compile('dasm', '\tprocessor 6502\n\torg $f000\nfoo lda #0\n', done, 2);
|
compile('dasm', '\tprocessor 6502\n\torg $f000\nfoo lda #0\n', done, 2);
|
||||||
});
|
});
|
||||||
it('should compile PLASMA', function(done) {
|
it('should compile PLASMA', function(done) {
|
||||||
|
@ -82,4 +82,10 @@ describe('Worker', function() {
|
||||||
it('should compile CC65', function(done) {
|
it('should compile CC65', function(done) {
|
||||||
compile('cc65', '#include <stdio.h>\nint main() {\nint x=1;\nprintf("%d",x);\nreturn x+2;\n}', done, 2947);
|
compile('cc65', '#include <stdio.h>\nint main() {\nint x=1;\nprintf("%d",x);\nreturn x+2;\n}', done, 2947);
|
||||||
});
|
});
|
||||||
|
it('should assemble Z80ASM', function(done) {
|
||||||
|
compile('z80asm', '\tMODULE test\n\tXREF _puts\n\tld hl,$0000\n\tret\n', done, 4);
|
||||||
|
});
|
||||||
|
it('should compile SDCC', function(done) {
|
||||||
|
compile('sdcc', 'int main(int argc) {\nint x=1; int y=2;\nreturn x+y+argc;\n}', done, 16);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
5
test/z80/README
Normal file
5
test/z80/README
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
This is a test harness for the z80 unit tests shipped with Fuse.
|
||||||
|
|
||||||
|
Pasting the contents of tests.in into the top input box and clicking 'run tests'
|
||||||
|
should (after hanging your browser for a few seconds) yield something
|
||||||
|
approximating to tests.expected in the second box.
|
243
test/z80/test.html
Normal file
243
test/z80/test.html
Normal file
|
@ -0,0 +1,243 @@
|
||||||
|
<!DOCTYPE HTML>
|
||||||
|
<!--
|
||||||
|
test.html: Test harness for the JSSpeccy Z80 core
|
||||||
|
|
||||||
|
Copyright (C) 2009 Matthew Westcott
|
||||||
|
|
||||||
|
This program is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
Contact details: <matthew@west.co.tt>
|
||||||
|
Matthew Westcott, 14 Daisy Hill Drive, Adlington, Chorley, Lancs PR6 9NE UNITED KINGDOM
|
||||||
|
-->
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>jsspeccy test</title>
|
||||||
|
<script>var global = {}</script>
|
||||||
|
<script src="../../src/cpu/z80.js"></script>
|
||||||
|
<script type="text/javascript">/* <![CDATA[ */
|
||||||
|
var testInputLines;
|
||||||
|
var currentLine;
|
||||||
|
|
||||||
|
global.buildZ80({
|
||||||
|
applyContention: true
|
||||||
|
});
|
||||||
|
|
||||||
|
var Memory = function() {
|
||||||
|
var self = {};
|
||||||
|
|
||||||
|
var mem;
|
||||||
|
var initialMem;
|
||||||
|
|
||||||
|
self.clear = function() {
|
||||||
|
mem = new Uint8Array(0x10000);
|
||||||
|
for (var i = 0; i < 0x10000; i += 4) {
|
||||||
|
mem[i] = 0xde; mem[i+1] = 0xad; mem[i+2] = 0xbe; mem[i+3] = 0xef;
|
||||||
|
}
|
||||||
|
initialMem = new Uint8Array(0x10000);
|
||||||
|
for (var i = 0; i < 0x10000; i += 4) {
|
||||||
|
initialMem[i] = 0xde; initialMem[i+1] = 0xad; initialMem[i+2] = 0xbe; initialMem[i+3] = 0xef;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
self.isContended = function(addr) {
|
||||||
|
return ((addr & 0xc000) == 0x4000);
|
||||||
|
}
|
||||||
|
|
||||||
|
self.contend = function(addr, tstate) {
|
||||||
|
if (self.oncontend) self.oncontend(addr);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
self.read = function(addr) {
|
||||||
|
var val = mem[addr];
|
||||||
|
if (self.onread) self.onread(addr, val);
|
||||||
|
return val;
|
||||||
|
}
|
||||||
|
self.write = function(addr, val) {
|
||||||
|
mem[addr] = val;
|
||||||
|
if (self.onwrite) self.onwrite(addr, val);
|
||||||
|
}
|
||||||
|
self.initialWrite = function(addr, val) {
|
||||||
|
mem[addr] = val;
|
||||||
|
initialMem[addr] = val;
|
||||||
|
}
|
||||||
|
self.dump = function() {
|
||||||
|
for (i = 0; i < mem.length; i++) {
|
||||||
|
if((initialMem[i]) != (mem[i])) {
|
||||||
|
var changeReport = hexWord(i);
|
||||||
|
while ((initialMem[i]) != (mem[i]) && i < mem.length) {
|
||||||
|
changeReport += ' ' + hexByte(mem[i]);
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
dump(changeReport + ' -1');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
var IOBus = function() {
|
||||||
|
var self = {};
|
||||||
|
|
||||||
|
self.read = function(addr) {
|
||||||
|
var val = addr >> 8;
|
||||||
|
if (self.onread) self.onread(addr, val);
|
||||||
|
return val;
|
||||||
|
}
|
||||||
|
self.write = function(addr, val) {
|
||||||
|
if (self.onwrite) self.onwrite(addr, val);
|
||||||
|
}
|
||||||
|
self.isULAPort = function(addr) {
|
||||||
|
return ((addr & 0x0001) == 0x0000);
|
||||||
|
}
|
||||||
|
self.contend = function(addr) {
|
||||||
|
if (self.oncontend) self.oncontend(addr);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
memory = Memory();
|
||||||
|
ioBus = IOBus();
|
||||||
|
z80 = global.Z80({
|
||||||
|
display: {},
|
||||||
|
memory: memory,
|
||||||
|
ioBus: ioBus
|
||||||
|
});
|
||||||
|
memory.oncontend = function(addr) {
|
||||||
|
dump((' ' + z80.getTstates()).substr(-5) + ' MC ' + hexWord(addr));
|
||||||
|
}
|
||||||
|
memory.onread = function(addr, val) {
|
||||||
|
dump((' ' + z80.getTstates()).substr(-5) + ' MR ' + hexWord(addr) + ' ' + hexByte(val));
|
||||||
|
}
|
||||||
|
memory.onwrite = function(addr, val) {
|
||||||
|
dump((' ' + z80.getTstates()).substr(-5) + ' MW ' + hexWord(addr) + ' ' + hexByte(val));
|
||||||
|
}
|
||||||
|
ioBus.onread = function(addr, val) {
|
||||||
|
dump((' ' + z80.getTstates()).substr(-5) + ' PR ' + hexWord(addr) + ' ' + hexByte(val));
|
||||||
|
}
|
||||||
|
ioBus.onwrite = function(addr, val) {
|
||||||
|
dump((' ' + z80.getTstates()).substr(-5) + ' PW ' + hexWord(addr) + ' ' + hexByte(val));
|
||||||
|
}
|
||||||
|
ioBus.oncontend = function(addr) {
|
||||||
|
dump((' ' + z80.getTstates()).substr(-5) + ' PC ' + hexWord(addr));
|
||||||
|
}
|
||||||
|
|
||||||
|
function runTests() {
|
||||||
|
testInput = document.getElementById('test_in').value;
|
||||||
|
testInputLines = testInput.split(/\n/);
|
||||||
|
currentLine = 0;
|
||||||
|
document.getElementById('test_out').value = '';
|
||||||
|
while (!eof()) {
|
||||||
|
/* skip past all blank lines and consume the first non-blank line */
|
||||||
|
if ((name = readLine()) == '') continue;
|
||||||
|
dump(name);
|
||||||
|
var mainRegs = readLine().split(/\s+/); /* AF BC DE HL AF' BC' DE' HL' IX IY SP PC */
|
||||||
|
var af = parseInt(mainRegs[0], 16); z80.setAF(af);
|
||||||
|
var bc = parseInt(mainRegs[1], 16); z80.setBC(bc);
|
||||||
|
var de = parseInt(mainRegs[2], 16); z80.setDE(de);
|
||||||
|
var hl = parseInt(mainRegs[3], 16); z80.setHL(hl);
|
||||||
|
var af_ = parseInt(mainRegs[4], 16); z80.setAF_(af_);
|
||||||
|
var bc_ = parseInt(mainRegs[5], 16); z80.setBC_(bc_);
|
||||||
|
var de_ = parseInt(mainRegs[6], 16); z80.setDE_(de_);
|
||||||
|
var hl_ = parseInt(mainRegs[7], 16); z80.setHL_(hl_);
|
||||||
|
var ix = parseInt(mainRegs[8], 16); z80.setIX(ix);
|
||||||
|
var iy = parseInt(mainRegs[9], 16); z80.setIY(iy);
|
||||||
|
z80.setSP(parseInt(mainRegs[10], 16));
|
||||||
|
z80.setPC(parseInt(mainRegs[11], 16));
|
||||||
|
var otherRegs = readLine().split(/\s+/); /* I R IFF1 IFF2 IM <halted> <tstates> */
|
||||||
|
z80.setI(parseInt(otherRegs[0], 16));
|
||||||
|
z80.setR(parseInt(otherRegs[1], 16));
|
||||||
|
z80.setIFF1(parseInt(otherRegs[2], 16));
|
||||||
|
z80.setIFF2(parseInt(otherRegs[3], 16));
|
||||||
|
z80.setIM(parseInt(otherRegs[4], 16));
|
||||||
|
z80.setHalted(otherRegs[5] == '1');
|
||||||
|
var runTime = parseInt(otherRegs[6]);
|
||||||
|
|
||||||
|
memory.clear();
|
||||||
|
|
||||||
|
while ((memLine = readLine()) != '-1') {
|
||||||
|
var memWrites = memLine.split(/\s+/);
|
||||||
|
var addr = parseInt(memWrites.shift(), 16);
|
||||||
|
for (var i = 0; i < memWrites.length; i++) {
|
||||||
|
var byte = memWrites[i];
|
||||||
|
if (byte != '-1') {
|
||||||
|
memory.initialWrite(addr++, parseInt(byte, 16));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
z80.setTstates(0);
|
||||||
|
z80.runFrame(runTime);
|
||||||
|
|
||||||
|
/* AF BC DE HL AF' BC' DE' HL' IX IY SP PC */
|
||||||
|
dump(
|
||||||
|
hexWord(z80.getAF()) + ' '
|
||||||
|
+ hexWord(z80.getBC()) + ' '
|
||||||
|
+ hexWord(z80.getDE()) + ' '
|
||||||
|
+ hexWord(z80.getHL()) + ' '
|
||||||
|
+ hexWord(z80.getAF_()) + ' '
|
||||||
|
+ hexWord(z80.getBC_()) + ' '
|
||||||
|
+ hexWord(z80.getDE_()) + ' '
|
||||||
|
+ hexWord(z80.getHL_()) + ' '
|
||||||
|
+ hexWord(z80.getIX()) + ' '
|
||||||
|
+ hexWord(z80.getIY()) + ' '
|
||||||
|
+ hexWord(z80.getSP()) + ' '
|
||||||
|
+ hexWord(z80.getPC()) + ' '
|
||||||
|
);
|
||||||
|
/* I R IFF1 IFF2 IM <halted> <tstates> */
|
||||||
|
dump(
|
||||||
|
hexByte(z80.getI()) + ' ' + hexByte(z80.getR()) + ' '
|
||||||
|
+ z80.getIFF1() + ' ' + z80.getIFF2() + ' ' + z80.getIM() + ' ' + (z80.getHalted() ? '1' : '0') + ' ' + z80.getTstates()
|
||||||
|
)
|
||||||
|
/* dump memory state */
|
||||||
|
memory.dump();
|
||||||
|
dump('');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
function eof() {
|
||||||
|
return (currentLine >= testInputLines.length);
|
||||||
|
}
|
||||||
|
function readLine() {
|
||||||
|
return testInputLines[currentLine++];
|
||||||
|
}
|
||||||
|
function dump(line) {
|
||||||
|
document.getElementById('test_out').value += (line + "\n");
|
||||||
|
}
|
||||||
|
function hexByte(num) {
|
||||||
|
return ('00' + num.toString(16)).substr(-2);
|
||||||
|
}
|
||||||
|
function hexWord(num) {
|
||||||
|
return ('0000' + num.toString(16)).substr(-4);
|
||||||
|
}
|
||||||
|
|
||||||
|
window.onload = function() {
|
||||||
|
var request = new XMLHttpRequest();
|
||||||
|
request.addEventListener('load', function(e) {
|
||||||
|
data = request.response;
|
||||||
|
document.getElementById('test_in').value = data;
|
||||||
|
});
|
||||||
|
request.open('GET', 'tests.in', true);
|
||||||
|
request.send();
|
||||||
|
}
|
||||||
|
/* ]]> */</script>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<textarea rows="24" cols="80" id="test_in"></textarea>
|
||||||
|
<textarea rows="24" cols="80" id="test_out"></textarea>
|
||||||
|
<input type="button" value="run tests" onclick="runTests()" />
|
||||||
|
</body>
|
||||||
|
</html>
|
18394
test/z80/tests.expected
Normal file
18394
test/z80/tests.expected
Normal file
File diff suppressed because it is too large
Load Diff
9011
test/z80/tests.in
Normal file
9011
test/z80/tests.in
Normal file
File diff suppressed because it is too large
Load Diff
210
test/z80/z80test.js
Normal file
210
test/z80/z80test.js
Normal file
|
@ -0,0 +1,210 @@
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
require('../../src/cpu/z80.js');
|
||||||
|
|
||||||
|
global.buildZ80({
|
||||||
|
applyContention: true
|
||||||
|
});
|
||||||
|
|
||||||
|
var Memory = function(dump) {
|
||||||
|
var self = {};
|
||||||
|
|
||||||
|
var mem;
|
||||||
|
var initialMem;
|
||||||
|
|
||||||
|
self.clear = function() {
|
||||||
|
mem = new Uint8Array(0x10000);
|
||||||
|
for (var i = 0; i < 0x10000; i += 4) {
|
||||||
|
mem[i] = 0xde; mem[i+1] = 0xad; mem[i+2] = 0xbe; mem[i+3] = 0xef;
|
||||||
|
}
|
||||||
|
initialMem = new Uint8Array(0x10000);
|
||||||
|
for (var i = 0; i < 0x10000; i += 4) {
|
||||||
|
initialMem[i] = 0xde; initialMem[i+1] = 0xad; initialMem[i+2] = 0xbe; initialMem[i+3] = 0xef;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
self.isContended = function(addr) {
|
||||||
|
return ((addr & 0xc000) == 0x4000);
|
||||||
|
}
|
||||||
|
|
||||||
|
self.contend = function(addr, tstate) {
|
||||||
|
if (self.oncontend) self.oncontend(addr);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
self.read = function(addr) {
|
||||||
|
var val = mem[addr];
|
||||||
|
if (self.onread) self.onread(addr, val);
|
||||||
|
return val;
|
||||||
|
}
|
||||||
|
self.write = function(addr, val) {
|
||||||
|
mem[addr] = val;
|
||||||
|
if (self.onwrite) self.onwrite(addr, val);
|
||||||
|
}
|
||||||
|
self.initialWrite = function(addr, val) {
|
||||||
|
mem[addr] = val;
|
||||||
|
initialMem[addr] = val;
|
||||||
|
}
|
||||||
|
self.dump = function() {
|
||||||
|
for (var i = 0; i < mem.length; i++) {
|
||||||
|
if((initialMem[i]) != (mem[i])) {
|
||||||
|
var changeReport = hexWord(i);
|
||||||
|
while ((initialMem[i]) != (mem[i]) && i < mem.length) {
|
||||||
|
changeReport += ' ' + hexByte(mem[i]);
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
dump(changeReport + ' -1');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
var IOBus = function() {
|
||||||
|
var self = {};
|
||||||
|
|
||||||
|
self.read = function(addr) {
|
||||||
|
var val = addr >> 8;
|
||||||
|
if (self.onread) self.onread(addr, val);
|
||||||
|
return val;
|
||||||
|
}
|
||||||
|
self.write = function(addr, val) {
|
||||||
|
if (self.onwrite) self.onwrite(addr, val);
|
||||||
|
}
|
||||||
|
self.isULAPort = function(addr) {
|
||||||
|
return ((addr & 0x0001) == 0x0000);
|
||||||
|
}
|
||||||
|
self.contend = function(addr) {
|
||||||
|
if (self.oncontend) self.oncontend(addr);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
function runTest(input, expected) {
|
||||||
|
|
||||||
|
var output = "";
|
||||||
|
function dump(line) {
|
||||||
|
output += line + "\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
var memory = Memory(dump);
|
||||||
|
var ioBus = IOBus();
|
||||||
|
var z80 = global.Z80({
|
||||||
|
display: {},
|
||||||
|
memory: memory,
|
||||||
|
ioBus: ioBus
|
||||||
|
});
|
||||||
|
memory.oncontend = function(addr) {
|
||||||
|
dump((' ' + z80.getTstates()).substr(-5) + ' MC ' + hexWord(addr));
|
||||||
|
}
|
||||||
|
memory.onread = function(addr, val) {
|
||||||
|
dump((' ' + z80.getTstates()).substr(-5) + ' MR ' + hexWord(addr) + ' ' + hexByte(val));
|
||||||
|
}
|
||||||
|
memory.onwrite = function(addr, val) {
|
||||||
|
dump((' ' + z80.getTstates()).substr(-5) + ' MW ' + hexWord(addr) + ' ' + hexByte(val));
|
||||||
|
}
|
||||||
|
ioBus.onread = function(addr, val) {
|
||||||
|
dump((' ' + z80.getTstates()).substr(-5) + ' PR ' + hexWord(addr) + ' ' + hexByte(val));
|
||||||
|
}
|
||||||
|
ioBus.onwrite = function(addr, val) {
|
||||||
|
dump((' ' + z80.getTstates()).substr(-5) + ' PW ' + hexWord(addr) + ' ' + hexByte(val));
|
||||||
|
}
|
||||||
|
ioBus.oncontend = function(addr) {
|
||||||
|
dump((' ' + z80.getTstates()).substr(-5) + ' PC ' + hexWord(addr));
|
||||||
|
}
|
||||||
|
|
||||||
|
var lines = input.split("\n");
|
||||||
|
var mainRegs = lines[1].split(/\s+/); /* AF BC DE HL AF' BC' DE' HL' IX IY SP PC */
|
||||||
|
var af = parseInt(mainRegs[0], 16); z80.setAF(af);
|
||||||
|
var bc = parseInt(mainRegs[1], 16); z80.setBC(bc);
|
||||||
|
var de = parseInt(mainRegs[2], 16); z80.setDE(de);
|
||||||
|
var hl = parseInt(mainRegs[3], 16); z80.setHL(hl);
|
||||||
|
var af_ = parseInt(mainRegs[4], 16); z80.setAF_(af_);
|
||||||
|
var bc_ = parseInt(mainRegs[5], 16); z80.setBC_(bc_);
|
||||||
|
var de_ = parseInt(mainRegs[6], 16); z80.setDE_(de_);
|
||||||
|
var hl_ = parseInt(mainRegs[7], 16); z80.setHL_(hl_);
|
||||||
|
var ix = parseInt(mainRegs[8], 16); z80.setIX(ix);
|
||||||
|
var iy = parseInt(mainRegs[9], 16); z80.setIY(iy);
|
||||||
|
z80.setSP(parseInt(mainRegs[10], 16));
|
||||||
|
z80.setPC(parseInt(mainRegs[11], 16));
|
||||||
|
var otherRegs = lines[2].split(/\s+/); /* I R IFF1 IFF2 IM <halted> <tstates> */
|
||||||
|
z80.setI(parseInt(otherRegs[0], 16));
|
||||||
|
z80.setR(parseInt(otherRegs[1], 16));
|
||||||
|
z80.setIFF1(parseInt(otherRegs[2], 16));
|
||||||
|
z80.setIFF2(parseInt(otherRegs[3], 16));
|
||||||
|
z80.setIM(parseInt(otherRegs[4], 16));
|
||||||
|
z80.setHalted(otherRegs[5] == '1');
|
||||||
|
var runTime = parseInt(otherRegs[6]);
|
||||||
|
|
||||||
|
memory.clear();
|
||||||
|
|
||||||
|
for (var j=3; j<lines.length; j++) {
|
||||||
|
var memLine = lines[j];
|
||||||
|
var memWrites = memLine.split(/\s+/);
|
||||||
|
var addr = parseInt(memWrites.shift(), 16);
|
||||||
|
for (var i = 0; i < memWrites.length; i++) {
|
||||||
|
var byte = memWrites[i];
|
||||||
|
if (byte != '-1') {
|
||||||
|
memory.initialWrite(addr++, parseInt(byte, 16));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
dump(lines[0]);
|
||||||
|
z80.setTstates(0);
|
||||||
|
z80.runFrame(runTime);
|
||||||
|
|
||||||
|
/* AF BC DE HL AF' BC' DE' HL' IX IY SP PC */
|
||||||
|
dump(
|
||||||
|
hexWord(z80.getAF()) + ' '
|
||||||
|
+ hexWord(z80.getBC()) + ' '
|
||||||
|
+ hexWord(z80.getDE()) + ' '
|
||||||
|
+ hexWord(z80.getHL()) + ' '
|
||||||
|
+ hexWord(z80.getAF_()) + ' '
|
||||||
|
+ hexWord(z80.getBC_()) + ' '
|
||||||
|
+ hexWord(z80.getDE_()) + ' '
|
||||||
|
+ hexWord(z80.getHL_()) + ' '
|
||||||
|
+ hexWord(z80.getIX()) + ' '
|
||||||
|
+ hexWord(z80.getIY()) + ' '
|
||||||
|
+ hexWord(z80.getSP()) + ' '
|
||||||
|
+ hexWord(z80.getPC())
|
||||||
|
);
|
||||||
|
/* I R IFF1 IFF2 IM <halted> <tstates> */
|
||||||
|
dump(
|
||||||
|
hexByte(z80.getI()) + ' ' + hexByte(z80.getR()) + ' '
|
||||||
|
+ z80.getIFF1() + ' ' + z80.getIFF2() + ' ' + z80.getIM() + ' ' + (z80.getHalted() ? '1' : '0') + ' ' + z80.getTstates()
|
||||||
|
)
|
||||||
|
/* dump memory state */
|
||||||
|
memory.dump();
|
||||||
|
dump('');
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
|
||||||
|
function hexByte(num) {
|
||||||
|
return ('00' + num.toString(16)).substr(-2);
|
||||||
|
}
|
||||||
|
function hexWord(num) {
|
||||||
|
return ('0000' + num.toString(16)).substr(-4);
|
||||||
|
}
|
||||||
|
|
||||||
|
///
|
||||||
|
|
||||||
|
var fs = require('fs');
|
||||||
|
var assert = require('assert');
|
||||||
|
var testsIn = fs.readFileSync('test/z80/tests.in', {encoding:'utf8'}).split('\n\n');
|
||||||
|
var testsExpected = fs.readFileSync('test/z80/tests.expected', {encoding:'utf8'}).split('\n\n');
|
||||||
|
assert(testsIn.length == testsExpected.length);
|
||||||
|
|
||||||
|
describe('Z80 CPU', function() {
|
||||||
|
it('should execute test cases', function() {
|
||||||
|
for (var iter=0; iter<testsIn.length; iter++) {
|
||||||
|
var fn = function(index, input, expected) {
|
||||||
|
var output = runTest(input);
|
||||||
|
assert.equal(output.trim(), expected.trim());
|
||||||
|
}.call(this, iter, testsIn[iter], testsExpected[iter]);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
Loading…
Reference in New Issue
Block a user