mirror of
https://github.com/sehugg/8bitworkshop.git
synced 2026-04-21 21:16:51 +00:00
moved TS test files from src/test to test/unit
This commit is contained in:
@@ -0,0 +1,116 @@
|
||||
import { describe } from "mocha";
|
||||
import { OpcodeMetadata, Platform } from "../../src/common/baseplatform";
|
||||
import { CodeAnalyzer_vcs } from "../../src/common/analysis";
|
||||
import { MOS6502 } from "../../src/common/cpu/MOS6502";
|
||||
import assert from "assert";
|
||||
|
||||
class Test6502Platform implements Platform {
|
||||
cpu = new MOS6502();
|
||||
ram = new Uint8Array(65536);
|
||||
|
||||
start(): void | Promise<void> {
|
||||
throw new Error("Method not implemented.");
|
||||
}
|
||||
reset(): void {
|
||||
throw new Error("Method not implemented.");
|
||||
}
|
||||
isRunning(): boolean {
|
||||
throw new Error("Method not implemented.");
|
||||
}
|
||||
getToolForFilename(s: string): string {
|
||||
throw new Error("Method not implemented.");
|
||||
}
|
||||
getDefaultExtension(): string {
|
||||
throw new Error("Method not implemented.");
|
||||
}
|
||||
pause(): void {
|
||||
throw new Error("Method not implemented.");
|
||||
}
|
||||
resume(): void {
|
||||
throw new Error("Method not implemented.");
|
||||
}
|
||||
loadROM(title: string, rom: any) {
|
||||
throw new Error("Method not implemented.");
|
||||
}
|
||||
getOpcodeMetadata?(opcode: number, offset: number): OpcodeMetadata {
|
||||
return this.cpu.cpu.getOpcodeMetadata(opcode, offset);
|
||||
}
|
||||
getOriginPC(): number {
|
||||
return 0;
|
||||
}
|
||||
readAddress?(addr:number) : number {
|
||||
return this.ram[addr] || 0;
|
||||
}
|
||||
}
|
||||
|
||||
describe('6502 analysis', function () {
|
||||
it('Should analyze WSYNC', function () {
|
||||
let platform = new Test6502Platform();
|
||||
platform.ram.set([0xea,0x85,0x02,0xa9,0x60,0x20,0x04,0x00,0xea,0x4c,0x01,0x00]);
|
||||
let analysis = new CodeAnalyzer_vcs(platform);
|
||||
analysis.showLoopTimingForPC(0x0);
|
||||
console.log(analysis.pc2clockrange);
|
||||
assert.equal(analysis.pc2clockrange[0x0].minclocks, 0);
|
||||
assert.equal(analysis.pc2clockrange[0x0].maxclocks, 0);
|
||||
assert.equal(analysis.pc2clockrange[0x1].minclocks, 2);
|
||||
assert.equal(analysis.pc2clockrange[0x1].maxclocks, 19);
|
||||
assert.equal(analysis.pc2clockrange[0x3].minclocks, 0);
|
||||
assert.equal(analysis.pc2clockrange[0x3].maxclocks, 0);
|
||||
});
|
||||
it('Should analyze loop', function () {
|
||||
let platform = new Test6502Platform();
|
||||
platform.ram.set([0xea,0x4c,0x00,0x00]); // 5 cycles
|
||||
let analysis = new CodeAnalyzer_vcs(platform);
|
||||
analysis.showLoopTimingForPC(0x0);
|
||||
console.log(analysis.pc2clockrange);
|
||||
assert.equal(analysis.pc2clockrange[0x0].minclocks, 0);
|
||||
assert.equal(analysis.pc2clockrange[0x0].maxclocks, 75);
|
||||
// TODO: should be 0-75
|
||||
/*
|
||||
assert.equal(analysis.pc2clockrange[0x1].minclocks, 1);
|
||||
assert.equal(analysis.pc2clockrange[0x1].maxclocks, 72);
|
||||
*/
|
||||
});
|
||||
it('Should wrap clocks', function () {
|
||||
let platform = new Test6502Platform();
|
||||
platform.ram.set([0xb1,0,0xb1,0,0xb1,0,0xb1,0,0xb1,0,0xb1,0,0xb1,0,0xb1,0,0xb1,0,0xb1,0,0xb1,0,0xb1,0,0xb1,0,0xb1,0,0xea,0x4c,0,0]);
|
||||
let analysis = new CodeAnalyzer_vcs(platform);
|
||||
analysis.showLoopTimingForPC(0x0);
|
||||
console.log(analysis.pc2clockrange);
|
||||
// TODO: should be 0-75
|
||||
assert.equal(analysis.pc2clockrange[0x0].minclocks, 0);
|
||||
assert.equal(analysis.pc2clockrange[0x0].maxclocks, 75);
|
||||
assert.equal(analysis.pc2clockrange[0x2].minclocks, 5);
|
||||
assert.equal(analysis.pc2clockrange[0x2].maxclocks, 6);
|
||||
});
|
||||
it('Should wrap RTS', function () {
|
||||
let platform = new Test6502Platform();
|
||||
platform.ram.set([0xb1,0x60,0x20,1,0,0x4c,0,0]);
|
||||
let analysis = new CodeAnalyzer_vcs(platform);
|
||||
analysis.showLoopTimingForPC(0x0);
|
||||
console.log(analysis.pc2clockrange);
|
||||
// TODO: should be 0-75
|
||||
assert.equal(analysis.pc2clockrange[0x0].minclocks, 0);
|
||||
assert.equal(analysis.pc2clockrange[0x0].maxclocks, 75);
|
||||
});
|
||||
it('Should not recurse', function () {
|
||||
let platform = new Test6502Platform();
|
||||
platform.ram.set([0xea,0x20,0x07,0x00,0x4c,0x00,0x00, 0xa4,0x88,0xea,0xd0,0xfb,0x60]);
|
||||
let analysis = new CodeAnalyzer_vcs(platform);
|
||||
analysis.showLoopTimingForPC(0x0);
|
||||
console.log(analysis.pc2clockrange);
|
||||
// TODO: should be 0-75
|
||||
assert.equal(analysis.pc2clockrange[0x0].minclocks, 0);
|
||||
assert.equal(analysis.pc2clockrange[0x0].maxclocks, 75);
|
||||
});
|
||||
it('Should not break', function () {
|
||||
let platform = new Test6502Platform();
|
||||
platform.ram.set([0x85,0x02,0x85,0x2a,0xa9,0x00,0x85,0x26,0x85,0x1b,0x85,0x1c,0x4c,0x00,0x00]);
|
||||
let analysis = new CodeAnalyzer_vcs(platform);
|
||||
analysis.showLoopTimingForPC(0x0);
|
||||
console.log(analysis.pc2clockrange);
|
||||
// TODO: should be 0-75
|
||||
assert.equal(analysis.pc2clockrange[0x0].minclocks, 0);
|
||||
assert.equal(analysis.pc2clockrange[0x0].maxclocks, 17);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,596 @@
|
||||
import { describe, it } from "mocha";
|
||||
import assert from "assert";
|
||||
import { Assembler } from "../../src/worker/assembler";
|
||||
|
||||
describe('Assembler', function () {
|
||||
|
||||
describe('Basic Assembly', function () {
|
||||
it('Should assemble simple 8-bit instructions', function () {
|
||||
const spec = {
|
||||
name: 'test8',
|
||||
width: 8,
|
||||
vars: {
|
||||
reg: { bits: 3, toks: ['r0', 'r1', 'r2', 'r3', 'r4', 'r5', 'r6', 'r7'] },
|
||||
imm8: { bits: 8 }
|
||||
},
|
||||
rules: [
|
||||
{ fmt: 'nop', bits: ['00000000'] },
|
||||
{ fmt: 'mov ~reg,~reg', bits: ['10', 0, 1] },
|
||||
{ fmt: 'add ~reg,~imm8', bits: ['00001', 0, 1] }
|
||||
]
|
||||
};
|
||||
|
||||
const asm = new Assembler(spec);
|
||||
asm.assemble('.org 0');
|
||||
asm.assemble('.len 256');
|
||||
asm.assemble('nop');
|
||||
asm.assemble('mov r1,r2');
|
||||
asm.assemble('add r3,$42');
|
||||
|
||||
const state = asm.finish();
|
||||
assert.equal(state.errors.length, 0, 'Should have no errors');
|
||||
assert.equal(state.output[0], 0x00, 'NOP should be 0x00');
|
||||
assert.equal(state.output[1], 0b10001010, 'MOV r1,r2');
|
||||
assert.equal(state.output[2], 0b00001011, 'ADD r3,imm first byte');
|
||||
assert.equal(state.output[3], 0x42, 'ADD r3,imm second bytes');
|
||||
});
|
||||
|
||||
it('Should handle labels', function () {
|
||||
const spec = {
|
||||
name: 'test8',
|
||||
width: 8,
|
||||
vars: {
|
||||
imm8: { bits: 8 }
|
||||
},
|
||||
rules: [
|
||||
{ fmt: 'jmp ~imm8', bits: ['11110000', 0] }
|
||||
]
|
||||
};
|
||||
|
||||
const asm = new Assembler(spec);
|
||||
asm.assemble('.org 0');
|
||||
asm.assemble('.len 256');
|
||||
asm.assemble('jmp target');
|
||||
asm.assemble('nop: jmp nop');
|
||||
asm.assemble('target: jmp target');
|
||||
|
||||
const state = asm.finish();
|
||||
assert.equal(state.errors.length, 0, 'Should have no errors');
|
||||
assert.equal(state.output[0], 0xf0, 'JMP opcode');
|
||||
assert.equal(state.output[1], 4, 'Should jump to address 4');
|
||||
assert.equal(state.output[2], 0xf0, 'JMP opcode');
|
||||
assert.equal(state.output[3], 2, 'Should jump to itself (address 2)');
|
||||
assert.equal(state.output[4], 0xf0, 'JMP opcode');
|
||||
assert.equal(state.output[5], 4, 'Should jump to itself (address 4)');
|
||||
});
|
||||
});
|
||||
|
||||
describe('PC-Relative Addressing', function () {
|
||||
it('Should handle simple PC-relative branches', function () {
|
||||
const spec = {
|
||||
name: 'test8',
|
||||
width: 8,
|
||||
vars: {
|
||||
rel8: { bits: 8, iprel: true, ipofs: 0, ipmul: 1 }
|
||||
},
|
||||
rules: [
|
||||
{ fmt: 'br ~rel8', bits: ['10000000', 0] }
|
||||
]
|
||||
};
|
||||
|
||||
const asm = new Assembler(spec);
|
||||
asm.assemble('.org 0');
|
||||
asm.assemble('.len 256');
|
||||
asm.assemble('br forward'); // offset 0
|
||||
asm.assemble('br forward'); // offset 2
|
||||
asm.assemble('br forward'); // offset 4
|
||||
asm.assemble('forward: br forward'); // offset 6
|
||||
|
||||
const state = asm.finish();
|
||||
assert.equal(state.errors.length, 0, 'Should have no errors');
|
||||
assert.equal(state.output[1], 6, 'First branch offset should be 6');
|
||||
assert.equal(state.output[3], 4, 'Second branch offset should be 4');
|
||||
assert.equal(state.output[5], 2, 'Third branch offset should be 2');
|
||||
assert.equal(state.output[7], 0, 'Fourth branch offset should be 0 (self)');
|
||||
});
|
||||
|
||||
it('Should handle PC-relative with instruction multiplier', function () {
|
||||
// Simulate word-addressed architecture where PC increments by 4
|
||||
const spec = {
|
||||
name: 'test32',
|
||||
width: 32,
|
||||
vars: {
|
||||
rel13: { bits: 13, iprel: true, ipofs: 0, ipmul: 4 }
|
||||
},
|
||||
rules: [
|
||||
{ fmt: 'beq ~rel13', bits: ['1100011000000000000', 0] }
|
||||
]
|
||||
};
|
||||
|
||||
const asm = new Assembler(spec);
|
||||
asm.assemble('.org 0');
|
||||
asm.assemble('.len 256');
|
||||
asm.assemble('beq target'); // offset 0
|
||||
asm.assemble('beq target'); // offset 4
|
||||
asm.assemble('target: beq target'); // offset 8
|
||||
|
||||
const state = asm.finish();
|
||||
assert.equal(state.errors.length, 0, 'Should have no errors');
|
||||
// PC-relative offset = (target - current) * ipmul
|
||||
// First: (8 - 0) * 1 = 8
|
||||
// Second: (8 - 4) * 1 = 4
|
||||
// Third: (8 - 8) * 1 = 0
|
||||
const first = state.output[0];
|
||||
const second = state.output[1];
|
||||
const third = state.output[2];
|
||||
|
||||
// Extract the 13-bit immediate from the instruction
|
||||
// It's in the lower 13 bits
|
||||
const offset1 = first & 0x1fff;
|
||||
const offset2 = second & 0x1fff;
|
||||
const offset3 = third & 0x1fff;
|
||||
|
||||
assert.equal(offset1, 8, 'First branch offset should be 8');
|
||||
assert.equal(offset2, 4, 'Second branch offset should be 4');
|
||||
assert.equal(offset3, 0, 'Third branch offset should be 0');
|
||||
});
|
||||
});
|
||||
|
||||
describe('Bit Slicing', function () {
|
||||
it('Should extract bit slices correctly', function () {
|
||||
// RISC-V style branch with scrambled immediate
|
||||
const spec = {
|
||||
name: 'riscv',
|
||||
width: 32,
|
||||
vars: {
|
||||
reg: { bits: 5, toks: ['x0', 'x1', 'x2', 'x3', 'x4', 'x5', 'x6', 'x7'] },
|
||||
rel13: { bits: 13, iprel: true, ipofs: 0, ipmul: 1 }
|
||||
},
|
||||
rules: [
|
||||
// beq rs1, rs2, offset
|
||||
// Format: imm[12] | imm[10:5] | rs2 | rs1 | 000 | imm[4:1] | imm[11] | 1100011
|
||||
{
|
||||
fmt: 'beq ~reg,~reg,~rel13',
|
||||
bits: [
|
||||
{ a: 2, b: 12, n: 1 }, // imm[12]
|
||||
{ a: 2, b: 5, n: 6 }, // imm[10:5]
|
||||
1, // rs2
|
||||
0, // rs1
|
||||
'000', // funct3
|
||||
{ a: 2, b: 1, n: 4 }, // imm[4:1]
|
||||
{ a: 2, b: 11, n: 1 }, // imm[11]
|
||||
'1100011' // opcode
|
||||
]
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
const asm = new Assembler(spec);
|
||||
asm.assemble('.org 0');
|
||||
asm.assemble('.len 256');
|
||||
asm.assemble('target: beq x1,x2,target'); // Self-branch with offset 0
|
||||
|
||||
const state = asm.finish();
|
||||
assert.equal(state.errors.length, 0, 'Should have no errors');
|
||||
|
||||
const insn = state.output[0];
|
||||
// Decode the instruction to verify bit positions
|
||||
const opcode = insn & 0x7f;
|
||||
const imm_11 = (insn >> 7) & 1;
|
||||
const imm_4_1 = (insn >> 8) & 0xf;
|
||||
const funct3 = (insn >> 12) & 0x7;
|
||||
const rs1 = (insn >> 15) & 0x1f;
|
||||
const rs2 = (insn >> 20) & 0x1f;
|
||||
const imm_10_5 = (insn >> 25) & 0x3f;
|
||||
const imm_12 = (insn >> 31) & 1;
|
||||
|
||||
assert.equal(opcode, 0x63, 'Opcode should be 0x63 (branch)');
|
||||
assert.equal(funct3, 0, 'funct3 should be 0 (BEQ)');
|
||||
assert.equal(rs1, 1, 'rs1 should be 1 (x1)');
|
||||
assert.equal(rs2, 2, 'rs2 should be 2 (x2)');
|
||||
|
||||
// All immediate bits should be 0 for self-branch
|
||||
assert.equal(imm_12, 0, 'imm[12] should be 0');
|
||||
assert.equal(imm_11, 0, 'imm[11] should be 0');
|
||||
assert.equal(imm_10_5, 0, 'imm[10:5] should be 0');
|
||||
assert.equal(imm_4_1, 0, 'imm[4:1] should be 0');
|
||||
});
|
||||
|
||||
it('Should handle non-zero bit slice offsets', function () {
|
||||
const spec = {
|
||||
name: 'riscv',
|
||||
width: 32,
|
||||
vars: {
|
||||
brop: { bits: 3, toks: ["beq","bne","bx2","bx3","blt","bge","bltu","bgeu"] },
|
||||
reg: { bits: 5, toks: ['x0', 'x1', 'x2'] },
|
||||
rel13: { bits: 13, iprel: true, ipofs: 0, ipmul: 4 }
|
||||
},
|
||||
rules: [
|
||||
{
|
||||
fmt: '~brop ~reg,~reg,~rel13',
|
||||
bits: [
|
||||
{ a: 3, b: 12, n: 1 },
|
||||
{ a: 3, b: 5, n: 6 },
|
||||
2,
|
||||
1,
|
||||
'000',
|
||||
{ a: 3, b: 1, n: 4 },
|
||||
{ a: 3, b: 11, n: 1 },
|
||||
'1100011'
|
||||
]
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
const asm = new Assembler(spec);
|
||||
asm.assemble('.org 4096');
|
||||
asm.assemble('.len 1024');
|
||||
asm.assemble('beq x1,x2,target'); // offset 0
|
||||
asm.assemble('beq x1,x2,target'); // offset 4
|
||||
asm.assemble('beq x1,x2,target'); // offset 8
|
||||
asm.assemble('target: beq x1,x2,target'); // offset 12 (self)
|
||||
asm.assemble('beq x1,x2,target'); // offset 16
|
||||
asm.assemble('beq x1,x2,target'); // offset 20
|
||||
|
||||
/*
|
||||
00208663
|
||||
00208463
|
||||
00208263
|
||||
00208063
|
||||
fe208ee3
|
||||
fe208ce3
|
||||
*/
|
||||
|
||||
const state = asm.finish();
|
||||
assert.equal(state.errors.length, 0, 'Should have no errors');
|
||||
assert.equal(state.output[0], 0x208663, 'insn 0');
|
||||
assert.equal(state.output[1], 0x208463, 'insn 1');
|
||||
assert.equal(state.output[2], 0x208263, 'insn 2');
|
||||
assert.equal(state.output[3], 0x208063, 'insn 3');
|
||||
assert.equal(state.output[4], 0xfe208ee3|0, 'insn 4');
|
||||
assert.equal(state.output[5], 0xfe208ce3|0, 'insn 5');
|
||||
|
||||
// Check that offset 12 was correctly calculated and sliced
|
||||
const insn0 = state.output[0];
|
||||
|
||||
// Reconstruct the immediate from the instruction
|
||||
const imm_11 = (insn0 >> 7) & 1;
|
||||
const imm_4_1 = (insn0 >> 8) & 0xf;
|
||||
const imm_10_5 = (insn0 >> 25) & 0x3f;
|
||||
const imm_12 = (insn0 >> 31) & 1;
|
||||
|
||||
//console.log('insn0: $', insn0.toString(16), 'imm_12:', imm_12, 'imm_11:', imm_11, 'imm_10_5:', imm_10_5, 'imm_4_1:', imm_4_1);
|
||||
|
||||
// Reconstruct the 13-bit signed offset (bit 0 is implicit 0)
|
||||
const offset = (imm_12 << 12) | (imm_11 << 11) | (imm_10_5 << 5) | (imm_4_1 << 1);
|
||||
|
||||
assert.equal(offset, 12, 'Offset should be 12 bytes');
|
||||
});
|
||||
});
|
||||
|
||||
describe('Endianness', function () {
|
||||
it('Should handle little-endian values', function () {
|
||||
const spec = {
|
||||
name: 'test8',
|
||||
width: 8,
|
||||
vars: {
|
||||
imm16: { bits: 16, endian: 'little' as const }
|
||||
},
|
||||
rules: [
|
||||
{ fmt: 'ldi ~imm16', bits: ['10000000', 0] }
|
||||
]
|
||||
};
|
||||
|
||||
const asm = new Assembler(spec);
|
||||
asm.assemble('.org 0');
|
||||
asm.assemble('.len 256');
|
||||
asm.assemble('ldi $1234');
|
||||
|
||||
const state = asm.finish();
|
||||
assert.equal(state.errors.length, 0, 'Should have no errors');
|
||||
assert.equal(state.output[0], 0x80, 'Opcode');
|
||||
assert.equal(state.output[1], 0x34, 'Low byte first (little-endian)');
|
||||
assert.equal(state.output[2], 0x12, 'High byte second');
|
||||
});
|
||||
|
||||
it('Should handle big-endian values', function () {
|
||||
const spec = {
|
||||
name: 'test8',
|
||||
width: 8,
|
||||
vars: {
|
||||
imm16: { bits: 16, endian: 'big' as const }
|
||||
},
|
||||
rules: [
|
||||
{ fmt: 'ldi ~imm16', bits: ['10000000', 0] }
|
||||
]
|
||||
};
|
||||
|
||||
const asm = new Assembler(spec);
|
||||
asm.assemble('.org 0');
|
||||
asm.assemble('.len 256');
|
||||
asm.assemble('ldi $1234');
|
||||
|
||||
const state = asm.finish();
|
||||
assert.equal(state.errors.length, 0, 'Should have no errors');
|
||||
assert.equal(state.output[0], 0x80, 'Opcode');
|
||||
assert.equal(state.output[1], 0x12, 'High byte first (big-endian)');
|
||||
assert.equal(state.output[2], 0x34, 'Low byte second');
|
||||
});
|
||||
});
|
||||
|
||||
describe('Directives', function () {
|
||||
it('Should handle .org directive', function () {
|
||||
const spec = {
|
||||
name: 'test8',
|
||||
width: 8,
|
||||
vars: {},
|
||||
rules: [
|
||||
{ fmt: 'nop', bits: ['00000000'] }
|
||||
]
|
||||
};
|
||||
|
||||
const asm = new Assembler(spec);
|
||||
asm.assemble('.org 100');
|
||||
asm.assemble('.len 256');
|
||||
asm.assemble('nop');
|
||||
|
||||
const state = asm.finish();
|
||||
assert.equal(state.origin, 100, 'Origin should be 100');
|
||||
assert.equal(state.ip, 101, 'IP should be at 101');
|
||||
assert.equal(state.output[0], 0x00, 'NOP at origin');
|
||||
});
|
||||
|
||||
it('Should handle .data directive', function () {
|
||||
const spec = {
|
||||
name: 'test8',
|
||||
width: 8,
|
||||
vars: {},
|
||||
rules: []
|
||||
};
|
||||
|
||||
const asm = new Assembler(spec);
|
||||
asm.assemble('.org 0');
|
||||
asm.assemble('.len 256');
|
||||
asm.assemble('.data 10 20 $30');
|
||||
|
||||
const state = asm.finish();
|
||||
assert.equal(state.output[0], 10);
|
||||
assert.equal(state.output[1], 20);
|
||||
assert.equal(state.output[2], 0x30);
|
||||
});
|
||||
|
||||
it('Should handle .string directive', function () {
|
||||
const spec = {
|
||||
name: 'test8',
|
||||
width: 8,
|
||||
vars: {},
|
||||
rules: []
|
||||
};
|
||||
|
||||
const asm = new Assembler(spec);
|
||||
asm.assemble('.org 0');
|
||||
asm.assemble('.len 256');
|
||||
asm.assemble('.string HELLO');
|
||||
|
||||
const state = asm.finish();
|
||||
assert.equal(state.output[0], 'H'.charCodeAt(0));
|
||||
assert.equal(state.output[1], 'E'.charCodeAt(0));
|
||||
assert.equal(state.output[2], 'L'.charCodeAt(0));
|
||||
assert.equal(state.output[3], 'L'.charCodeAt(0));
|
||||
assert.equal(state.output[4], 'O'.charCodeAt(0));
|
||||
});
|
||||
|
||||
it('Should handle .align directive', function () {
|
||||
const spec = {
|
||||
name: 'test8',
|
||||
width: 8,
|
||||
vars: {},
|
||||
rules: [
|
||||
{ fmt: 'nop', bits: ['00000000'] }
|
||||
]
|
||||
};
|
||||
|
||||
const asm = new Assembler(spec);
|
||||
asm.assemble('.org 0');
|
||||
asm.assemble('.len 256');
|
||||
asm.assemble('nop'); // offset 0
|
||||
asm.assemble('nop'); // offset 1
|
||||
asm.assemble('.align 4'); // align to 4
|
||||
asm.assemble('nop'); // offset 4
|
||||
|
||||
const state = asm.finish();
|
||||
assert.equal(state.lines[2].offset, 4, 'Should align to offset 4');
|
||||
});
|
||||
});
|
||||
|
||||
describe('Error Handling', function () {
|
||||
it('Should detect undefined labels', function () {
|
||||
const spec = {
|
||||
name: 'test8',
|
||||
width: 8,
|
||||
vars: {
|
||||
imm8: { bits: 8 }
|
||||
},
|
||||
rules: [
|
||||
{ fmt: 'jmp ~imm8', bits: ['11110000', 0] }
|
||||
]
|
||||
};
|
||||
|
||||
const asm = new Assembler(spec);
|
||||
asm.assemble('.org 0');
|
||||
asm.assemble('.len 256');
|
||||
asm.assemble('jmp undefined_label');
|
||||
|
||||
const state = asm.finish();
|
||||
assert.equal(state.errors.length, 1, 'Should have one error');
|
||||
assert(state.errors[0].msg.includes('undefined_label'), 'Error should mention undefined_label');
|
||||
});
|
||||
|
||||
it('Should detect value overflow', function () {
|
||||
const spec = {
|
||||
name: 'test8',
|
||||
width: 8,
|
||||
vars: {
|
||||
imm4: { bits: 4 }
|
||||
},
|
||||
rules: [
|
||||
{ fmt: 'mov ~imm4', bits: ['1111', 0] }
|
||||
]
|
||||
};
|
||||
|
||||
const asm = new Assembler(spec);
|
||||
asm.assemble('.org 0');
|
||||
asm.assemble('.len 256');
|
||||
asm.assemble('mov 20'); // 20 > 15 (max 4-bit value)
|
||||
|
||||
const state = asm.finish();
|
||||
assert.equal(state.errors.length, 1, 'Should have one error');
|
||||
assert(state.errors[0].msg.includes('does not fit'), 'Error should mention overflow');
|
||||
});
|
||||
|
||||
it('Should detect invalid instructions', function () {
|
||||
const spec = {
|
||||
name: 'test8',
|
||||
width: 8,
|
||||
vars: {},
|
||||
rules: [
|
||||
{ fmt: 'nop', bits: ['00000000'] }
|
||||
]
|
||||
};
|
||||
|
||||
const asm = new Assembler(spec);
|
||||
asm.assemble('.org 0');
|
||||
asm.assemble('.len 256');
|
||||
asm.assemble('invalid_instruction');
|
||||
|
||||
const state = asm.finish();
|
||||
assert.equal(state.errors.length, 1, 'Should have one error');
|
||||
assert(state.errors[0].msg.includes('Could not decode'), 'Error should mention decode failure');
|
||||
});
|
||||
});
|
||||
|
||||
describe('32-bit Width', function () {
|
||||
it('Should handle 32-bit instructions', function () {
|
||||
const spec = {
|
||||
name: 'test32',
|
||||
width: 32,
|
||||
vars: {
|
||||
reg: { bits: 5, toks: ['r0', 'r1', 'r2', 'r3', 'r4'] },
|
||||
imm12: { bits: 12 }
|
||||
},
|
||||
rules: [
|
||||
// RISC-V ADDI format: imm[11:0] | rs1 | 000 | rd | 0010011
|
||||
{
|
||||
fmt: 'addi ~reg,~reg,~imm12',
|
||||
bits: [2, 1, '000', 0, '0010011']
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
const asm = new Assembler(spec);
|
||||
asm.assemble('.org 0');
|
||||
asm.assemble('.len 256');
|
||||
asm.assemble('addi r1,r2,$123');
|
||||
|
||||
const state = asm.finish();
|
||||
assert.equal(state.errors.length, 0, 'Should have no errors');
|
||||
|
||||
const insn = state.output[0];
|
||||
const opcode = insn & 0x7f;
|
||||
const rd = (insn >> 7) & 0x1f;
|
||||
const funct3 = (insn >> 12) & 0x7;
|
||||
const rs1 = (insn >> 15) & 0x1f;
|
||||
const imm = (insn >> 20) & 0xfff;
|
||||
|
||||
assert.equal(opcode, 0x13, 'Opcode should be 0x13 (OP-IMM)');
|
||||
assert.equal(rd, 1, 'rd should be 1');
|
||||
assert.equal(rs1, 2, 'rs1 should be 2');
|
||||
assert.equal(funct3, 0, 'funct3 should be 0 (ADDI)');
|
||||
assert.equal(imm, 0x123, 'Immediate should be 0x123');
|
||||
});
|
||||
});
|
||||
|
||||
describe('Symbol Definition', function () {
|
||||
it('Should allow symbol definition with .define', function () {
|
||||
const spec = {
|
||||
name: 'test8',
|
||||
width: 8,
|
||||
vars: {
|
||||
imm8: { bits: 8 }
|
||||
},
|
||||
rules: [
|
||||
{ fmt: 'ldi ~imm8', bits: ['10000000', 0] }
|
||||
]
|
||||
};
|
||||
|
||||
const asm = new Assembler(spec);
|
||||
asm.assemble('.org 0');
|
||||
asm.assemble('.len 256');
|
||||
asm.assemble('.define MYCONST 42');
|
||||
asm.assemble('ldi MYCONST');
|
||||
|
||||
const state = asm.finish();
|
||||
assert.equal(state.errors.length, 0, 'Should have no errors');
|
||||
assert.equal(state.output[1], 42, 'Should use defined constant');
|
||||
});
|
||||
});
|
||||
|
||||
describe('Complex Fixups', function () {
|
||||
it('Should handle multiple fixups to same location', function () {
|
||||
const spec = {
|
||||
name: 'test8',
|
||||
width: 8,
|
||||
vars: {
|
||||
imm8: { bits: 8 }
|
||||
},
|
||||
rules: [
|
||||
{ fmt: 'jmp ~imm8', bits: ['11110000', 0] }
|
||||
]
|
||||
};
|
||||
|
||||
const asm = new Assembler(spec);
|
||||
asm.assemble('.org 0');
|
||||
asm.assemble('.len 256');
|
||||
asm.assemble('jmp target');
|
||||
asm.assemble('jmp target');
|
||||
asm.assemble('target: jmp target');
|
||||
|
||||
const state = asm.finish();
|
||||
assert.equal(state.errors.length, 0, 'Should have no errors');
|
||||
assert.equal(state.output[1], 4, 'First jump to target');
|
||||
assert.equal(state.output[3], 4, 'Second jump to target');
|
||||
assert.equal(state.output[5], 4, 'Third jump to itself');
|
||||
});
|
||||
|
||||
it('Should handle backward references', function () {
|
||||
const spec = {
|
||||
name: 'test8',
|
||||
width: 8,
|
||||
vars: {
|
||||
rel8: { bits: 8, iprel: true, ipofs: 0, ipmul: 1 }
|
||||
},
|
||||
rules: [
|
||||
{ fmt: 'br ~rel8', bits: ['10000000', 0] }
|
||||
]
|
||||
};
|
||||
|
||||
const asm = new Assembler(spec);
|
||||
asm.assemble('.org 0');
|
||||
asm.assemble('.len 256');
|
||||
asm.assemble('start: br forward'); // offset 0
|
||||
asm.assemble('br start'); // offset 2 (backward)
|
||||
asm.assemble('forward: br start'); // offset 4 (backward)
|
||||
|
||||
const state = asm.finish();
|
||||
assert.equal(state.errors.length, 0, 'Should have no errors');
|
||||
assert.equal(state.output[1], 4, 'Forward branch');
|
||||
|
||||
// Backward branches: offset = target - current
|
||||
// For offset 2: target=0, current=2, offset=-2 (0xfe in 8-bit signed)
|
||||
assert.equal(state.output[3] & 0xff, 0xfe, 'Backward branch from offset 2');
|
||||
|
||||
// For offset 4: target=0, current=4, offset=-4 (0xfc in 8-bit signed)
|
||||
assert.equal(state.output[5] & 0xff, 0xfc, 'Backward branch from offset 4');
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,190 @@
|
||||
|
||||
import { spawnSync } from "child_process";
|
||||
import { existsSync, readdirSync, readFileSync, writeFileSync } from "fs";
|
||||
import { describe } from "mocha";
|
||||
import { Bin, BoxConstraints, Packer } from "../../src/common/ecs/binpack";
|
||||
import { ECSCompiler } from "../../src/common/ecs/compiler";
|
||||
import { Dialect_CA65, EntityManager, SourceFileExport } from "../../src/common/ecs/ecs";
|
||||
|
||||
function testCompiler() {
|
||||
let em = new EntityManager(new Dialect_CA65()); // TODO
|
||||
let c = new ECSCompiler(em, true);
|
||||
try {
|
||||
c.parseFile(`
|
||||
// comment
|
||||
/*
|
||||
mju,fjeqowfjqewiofjqe
|
||||
*/
|
||||
component Kernel
|
||||
lines: 0..255
|
||||
bgcolor: 0..255
|
||||
end
|
||||
component Bitmap
|
||||
data: array of 0..255
|
||||
end
|
||||
component HasBitmap
|
||||
bitmap: [Bitmap]
|
||||
end
|
||||
|
||||
system SimpleKernel
|
||||
locals 8
|
||||
on preframe do with [Kernel] --- JUNK_AT_END
|
||||
lda #5
|
||||
sta #6
|
||||
Label:
|
||||
---
|
||||
end
|
||||
|
||||
comment ---
|
||||
|
||||
---
|
||||
|
||||
scope Root
|
||||
entity kernel [Kernel]
|
||||
const lines = 0xc0
|
||||
//const lines = $c0
|
||||
end
|
||||
entity nobmp [Bitmap]
|
||||
const data = [4]
|
||||
end
|
||||
entity bmp [Bitmap]
|
||||
const data = [1,2,3]
|
||||
end
|
||||
entity player1 [HasBitmap]
|
||||
init bitmap = #bmp
|
||||
end
|
||||
end
|
||||
|
||||
`, 'foo.txt');
|
||||
//console.log('json', c.em.toJSON());
|
||||
let src = new SourceFileExport();
|
||||
c.exportToFile(src);
|
||||
// TODO: test?
|
||||
console.log(src.toString());
|
||||
return em;
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
for (let err of c.errors) {
|
||||
console.log(err);
|
||||
}
|
||||
console.log(c.tokens);
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: files in markdown?
|
||||
// TODO: jsr OperModeExecutionTree?
|
||||
|
||||
describe('Tokenizer', function() {
|
||||
it('Should use Compiler', function() {
|
||||
testCompiler();
|
||||
});
|
||||
});
|
||||
|
||||
describe('Compiler', function() {
|
||||
let testdir = './test/ecs/';
|
||||
let files = readdirSync(testdir).filter(f => f.endsWith('.ecs'));
|
||||
files.forEach((ecsfn) => {
|
||||
it('Should compile ' + ecsfn, function() {
|
||||
let asmfn = ecsfn.replace('.ecs','.asm');
|
||||
let goodfn = ecsfn.replace('.ecs','.txt');
|
||||
let ecspath = testdir + ecsfn;
|
||||
let goodpath = testdir + goodfn;
|
||||
let dialect = new Dialect_CA65();
|
||||
let em = new EntityManager(dialect);
|
||||
em.mainPath = ecspath;
|
||||
let compiler = new ECSCompiler(em, true);
|
||||
compiler.getImportFile = (path: string) => {
|
||||
return readFileSync(testdir + path, 'utf-8');
|
||||
}
|
||||
let code = readFileSync(ecspath, 'utf-8');
|
||||
var outtxt = '';
|
||||
try {
|
||||
compiler.parseFile(code, ecspath);
|
||||
// TODO: errors
|
||||
let out = new SourceFileExport();
|
||||
em.exportToFile(out);
|
||||
outtxt = out.toString();
|
||||
} catch (e) {
|
||||
outtxt = e.toString();
|
||||
console.log(e);
|
||||
}
|
||||
if (compiler.errors.length)
|
||||
outtxt = compiler.errors.map(e => `${e.line}:${e.msg}`).join('\n');
|
||||
let goodtxt = existsSync(goodpath) ? readFileSync(goodpath, 'utf-8') : '';
|
||||
if (outtxt.trim() != goodtxt.trim()) {
|
||||
let asmpath = '/tmp/' + asmfn;
|
||||
writeFileSync(asmpath, outtxt, 'utf-8');
|
||||
console.log(spawnSync('/usr/bin/diff', [goodpath, asmpath], {encoding:'utf-8'}).stdout);
|
||||
throw new Error(`files different; to fix: cp ${asmpath} ${goodpath}`);
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
function testPack(bins: Bin[], boxes: BoxConstraints[]) {
|
||||
let packer = new Packer();
|
||||
for (let bin of bins) packer.bins.push(bin);
|
||||
for (let bc of boxes) packer.boxes.push(bc);
|
||||
if (!packer.pack()) throw new Error('cannot pack')
|
||||
//console.log(packer.boxes);
|
||||
//console.log(packer.bins[0].free)
|
||||
}
|
||||
|
||||
describe('Box Packer', function() {
|
||||
it('Should pack boxes', function() {
|
||||
testPack(
|
||||
[
|
||||
new Bin({ left:0, top:0, right:10, bottom:10 })
|
||||
], [
|
||||
{ width: 5, height: 5 },
|
||||
{ width: 5, height: 5 },
|
||||
{ width: 5, height: 5 },
|
||||
{ width: 5, height: 5 },
|
||||
]
|
||||
);
|
||||
});
|
||||
it('Should pack top-aligned boxes', function() {
|
||||
testPack(
|
||||
[
|
||||
new Bin({ left:0, top:0, right:10, bottom:10 })
|
||||
], [
|
||||
{ width: 5, height: 7, top: 0 },
|
||||
{ width: 5, height: 7, top: 1 },
|
||||
{ width: 5, height: 1 },
|
||||
{ width: 5, height: 1 },
|
||||
{ width: 5, height: 3 },
|
||||
{ width: 5, height: 1 },
|
||||
]
|
||||
);
|
||||
});
|
||||
it('Should pack unaligned boxes', function() {
|
||||
testPack(
|
||||
[
|
||||
new Bin({ left:0, top:0, right:10, bottom:10 })
|
||||
], [
|
||||
{ width: 3, height: 7, top: 0 },
|
||||
{ width: 3, height: 7, top: 1 },
|
||||
{ width: 3, height: 7, top: 2 },
|
||||
{ width: 5, height: 1 },
|
||||
{ width: 3, height: 1 },
|
||||
]
|
||||
);
|
||||
});
|
||||
it('Should pack multiple bins', function() {
|
||||
testPack(
|
||||
[
|
||||
new Bin({ left:0, top:0, right:10, bottom:10 }),
|
||||
new Bin({ left:0, top:0, right:10, bottom:10 })
|
||||
], [
|
||||
|
||||
{ width: 5, height: 10 },
|
||||
{ width: 5, height: 10 },
|
||||
{ width: 5, height: 5 },
|
||||
{ width: 5, height: 10 },
|
||||
{ width: 5, height: 5 },
|
||||
]
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -0,0 +1,63 @@
|
||||
/*
|
||||
* Copyright (c) 2024 Steven E. Hugg
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
import assert from "assert";
|
||||
import { DWARFParser, ELFParser } from "../../src/common/binutils";
|
||||
|
||||
describe('test ELFParser', () => {
|
||||
|
||||
const fs = require('fs');
|
||||
const data = fs.readFileSync('./test/exes/arm32.elf');
|
||||
const elfParser = new ELFParser(new Uint8Array(data));
|
||||
|
||||
it('should parse sections and symbols', () => {
|
||||
/*
|
||||
elfParser.sectionHeaders.forEach((section, index) => {
|
||||
console.log('section', index, section.name, section.type, section.vaddr.toString(16), section.size.toString(16));
|
||||
});
|
||||
elfParser.getSymbols().forEach((symbol, index) => {
|
||||
console.log('symbol', index, symbol.info, symbol.other, symbol.name, symbol.value.toString(16));
|
||||
});
|
||||
*/
|
||||
assert.strictEqual(21, elfParser.sectionHeaders.length);
|
||||
assert.strictEqual(29, elfParser.getSymbols().length);
|
||||
assert.ok(elfParser.sectionHeaders.find((section) => section.name === '.text') != null);
|
||||
assert.ok(elfParser.getSymbols().find((symbol) => symbol.name === 'main') != null);
|
||||
});
|
||||
|
||||
it('should parse DWARF info', () => {
|
||||
const dwarf = new DWARFParser(elfParser);
|
||||
assert.strictEqual(2, dwarf.units.length);
|
||||
const cu = dwarf.units[0];
|
||||
// TODO: check info content
|
||||
const li = dwarf.lineInfos[0];
|
||||
assert.strictEqual('crt0.c', li.files[1].name);
|
||||
/*
|
||||
assert.ok(info != null);
|
||||
assert.ok(info!.lineNumberProgram != null);
|
||||
assert.ok(info!.lineNumberProgram!.length > 0);
|
||||
assert.ok(info!.lineNumberProgram![0].file != null);
|
||||
assert.ok(info!.lineNumberProgram![0].file!.name != null);
|
||||
assert.ok(info!.lineNumberProgram![0].file!.name!.length > 0);
|
||||
*/
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,24 @@
|
||||
import assert from "assert";
|
||||
import * as fs from "fs";
|
||||
import { LibRetroRunner } from "../../src/common/wasi/libretro";
|
||||
|
||||
async function loadLibretro() {
|
||||
const wasmdata = fs.readFileSync(`./wasi/stella2014_libretro_2.wasm`);
|
||||
let shim = new LibRetroRunner();
|
||||
await shim.loadAsync(wasmdata);
|
||||
return shim;
|
||||
}
|
||||
|
||||
/*
|
||||
describe('test WASI libretro', function () {
|
||||
it('libretro init', async function () {
|
||||
let shim = await loadLibretro();
|
||||
assert.strictEqual(1, shim.retro_api_version());
|
||||
shim.retro_init();
|
||||
let romdata = fs.readFileSync(`./test/roms/vcs/brickgame.rom`);
|
||||
shim.load_rom('brickgame.rom', romdata);
|
||||
shim.reset();
|
||||
shim.advance();
|
||||
});
|
||||
});
|
||||
*/
|
||||
@@ -0,0 +1,201 @@
|
||||
|
||||
import assert from "assert";
|
||||
import { describe } from "mocha";
|
||||
import { EmuHalt } from "../../src/common/emu"
|
||||
import { lzgmini, isProbablyBinary, hex } from "../../src/common/util";
|
||||
import { Tokenizer, TokenType } from "../../src/common/tokenizer";
|
||||
import { OPS_6502 } from "../../src/common/cpu/disasm6502";
|
||||
import { MOS6502 } from "../../src/common/cpu/MOS6502";
|
||||
|
||||
var NES_CONIO_ROM_LZG = [
|
||||
76, 90, 71, 0, 0, 160, 16, 0, 0, 11, 158, 107, 131, 223, 83, 1, 9, 17, 21, 22, 78, 69, 83, 26, 2, 1, 3, 0, 22, 6, 120, 216,
|
||||
162, 0, 134, 112, 134, 114, 134, 113, 134, 115, 154, 169, 32, 157, 0, 2, 157, 0, 3, 157, 0, 4, 232, 208, 244, 32, 134, 130, 32, 85, 129, 169,
|
||||
0, 162, 8, 133, 2, 134, 3, 32, 93, 128, 32, 50, 129, 32, 73, 129, 76, 0, 128, 72, 152, 72, 138, 72, 169, 1, 133, 112, 230, 107, 208, 2,
|
||||
230, 108, 32, 232, 129, 169, 32, 141, 6, 32, 169, 0, 22, 129, 141, 5, 22, 66, 104, 170, 104, 168, 104, 64, 160, 0, 240, 7, 169, 105, 162, 128,
|
||||
76, 4, 96, 96, 162, 0, 21, 23, 0, 32, 22, 195, 1, 22, 194, 63, 21, 37, 21, 134, 22, 197, 41, 21, 27, 173, 41, 96, 201, 4, 32, 169,
|
||||
129, 240, 3, 76, 158, 128, 76, 188, 128, 169, 184, 162, 130, 24, 109, 41, 96, 144, 1, 232, 160, 0, 32, 130, 129, 141, 7, 21, 36, 238, 41, 96,
|
||||
21, 32, 76, 140, 128, 21, 47, 33, 21, 246, 201, 17, 14, 61, 15, 21, 253, 227, 128, 76, 1, 129, 169, 169, 17, 24, 61, 209, 21, 125, 17, 2,
|
||||
180, 17, 10, 130, 5, 22, 201, 128, 17, 4, 172, 30, 141, 1, 32, 76, 46, 129, 22, 65, 96, 173, 0, 96, 174, 1, 96, 32, 112, 130, 173, 2,
|
||||
96, 174, 3, 21, 65, 160, 4, 76, 105, 128, 17, 3, 228, 188, 162, 130, 17, 2, 228, 169, 188, 133, 10, 169, 130, 133, 11, 169, 0, 133, 12, 169,
|
||||
96, 133, 13, 162, 214, 169, 255, 133, 18, 160, 0, 232, 240, 13, 177, 10, 145, 12, 200, 208, 246, 230, 11, 230, 13, 208, 240, 230, 18, 208, 239, 96,
|
||||
133, 10, 134, 11, 162, 0, 177, 10, 96, 208, 42, 162, 0, 138, 96, 240, 36, 22, 163, 30, 48, 28, 22, 227, 2, 16, 20, 22, 227, 14, 144, 12,
|
||||
21, 200, 176, 4, 22, 226, 162, 0, 169, 1, 96, 165, 115, 208, 252, 96, 169, 255, 197, 115, 240, 252, 96, 133, 118, 132, 116, 134, 117, 32, 193, 129,
|
||||
164, 113, 165, 116, 153, 0, 2, 165, 117, 153, 0, 3, 165, 118, 153, 0, 4, 200, 132, 113, 230, 115, 96, 164, 115, 208, 1, 96, 166, 114, 169, 14,
|
||||
141, 42, 96, 189, 0, 2, 141, 6, 32, 189, 0, 3, 22, 163, 4, 141, 7, 32, 232, 136, 240, 93, 17, 19, 14, 71, 17, 19, 14, 49, 17, 19,
|
||||
14, 27, 17, 19, 14, 5, 206, 42, 96, 208, 141, 134, 114, 132, 115, 96, 169, 0, 162, 0, 72, 165, 2, 56, 233, 2, 133, 2, 176, 2, 198, 3,
|
||||
160, 1, 138, 145, 2, 104, 136, 145, 2, 96, 169, 41, 133, 10, 169, 96, 17, 34, 41, 168, 162, 0, 240, 10, 145, 10, 200, 208, 251, 230, 11, 202,
|
||||
208, 246, 192, 2, 240, 5, 21, 70, 247, 96, 78, 111, 32, 99, 97, 114, 116, 32, 108, 111, 97, 100, 101, 100, 0, 1, 0, 16, 32, 17, 66, 184,
|
||||
141, 18, 96, 142, 19, 96, 141, 25, 96, 142, 26, 96, 136, 185, 255, 255, 141, 35, 22, 196, 34, 96, 140, 37, 96, 32, 255, 255, 160, 255, 208, 232,
|
||||
96, 17, 71, 230, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31,
|
||||
22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31,
|
||||
22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31,
|
||||
22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31,
|
||||
22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31,
|
||||
22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31,
|
||||
22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31,
|
||||
22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31,
|
||||
22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31,
|
||||
22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31,
|
||||
22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31,
|
||||
22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31,
|
||||
22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31,
|
||||
22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31,
|
||||
22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31,
|
||||
22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 10, 53, 128, 0, 128, 92, 128,
|
||||
17, 14, 14, 204, 204, 51, 51, 22, 106, 0, 24, 60, 126, 24, 22, 1, 22, 231, 16, 48, 127, 127, 48, 16, 0, 22, 230, 12, 18, 48, 124, 48,
|
||||
98, 252, 22, 231, 0, 0, 3, 62, 118, 54, 54, 22, 231, 127, 127, 17, 4, 80, 22, 230, 224, 224, 96, 22, 3, 22, 230, 24, 24, 24, 248, 248,
|
||||
21, 16, 22, 230, 204, 153, 51, 102, 22, 106, 51, 153, 204, 22, 107, 21, 27, 255, 255, 17, 4, 67, 22, 227, 3, 22, 13, 17, 6, 188, 22, 230,
|
||||
17, 2, 172, 22, 13, 31, 31, 22, 236, 255, 255, 22, 236, 31, 31, 17, 4, 136, 22, 227, 22, 1, 248, 248, 21, 5, 22, 233, 17, 14, 123, 17,
|
||||
3, 64, 22, 230, 17, 3, 64, 21, 248, 17, 8, 29, 21, 216, 17, 6, 88, 17, 3, 64, 22, 230, 240, 22, 13, 21, 233, 21, 243, 22, 230, 17,
|
||||
6, 16, 22, 226, 192, 192, 48, 48, 22, 106, 15, 22, 1, 21, 84, 22, 230, 17, 10, 4, 22, 226, 17, 10, 52, 22, 230, 17, 6, 16, 17, 10,
|
||||
44, 22, 6, 17, 35, 220, 0, 24, 22, 231, 102, 102, 17, 34, 107, 0, 22, 233, 255, 22, 33, 102, 22, 231, 24, 62, 96, 60, 6, 124, 21, 40,
|
||||
22, 229, 0, 102, 12, 24, 48, 102, 70, 22, 231, 60, 102, 60, 56, 103, 102, 63, 22, 231, 6, 12, 17, 36, 59, 22, 230, 21, 30, 48, 48, 24,
|
||||
12, 22, 231, 22, 97, 12, 21, 4, 22, 231, 0, 102, 60, 255, 60, 17, 2, 115, 22, 230, 24, 24, 126, 17, 35, 70, 22, 230, 17, 4, 173, 21,
|
||||
33, 22, 231, 126, 21, 205, 22, 231, 21, 80, 22, 232, 3, 6, 12, 24, 48, 96, 22, 231, 60, 102, 110, 118, 102, 102, 60, 22, 231, 24, 24, 56,
|
||||
24, 24, 24, 126, 22, 231, 60, 102, 6, 12, 48, 96, 22, 235, 28, 6, 21, 168, 22, 228, 6, 14, 30, 102, 127, 6, 6, 22, 231, 126, 96, 124,
|
||||
6, 21, 80, 22, 230, 60, 102, 96, 124, 17, 4, 88, 22, 228, 126, 102, 12, 17, 35, 83, 22, 230, 60, 21, 13, 21, 216, 22, 231, 62, 21, 240,
|
||||
22, 228, 17, 34, 124, 22, 66, 22, 236, 17, 2, 224, 22, 228, 14, 24, 48, 96, 48, 24, 14, 0, 22, 230, 17, 2, 239, 17, 4, 241, 22, 228,
|
||||
112, 24, 12, 6, 12, 24, 112, 22, 231, 17, 2, 192, 24, 21, 52, 22, 232, 110, 110, 96, 98, 17, 3, 248, 22, 227, 24, 60, 102, 126, 17, 34,
|
||||
228, 22, 230, 124, 102, 102, 22, 66, 22, 231, 60, 102, 96, 96, 96, 17, 4, 200, 22, 227, 120, 108, 21, 30, 108, 120, 22, 231, 126, 96, 96, 120,
|
||||
96, 96, 126, 22, 237, 96, 22, 231, 21, 48, 110, 17, 37, 8, 22, 227, 21, 46, 17, 3, 96, 22, 230, 60, 17, 99, 19, 21, 24, 22, 229, 30,
|
||||
12, 22, 1, 108, 56, 22, 231, 102, 108, 120, 112, 120, 108, 21, 40, 22, 229, 17, 132, 62, 126, 22, 231, 99, 119, 127, 107, 99, 99, 99, 22, 231,
|
||||
102, 118, 126, 126, 110, 17, 2, 88, 22, 229, 60, 102, 22, 2, 17, 35, 88, 22, 227, 17, 2, 205, 21, 49, 22, 231, 21, 144, 60, 14, 22, 231,
|
||||
21, 80, 17, 2, 96, 22, 230, 60, 102, 96, 60, 17, 37, 208, 22, 227, 17, 163, 13, 17, 34, 200, 22, 229, 21, 111, 17, 5, 208, 22, 232, 60,
|
||||
17, 5, 16, 22, 225, 99, 99, 99, 107, 127, 119, 99, 22, 231, 21, 77, 60, 17, 3, 248, 22, 230, 21, 1, 17, 4, 64, 22, 227, 126, 17, 67,
|
||||
159, 126, 22, 231, 60, 48, 22, 2, 60, 22, 231, 96, 48, 24, 12, 6, 3, 0, 22, 231, 60, 17, 34, 32, 12, 21, 24, 22, 229, 17, 34, 193,
|
||||
17, 68, 244, 22, 229, 22, 3, 17, 165, 133, 22, 225, 17, 134, 203, 22, 230, 21, 58, 6, 62, 102, 62, 22, 232, 96, 17, 66, 176, 124, 22, 232,
|
||||
0, 60, 96, 96, 96, 17, 66, 144, 22, 229, 6, 21, 31, 21, 96, 22, 230, 0, 60, 102, 126, 21, 216, 22, 228, 14, 24, 62, 17, 3, 84, 22,
|
||||
230, 0, 21, 95, 6, 124, 22, 231, 17, 3, 80, 102, 17, 5, 88, 22, 225, 24, 0, 56, 17, 34, 240, 22, 231, 6, 0, 6, 22, 1, 60, 22,
|
||||
231, 96, 96, 108, 17, 34, 128, 22, 231, 21, 30, 21, 160, 22, 230, 0, 102, 127, 127, 107, 99, 22, 233, 17, 2, 79, 21, 32, 22, 231, 17, 34,
|
||||
210, 17, 4, 152, 22, 228, 17, 36, 242, 22, 232, 17, 3, 144, 6, 22, 232, 124, 17, 66, 226, 21, 160, 22, 228, 17, 131, 225, 22, 232, 17, 130,
|
||||
127, 17, 98, 112, 22, 230, 17, 35, 226, 17, 34, 0, 22, 233, 60, 17, 2, 240, 22, 230, 99, 107, 127, 62, 17, 226, 24, 22, 230, 17, 35, 241,
|
||||
22, 234, 21, 47, 12, 120, 22, 232, 126, 12, 24, 48, 17, 98, 194, 22, 228, 28, 48, 24, 112, 24, 48, 28, 22, 231, 17, 164, 159, 22, 3, 22,
|
||||
227, 56, 12, 24, 14, 24, 12, 56, 0, 22, 230, 51, 255, 204, 17, 35, 206, 22, 230, 22, 14, 17, 194, 92, 22, 10, 17, 236, 246, 204, 204, 255,
|
||||
231, 195, 129, 231, 22, 1, 22, 231, 239, 207, 128, 128, 207, 239, 255, 22, 230, 243, 237, 207, 131, 207, 157, 3, 22, 231, 255, 255, 252, 193, 137, 201,
|
||||
201, 22, 231, 128, 128, 17, 4, 80, 22, 230, 31, 31, 159, 22, 3, 22, 230, 231, 231, 231, 7, 7, 21, 16, 22, 230, 17, 236, 246, 204, 17, 237,
|
||||
246, 51, 153, 17, 227, 11, 17, 4, 67, 22, 227, 252, 22, 13, 17, 6, 188, 22, 230, 17, 2, 172, 22, 13, 224, 224, 22, 236, 0, 0, 22, 236,
|
||||
224, 224, 17, 4, 136, 22, 227, 22, 1, 7, 7, 21, 5, 22, 233, 17, 14, 123, 17, 3, 64, 22, 230, 17, 3, 64, 21, 248, 17, 8, 29, 21,
|
||||
216, 17, 6, 88, 17, 3, 64, 22, 230, 17, 226, 124, 22, 10, 17, 238, 244, 22, 226, 17, 6, 16, 22, 226, 63, 63, 207, 207, 22, 106, 17, 226,
|
||||
192, 21, 84, 22, 230, 17, 10, 4, 17, 230, 220, 17, 14, 60, 17, 234, 252, 17, 6, 44, 22, 6, 17, 35, 220, 255, 231, 22, 231, 153, 153, 17,
|
||||
34, 107, 255, 22, 233, 0, 22, 33, 153, 22, 231, 231, 193, 159, 195, 249, 131, 21, 40, 22, 229, 255, 153, 243, 231, 207, 153, 185, 22, 231, 195, 153,
|
||||
195, 199, 152, 153, 192, 22, 231, 249, 243, 17, 36, 59, 22, 230, 21, 30, 207, 207, 231, 243, 22, 231, 22, 97, 243, 21, 4, 22, 231, 255, 153, 195,
|
||||
0, 195, 17, 2, 115, 22, 230, 231, 231, 129, 17, 35, 70, 22, 230, 17, 4, 173, 21, 33, 22, 231, 129, 21, 205, 22, 231, 21, 80, 22, 232, 252,
|
||||
249, 243, 231, 207, 159, 22, 231, 195, 153, 145, 137, 153, 153, 195, 22, 231, 231, 231, 199, 231, 231, 231, 129, 22, 231, 195, 153, 249, 243, 207, 159, 22,
|
||||
235, 227, 249, 21, 168, 22, 228, 249, 241, 225, 153, 128, 249, 249, 22, 231, 129, 159, 131, 249, 21, 80, 22, 230, 195, 153, 159, 131, 17, 4, 88, 22,
|
||||
228, 129, 153, 243, 17, 35, 83, 22, 230, 195, 21, 13, 21, 216, 22, 231, 193, 21, 240, 22, 228, 17, 34, 124, 22, 66, 22, 236, 17, 2, 224, 22,
|
||||
228, 241, 231, 207, 159, 207, 231, 241, 255, 22, 230, 17, 2, 239, 17, 4, 241, 22, 228, 143, 231, 243, 249, 243, 231, 143, 22, 231, 17, 2, 192, 231,
|
||||
21, 52, 22, 232, 145, 145, 159, 157, 17, 3, 248, 22, 227, 231, 195, 153, 129, 17, 34, 228, 22, 230, 131, 153, 153, 22, 66, 22, 231, 195, 153, 159,
|
||||
159, 159, 17, 4, 200, 22, 227, 135, 147, 21, 30, 147, 135, 22, 231, 129, 159, 159, 135, 159, 159, 129, 22, 237, 159, 22, 231, 21, 48, 145, 17, 37,
|
||||
8, 22, 227, 21, 46, 17, 3, 96, 22, 230, 195, 17, 99, 19, 21, 24, 22, 229, 225, 243, 22, 1, 147, 199, 22, 231, 153, 147, 135, 143, 135, 147,
|
||||
21, 40, 22, 229, 17, 132, 62, 129, 22, 231, 156, 136, 128, 148, 156, 156, 156, 22, 231, 153, 137, 129, 129, 145, 17, 2, 88, 22, 229, 195, 153, 22,
|
||||
2, 17, 35, 88, 22, 227, 17, 2, 205, 21, 49, 22, 231, 21, 144, 195, 241, 22, 231, 21, 80, 17, 2, 96, 22, 230, 195, 153, 159, 195, 17, 37,
|
||||
208, 22, 227, 17, 163, 13, 17, 34, 200, 22, 229, 21, 111, 17, 5, 208, 22, 232, 195, 17, 5, 16, 22, 225, 156, 156, 156, 148, 128, 136, 156, 22,
|
||||
231, 21, 77, 195, 17, 3, 248, 22, 230, 21, 1, 17, 4, 64, 22, 227, 129, 17, 67, 159, 129, 22, 231, 195, 207, 22, 2, 195, 22, 231, 159, 207,
|
||||
231, 243, 249, 252, 255, 22, 231, 195, 17, 34, 32, 243, 21, 24, 22, 229, 17, 34, 193, 17, 68, 244, 22, 229, 22, 3, 17, 165, 133, 22, 225, 17,
|
||||
134, 203, 22, 230, 21, 58, 249, 193, 153, 193, 22, 232, 159, 17, 66, 176, 131, 22, 232, 255, 195, 159, 159, 159, 17, 66, 144, 22, 229, 249, 21, 31,
|
||||
21, 96, 22, 230, 255, 195, 153, 129, 21, 216, 22, 228, 241, 231, 193, 17, 3, 84, 22, 230, 255, 21, 95, 249, 131, 22, 231, 17, 3, 80, 153, 17,
|
||||
5, 88, 22, 225, 231, 255, 199, 17, 34, 240, 22, 231, 249, 255, 249, 22, 1, 195, 22, 231, 159, 159, 147, 17, 34, 128, 22, 231, 21, 30, 21, 160,
|
||||
22, 230, 255, 153, 128, 128, 148, 156, 22, 233, 17, 2, 79, 21, 32, 22, 231, 17, 34, 210, 17, 4, 152, 22, 228, 17, 36, 242, 22, 232, 17, 3,
|
||||
144, 249, 22, 232, 131, 17, 66, 226, 21, 160, 22, 228, 17, 131, 225, 22, 232, 17, 130, 127, 17, 98, 112, 22, 230, 17, 35, 226, 17, 34, 0, 22,
|
||||
233, 195, 17, 2, 240, 22, 230, 156, 148, 128, 193, 17, 226, 24, 22, 230, 17, 35, 241, 22, 234, 21, 47, 243, 135, 22, 232, 129, 243, 231, 207, 17,
|
||||
98, 194, 22, 228, 227, 207, 231, 143, 231, 207, 227, 22, 231, 17, 164, 159, 22, 3, 22, 227, 199, 243, 231, 241, 231, 243, 199, 255, 22, 230, 204, 0,
|
||||
51, 17, 35, 206, 22, 230, 22, 14, 9, 19, 0, 13, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31,
|
||||
22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 31,
|
||||
22, 31, 22, 31, 22, 31, 22, 31, 22, 31, 22, 30, 22, 28
|
||||
];
|
||||
|
||||
describe('LZG', function () {
|
||||
it('Should decode LZG', function () {
|
||||
var rom = new Uint8Array(new lzgmini().decode(NES_CONIO_ROM_LZG));
|
||||
assert.equal(40976, rom.length);
|
||||
});
|
||||
});
|
||||
|
||||
describe('string functions', function () {
|
||||
it('Should detect binary', function () {
|
||||
assert.ok(!isProbablyBinary(null, [32, 32, 10, 13, 9, 32, 32, 10, 13]));
|
||||
assert.ok(isProbablyBinary(null, [32, 32, 0x80]));
|
||||
assert.ok(!isProbablyBinary(null, [32, 32, 0xc1, 0x81, 32, 32, 10, 13]));
|
||||
assert.ok(isProbablyBinary(null, NES_CONIO_ROM_LZG));
|
||||
assert.ok(isProbablyBinary('test.bin'));
|
||||
assert.ok(isProbablyBinary('test.chr'));
|
||||
assert.ok(!isProbablyBinary('test.txt'));
|
||||
assert.ok(isProbablyBinary('test.dat'));
|
||||
assert.ok(!isProbablyBinary(null, [0x20, 0xa9, 0x20, 0x31, 0x39, 0x38, 0x32]));
|
||||
assert.ok(isProbablyBinary(null, [0x00, 0x00])); // 00 is binary
|
||||
assert.ok(isProbablyBinary(null, [0x9f])); // 9f is binary
|
||||
assert.ok(isProbablyBinary(null, [0xff])); // FF is binary
|
||||
assert.ok(isProbablyBinary(null, [0xf0, 0x12])); // ran out of data
|
||||
});
|
||||
});
|
||||
|
||||
describe('EmuHalt', function () {
|
||||
it('Should detect emuhalt', function () {
|
||||
var e = new EmuHalt("?");
|
||||
assert.ok(e instanceof EmuHalt);
|
||||
assert.ok(e.hasOwnProperty('$loc'));
|
||||
});
|
||||
});
|
||||
|
||||
describe('Tokenizer', function () {
|
||||
it('Should tokenize', function () {
|
||||
var t = new Tokenizer();
|
||||
t.setTokenRules([
|
||||
{ type: 'ident', regex: /[$A-Za-z_][A-Za-z0-9_-]*/ },
|
||||
{ type: 'delim', regex: /[\(\)\{\}\[\]]/ },
|
||||
{ type: 'qstring', regex: /".*?"/ },
|
||||
{ type: 'integer', regex: /[-]?\d+/ },
|
||||
{ type: 'eol', regex: /\n+/ },
|
||||
{ type: 'ignore', regex: /\s+/ },
|
||||
]);
|
||||
t.tokenizeFile("a\n{\"key\" value\n \"number\" 531\n\n \"f\" (fn [x] (+ x 2))}\n", "test.file");
|
||||
assert.strictEqual(t.tokens.map(t => t.type).join(' '),
|
||||
'ident delim qstring ident qstring integer qstring delim ident delim ident delim delim catch-all ident integer delim delim delim eof');
|
||||
assert.strictEqual(t.tokens.map(t => t.str).join(' '),
|
||||
'a { "key" value "number" 531 "f" ( fn [ x ] ( + x 2 ) ) } ');
|
||||
assert.strictEqual(t.tokens.filter(t => t.eol).map(t => t.str).join(' '),
|
||||
'a value 531 } ');
|
||||
assert.strictEqual(t.tokens.map(t => t.$loc.line).join(' '),
|
||||
'1 2 2 2 3 3 5 5 5 5 5 5 5 5 5 5 5 5 5 6');
|
||||
assert.strictEqual(20, t.tokens.length);
|
||||
assert.strictEqual('a', t.peekToken().str);
|
||||
assert.strictEqual('a', t.expectToken('a').str);
|
||||
t.expectTokens(['foo', '{']);
|
||||
t.pushbackToken(t.consumeToken());
|
||||
assert.strictEqual('"key"', t.consumeToken().str);
|
||||
assert.deepStrictEqual({ 'value': true }, t.parseModifiers(['foo', 'value', 'bar']));
|
||||
assert.deepStrictEqual([], t.errors);
|
||||
t = new Tokenizer();
|
||||
t.setTokenRules([
|
||||
{ type: 'ident', regex: /[$A-Za-z_][A-Za-z0-9_-]*/ },
|
||||
{ type: 'delim', regex: /[\(\)\{\}\[\]]/ },
|
||||
{ type: 'code-fragment', regex: /---[^-]*---/ },
|
||||
{ type: 'ignore', regex: /\s+/ },
|
||||
]);
|
||||
t.tokenizeFile("key value ---\nthis is\na fragment\n--- foo", "test.file");
|
||||
assert.strictEqual(t.tokens.map(t => t.type).join(' '),
|
||||
'ident ident code-fragment ident eof');
|
||||
});
|
||||
});
|
||||
|
||||
describe('EmuHalt', function () {
|
||||
try {
|
||||
throw new EmuHalt("test");
|
||||
} catch (err) {
|
||||
assert.ok(err instanceof EmuHalt);
|
||||
assert.ok(err.squelchError);
|
||||
}
|
||||
});
|
||||
|
||||
describe('CPU metadata', function () {
|
||||
let cpu = new MOS6502();
|
||||
for (let i=0; i<256; i++) {
|
||||
let meta1 = OPS_6502[i];
|
||||
let meta2 = cpu.getOpcodeMetadata(i);
|
||||
if (meta1.il > 0) continue;
|
||||
// assert.strictEqual(meta1.mn, meta2.mnenomic, `mnemonic ${hex(i)}: ${meta1.mn} != ${meta2.mnenomic}`);
|
||||
assert.strictEqual(meta1.nb, meta2.insnlength, `insnLength ${hex(i)}: ${meta1.nb} != ${meta2.insnlength}`);
|
||||
assert.strictEqual(meta1.c1, meta2.minCycles, `minCycles ${hex(i)}: ${meta1.c1} != ${meta2.minCycles}`);
|
||||
assert.strictEqual(meta1.c1+meta1.c2, meta2.maxCycles, `maxCycles ${hex(i)}: ${meta1.c1+meta1.c2} != ${meta2.maxCycles}`);
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,107 @@
|
||||
import assert from "assert";
|
||||
import { WASIRunner } from "../../src/common/wasi/wasishim";
|
||||
import * as fs from "fs";
|
||||
import { loadWASIFilesystemZip, unzipWASIFilesystem } from "../../src/worker/wasiutils";
|
||||
|
||||
async function loadWASM(filename: string) {
|
||||
const wasmdata = fs.readFileSync(`./src/worker/wasm/${filename}.wasm`);
|
||||
let shim = new WASIRunner();
|
||||
await shim.loadAsync(wasmdata);
|
||||
return shim;
|
||||
}
|
||||
|
||||
async function loadDASM() {
|
||||
return loadWASM('dasm-wasisdk');
|
||||
}
|
||||
async function loadCC7800() {
|
||||
return loadWASM('cc7800');
|
||||
}
|
||||
async function loadOscar64() {
|
||||
return loadWASM('oscar64');
|
||||
}
|
||||
|
||||
describe('test WASI DASM', function () {
|
||||
it('dasm help', async function () {
|
||||
let shim = await loadDASM();
|
||||
let errno = shim.run();
|
||||
assert.strictEqual(errno, 1);
|
||||
});
|
||||
it('dasm file not found', async function () {
|
||||
let shim = await loadDASM();
|
||||
shim.setArgs(["dasm", "file_not_found.asm"]);
|
||||
let errno = shim.run();
|
||||
assert.strictEqual(errno, 2);
|
||||
});
|
||||
it('dasm file not found 2', async function () {
|
||||
let shim = await loadDASM();
|
||||
shim.setArgs(["dasm", "/file.asm", "-d"]);
|
||||
let errno = shim.run();
|
||||
assert.strictEqual(errno, 2);
|
||||
});
|
||||
it('dasm bad args 1', async function () {
|
||||
let shim = await loadDASM();
|
||||
shim.setArgs(["dasm", "file_not_found.asm", "extra_arg.asm"]);
|
||||
let errno = shim.run();
|
||||
assert.strictEqual(errno, 1);
|
||||
});
|
||||
it('dasm bad args 2', async function () {
|
||||
let shim = await loadDASM();
|
||||
shim.setArgs(["dasm", "file_not_found.asm", "-E9"]);
|
||||
let errno = shim.run();
|
||||
assert.strictEqual(errno, 1);
|
||||
});
|
||||
it('dasm empty file', async function () {
|
||||
let shim = await loadDASM();
|
||||
shim.setArgs(["dasm", "empty.asm"]);
|
||||
shim.addPreopenDirectory("/root");
|
||||
shim.fs.putFile("/root/empty.asm", "");
|
||||
let errno = shim.run();
|
||||
assert.strictEqual(errno, 0);
|
||||
});
|
||||
it('dasm small file', async function () {
|
||||
let shim = await loadDASM();
|
||||
shim.setArgs(["dasm", "empty.asm"]);
|
||||
shim.addPreopenDirectory("/root");
|
||||
shim.fs.putFile("/root/empty.asm", " processor 6502\n org $100\n nop");
|
||||
let errno = shim.run();
|
||||
assert.strictEqual(errno, 0);
|
||||
let aout = shim.fs.getFile("/root/a.out");
|
||||
assert.deepStrictEqual(Array.from(aout.getBytes()), [0x00, 0x01, 0xea]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('test WASI cc7800', function () {
|
||||
it('cc7800 help', async function () {
|
||||
let shim = await loadCC7800();
|
||||
shim.setArgs(["cc7800", '-h']);
|
||||
let errno = shim.run();
|
||||
assert.strictEqual(errno, 0);
|
||||
const stdout = shim.fds[1].getBytesAsString();
|
||||
console.log(stdout);
|
||||
assert.ok(stdout.indexOf('Usage: cc7800') >= 0);
|
||||
});
|
||||
});
|
||||
|
||||
/*
|
||||
describe('test WASI oscar64', function () {
|
||||
it('oscar64 compile', async function () {
|
||||
let shim = await loadOscar64();
|
||||
const zipdata = fs.readFileSync(`./src/worker/fs/oscar64-fs.zip`);
|
||||
shim.fs = await unzipWASIFilesystem(zipdata, "/root/");
|
||||
// https://github.com/WebAssembly/wasi-filesystem/issues/24
|
||||
// https://github.com/WebAssembly/wasi-libc/pull/214
|
||||
shim.addPreopenDirectory("/root");
|
||||
shim.fs.putSymbolicLink("/proc/self/exe", "/root/bin/oscar64");
|
||||
shim.fs.putFile("/root/main.c", `#include <stdio.h>\nint main() { printf("FOO"); return 0; }`);
|
||||
shim.setArgs(["oscar64", '-v', '-g', '-O', '-o=foo.prg', 'main.c']);
|
||||
let errno = shim.run();
|
||||
const stdout = shim.fds[1].getBytesAsString();
|
||||
console.log(stdout);
|
||||
const stderr = shim.fds[2].getBytesAsString();
|
||||
console.log(stderr);
|
||||
assert.strictEqual(errno, 0);
|
||||
assert.ok(stdout.indexOf('Starting oscar64') >= 0);
|
||||
console.log(shim.fs.getFile("/root/foo.asm").getBytesAsString());
|
||||
});
|
||||
});
|
||||
*/
|
||||
Reference in New Issue
Block a user