mirror of
https://github.com/whscullin/apple2js.git
synced 2024-01-12 14:14:38 +00:00
432 lines
20 KiB
TypeScript
432 lines
20 KiB
TypeScript
|
import ApplesoftCompiler from 'js/applesoft/compiler';
|
||
|
import RAM from 'js/ram';
|
||
|
import { Memory } from 'js/types';
|
||
|
|
||
|
// Zero page locations used by Applesoft. The names come from
|
||
|
// the commented decompilation produced by the Merlin Pro
|
||
|
// assembler, revision 4/27/84. There is evidence from
|
||
|
// https://www.pagetable.com/?p=774 that the original Microsoft
|
||
|
// BASIC source code used these names as well.
|
||
|
const TXTTAB = 0x67; // start of program, word
|
||
|
const VARTAB = 0x69; // start of variables, word
|
||
|
const ARYTAB = 0x6B; // start of arrays, word
|
||
|
const STREND = 0x6D; // end of strings, word
|
||
|
const PRGEND = 0xAF; // end of program, word
|
||
|
|
||
|
function compileToMemory(ram: Memory, program: string) {
|
||
|
ApplesoftCompiler.compileToMemory(ram, program);
|
||
|
}
|
||
|
|
||
|
// Manual decompilation based on "Applesoft Internal Structure"
|
||
|
// by C.K. Mesztenyi/Washington Apple Pi, from Call—A.P.P.L.E.,
|
||
|
// January, 1982. Archived at:
|
||
|
// https://archive.org/details/DTCA2DOC-045_applesoft_internal
|
||
|
// Decompilation verified on the emulator by typing in the
|
||
|
// program, then:
|
||
|
// ]CALL -151
|
||
|
// *800.820
|
||
|
// and comparing the resulting bytes (starting at 801).
|
||
|
|
||
|
describe('ApplesoftCompiler', () => {
|
||
|
it('compiles a one-line hello world', () => {
|
||
|
const compiler = new ApplesoftCompiler();
|
||
|
compiler.compile('10 PRINT "HELLO, WORLD!"');
|
||
|
expect(compiler.program()).toEqual(new Uint8Array([
|
||
|
0x16, 0x08, 0x0a, 0x00, 0xba, 0x22, 0x48, 0x45,
|
||
|
0x4c, 0x4c, 0x4f, 0x2c, 0x20, 0x57, 0x4f, 0x52,
|
||
|
0x4c, 0x44, 0x21, 0x22, 0x00, 0x00, 0x00
|
||
|
]));
|
||
|
});
|
||
|
|
||
|
it('compiles a one-line hello world into memory', () => {
|
||
|
const ram = new RAM(0, 0xff); // 64K of RAM
|
||
|
|
||
|
compileToMemory(ram, '10 PRINT "HELLO, WORLD!"');
|
||
|
expect(ram.read(0x08, 0x01)).toBe(0x16); // pointer to next line low
|
||
|
expect(ram.read(0x08, 0x02)).toBe(0x08); // pointer to next line high
|
||
|
expect(ram.read(0x08, 0x03)).toBe(10); // line number low
|
||
|
expect(ram.read(0x08, 0x04)).toBe(0); // line number high
|
||
|
expect(ram.read(0x08, 0x05)).toBe(0xba); // PRINT
|
||
|
expect(ram.read(0x08, 0x06)).toBe(0x22); // "
|
||
|
expect(ram.read(0x08, 0x07)).toBe(0x48); // H
|
||
|
expect(ram.read(0x08, 0x08)).toBe(0x45); // E
|
||
|
expect(ram.read(0x08, 0x09)).toBe(0x4C); // L
|
||
|
expect(ram.read(0x08, 0x0a)).toBe(0x4C); // L
|
||
|
expect(ram.read(0x08, 0x0b)).toBe(0x4F); // O
|
||
|
expect(ram.read(0x08, 0x0c)).toBe(0x2C); // ,
|
||
|
expect(ram.read(0x08, 0x0d)).toBe(0x20); // space
|
||
|
expect(ram.read(0x08, 0x0e)).toBe(0x57); // W
|
||
|
expect(ram.read(0x08, 0x0f)).toBe(0x4F); // O
|
||
|
expect(ram.read(0x08, 0x10)).toBe(0x52); // R
|
||
|
expect(ram.read(0x08, 0x11)).toBe(0x4C); // L
|
||
|
expect(ram.read(0x08, 0x12)).toBe(0x44); // D
|
||
|
expect(ram.read(0x08, 0x13)).toBe(0x21); // !
|
||
|
expect(ram.read(0x08, 0x14)).toBe(0x22); // "
|
||
|
expect(ram.read(0x08, 0x15)).toBe(0x00); // end of line
|
||
|
expect(ram.read(0x08, 0x16)).toBe(0x00); // end of program low
|
||
|
expect(ram.read(0x08, 0x17)).toBe(0x00); // end of program high
|
||
|
|
||
|
expect(ram.read(0x00, TXTTAB)).toBe(0x01); // start of program low
|
||
|
expect(ram.read(0x00, TXTTAB + 1)).toBe(0x08); // start of program high
|
||
|
expect(ram.read(0x00, VARTAB)).toBe(0x19); // start of variables low
|
||
|
expect(ram.read(0x00, VARTAB + 1)).toBe(0x08); // start of variables high
|
||
|
expect(ram.read(0x00, ARYTAB)).toBe(0x19); // start of arrays low
|
||
|
expect(ram.read(0x00, ARYTAB + 1)).toBe(0x08); // start of arrays high
|
||
|
expect(ram.read(0x00, STREND)).toBe(0x19); // end of strings low
|
||
|
expect(ram.read(0x00, STREND + 1)).toBe(0x08); // end of strings high
|
||
|
expect(ram.read(0x00, PRGEND)).toBe(0x19); // end of program low
|
||
|
expect(ram.read(0x00, PRGEND + 1)).toBe(0x08); // end of program high
|
||
|
});
|
||
|
|
||
|
it('uppercases normal-mode text, like variables', () => {
|
||
|
const ram = new RAM(0, 0xff); // 64K of RAM
|
||
|
|
||
|
compileToMemory(ram, '10 fori=xtoz');
|
||
|
expect(ram.read(0x08, 0x03)).toBe(10); // line number low
|
||
|
expect(ram.read(0x08, 0x04)).toBe(0); // line number high
|
||
|
expect(ram.read(0x08, 0x05)).toBe(0x81); // FOR
|
||
|
expect(ram.read(0x08, 0x06)).toBe(0x49); // I
|
||
|
expect(ram.read(0x08, 0x07)).toBe(0xd0); // = (token)
|
||
|
expect(ram.read(0x08, 0x08)).toBe(0x58); // X
|
||
|
expect(ram.read(0x08, 0x09)).toBe(0xc1); // TO
|
||
|
expect(ram.read(0x08, 0x0a)).toBe(0x5a); // Z
|
||
|
expect(ram.read(0x08, 0x0b)).toBe(0x00); // end of line
|
||
|
});
|
||
|
|
||
|
it('allows lower-case characters in strings', () => {
|
||
|
const ram = new RAM(0, 0xff); // 64K of RAM
|
||
|
|
||
|
compileToMemory(ram, '10 PRINT "Hello!"');
|
||
|
expect(ram.read(0x08, 0x03)).toBe(10); // line number low
|
||
|
expect(ram.read(0x08, 0x04)).toBe(0); // line number high
|
||
|
expect(ram.read(0x08, 0x05)).toBe(0xba); // PRINT
|
||
|
expect(ram.read(0x08, 0x06)).toBe(0x22); // "
|
||
|
expect(ram.read(0x08, 0x07)).toBe(0x48); // H
|
||
|
expect(ram.read(0x08, 0x08)).toBe(0x65); // e
|
||
|
expect(ram.read(0x08, 0x09)).toBe(0x6C); // l
|
||
|
expect(ram.read(0x08, 0x0a)).toBe(0x6C); // l
|
||
|
expect(ram.read(0x08, 0x0b)).toBe(0x6F); // o
|
||
|
});
|
||
|
|
||
|
it('allows lower-case characters in comments', () => {
|
||
|
const ram = new RAM(0, 0xff); // 64K of RAM
|
||
|
|
||
|
compileToMemory(ram, '10 REM Hello!');
|
||
|
expect(ram.read(0x08, 0x03)).toBe(10); // line number low
|
||
|
expect(ram.read(0x08, 0x04)).toBe(0); // line number high
|
||
|
expect(ram.read(0x08, 0x05)).toBe(0xb2); // REM
|
||
|
expect(ram.read(0x08, 0x06)).toBe(0x20); // space
|
||
|
expect(ram.read(0x08, 0x07)).toBe(0x48); // H
|
||
|
expect(ram.read(0x08, 0x08)).toBe(0x65); // e
|
||
|
expect(ram.read(0x08, 0x09)).toBe(0x6C); // l
|
||
|
expect(ram.read(0x08, 0x0a)).toBe(0x6C); // l
|
||
|
expect(ram.read(0x08, 0x0b)).toBe(0x6F); // o
|
||
|
});
|
||
|
|
||
|
it('allows lower-case tokens', () => {
|
||
|
const ram = new RAM(0, 0xff); // 64K of RAM
|
||
|
|
||
|
compileToMemory(ram, '10 print "Hello!"');
|
||
|
expect(ram.read(0x08, 0x03)).toBe(10); // line number low
|
||
|
expect(ram.read(0x08, 0x04)).toBe(0); // line number high
|
||
|
expect(ram.read(0x08, 0x05)).toBe(0xba); // PRINT
|
||
|
});
|
||
|
|
||
|
it('accepts out-of-order lines', () => {
|
||
|
const ram = new RAM(0, 0xff); // 64K of RAM
|
||
|
|
||
|
compileToMemory(ram, '20 GOTO 10\n10 PRINT "HELLO');
|
||
|
expect(ram.read(0x08, 0x01)).toBe(0x0d); // pointer to next line low
|
||
|
expect(ram.read(0x08, 0x02)).toBe(0x08); // pointer to next line high
|
||
|
expect(ram.read(0x08, 0x03)).toBe(10); // line number low
|
||
|
expect(ram.read(0x08, 0x04)).toBe(0); // line number high
|
||
|
expect(ram.read(0x08, 0x05)).toBe(0xba); // PRINT
|
||
|
expect(ram.read(0x08, 0x06)).toBe(0x22); // "
|
||
|
expect(ram.read(0x08, 0x07)).toBe(0x48); // H
|
||
|
expect(ram.read(0x08, 0x08)).toBe(0x45); // E
|
||
|
expect(ram.read(0x08, 0x09)).toBe(0x4C); // L
|
||
|
expect(ram.read(0x08, 0x0a)).toBe(0x4C); // L
|
||
|
expect(ram.read(0x08, 0x0b)).toBe(0x4F); // O
|
||
|
expect(ram.read(0x08, 0x0c)).toBe(0x00); // end of line
|
||
|
expect(ram.read(0x08, 0x0d)).toBe(0x15); // pointer to next line low
|
||
|
expect(ram.read(0x08, 0x0e)).toBe(0x08); // pointer to next line high
|
||
|
expect(ram.read(0x08, 0x0f)).toBe(0x14); // line number low
|
||
|
expect(ram.read(0x08, 0x10)).toBe(0x00); // line number high
|
||
|
expect(ram.read(0x08, 0x11)).toBe(0xab); // GOTO
|
||
|
expect(ram.read(0x08, 0x12)).toBe(0x31); // 1
|
||
|
expect(ram.read(0x08, 0x13)).toBe(0x30); // 0
|
||
|
expect(ram.read(0x08, 0x14)).toBe(0x00); // end of line
|
||
|
expect(ram.read(0x08, 0x15)).toBe(0x00); // end of program low
|
||
|
expect(ram.read(0x08, 0x16)).toBe(0x00); // end of program high
|
||
|
});
|
||
|
|
||
|
it('prefers ATN to AT', () => {
|
||
|
const ram = new RAM(0, 0xff); // 64K of RAM
|
||
|
|
||
|
compileToMemory(ram, '10 X = ATN(20)');
|
||
|
expect(ram.read(0x08, 0x03)).toBe(10); // line number low
|
||
|
expect(ram.read(0x08, 0x04)).toBe(0); // line number high
|
||
|
expect(ram.read(0x08, 0x05)).toBe(0x58); // X
|
||
|
expect(ram.read(0x08, 0x06)).toBe(0xd0); // = (token)
|
||
|
expect(ram.read(0x08, 0x07)).toBe(0xe1); // ATN
|
||
|
expect(ram.read(0x08, 0x08)).toBe(0x28); // (
|
||
|
expect(ram.read(0x08, 0x09)).toBe(0x32); // 2
|
||
|
expect(ram.read(0x08, 0x0a)).toBe(0x30); // 0
|
||
|
expect(ram.read(0x08, 0x0b)).toBe(0x29); // )
|
||
|
expect(ram.read(0x08, 0x0c)).toBe(0x00); // end of line
|
||
|
});
|
||
|
|
||
|
it('prefers TO to AT', () => {
|
||
|
const ram = new RAM(0, 0xff); // 64K of RAM
|
||
|
|
||
|
compileToMemory(ram, '10 FORI=ATOZ');
|
||
|
expect(ram.read(0x08, 0x03)).toBe(10); // line number low
|
||
|
expect(ram.read(0x08, 0x04)).toBe(0); // line number high
|
||
|
expect(ram.read(0x08, 0x05)).toBe(0x81); // FOR
|
||
|
expect(ram.read(0x08, 0x06)).toBe(0x49); // I
|
||
|
expect(ram.read(0x08, 0x07)).toBe(0xd0); // = (token)
|
||
|
expect(ram.read(0x08, 0x08)).toBe(0x41); // A
|
||
|
expect(ram.read(0x08, 0x09)).toBe(0xc1); // TO
|
||
|
expect(ram.read(0x08, 0x0a)).toBe(0x5a); // Z
|
||
|
expect(ram.read(0x08, 0x0b)).toBe(0x00); // end of line
|
||
|
});
|
||
|
|
||
|
it('parses DATA statements that start with space', () => {
|
||
|
const ram = new RAM(0, 0xff); // 64K of RAM
|
||
|
|
||
|
compileToMemory(ram, '10 DATA 1,2,3');
|
||
|
expect(ram.read(0x08, 0x03)).toBe(10); // line number low
|
||
|
expect(ram.read(0x08, 0x04)).toBe(0); // line number high
|
||
|
expect(ram.read(0x08, 0x05)).toBe(0x83); // DATA
|
||
|
expect(ram.read(0x08, 0x06)).toBe(0x20); // space
|
||
|
expect(ram.read(0x08, 0x07)).toBe(0x31); // 1
|
||
|
expect(ram.read(0x08, 0x08)).toBe(0x2c); // ,
|
||
|
expect(ram.read(0x08, 0x09)).toBe(0x32); // 2
|
||
|
expect(ram.read(0x08, 0x0a)).toBe(0x2c); // ,
|
||
|
expect(ram.read(0x08, 0x0b)).toBe(0x33); // 3
|
||
|
expect(ram.read(0x08, 0x0c)).toBe(0x00); // end of line
|
||
|
});
|
||
|
|
||
|
it('parses DATA statements with numbers', () => {
|
||
|
const ram = new RAM(0, 0xff); // 64K of RAM
|
||
|
|
||
|
compileToMemory(ram, '10 DATA1,2,3');
|
||
|
expect(ram.read(0x08, 0x03)).toBe(10); // line number low
|
||
|
expect(ram.read(0x08, 0x04)).toBe(0); // line number high
|
||
|
expect(ram.read(0x08, 0x05)).toBe(0x83); // DATA
|
||
|
expect(ram.read(0x08, 0x06)).toBe(0x31); // 1
|
||
|
expect(ram.read(0x08, 0x07)).toBe(0x2c); // ,
|
||
|
expect(ram.read(0x08, 0x08)).toBe(0x32); // 2
|
||
|
expect(ram.read(0x08, 0x09)).toBe(0x2c); // ,
|
||
|
expect(ram.read(0x08, 0x0a)).toBe(0x33); // 3
|
||
|
expect(ram.read(0x08, 0x0b)).toBe(0x00); // end of line
|
||
|
});
|
||
|
|
||
|
it('parses DATA statements with strings including lower-case', () => {
|
||
|
const ram = new RAM(0, 0xff); // 64K of RAM
|
||
|
|
||
|
compileToMemory(ram, '10 DATA"abc"');
|
||
|
expect(ram.read(0x08, 0x03)).toBe(10); // line number low
|
||
|
expect(ram.read(0x08, 0x04)).toBe(0); // line number high
|
||
|
expect(ram.read(0x08, 0x05)).toBe(0x83); // DATA
|
||
|
expect(ram.read(0x08, 0x06)).toBe(0x22); // "
|
||
|
expect(ram.read(0x08, 0x07)).toBe(0x61); // a
|
||
|
expect(ram.read(0x08, 0x08)).toBe(0x62); // b
|
||
|
expect(ram.read(0x08, 0x09)).toBe(0x63); // c
|
||
|
expect(ram.read(0x08, 0x0a)).toBe(0x22); // "
|
||
|
expect(ram.read(0x08, 0x0b)).toBe(0x00); // end of line
|
||
|
});
|
||
|
|
||
|
it('parses DATA statements with literals', () => {
|
||
|
const ram = new RAM(0, 0xff); // 64K of RAM
|
||
|
|
||
|
compileToMemory(ram, '10 DATAHELLO');
|
||
|
expect(ram.read(0x08, 0x03)).toBe(10); // line number low
|
||
|
expect(ram.read(0x08, 0x04)).toBe(0); // line number high
|
||
|
expect(ram.read(0x08, 0x05)).toBe(0x83); // DATA
|
||
|
expect(ram.read(0x08, 0x06)).toBe(0x48); // H
|
||
|
expect(ram.read(0x08, 0x07)).toBe(0x45); // E
|
||
|
expect(ram.read(0x08, 0x08)).toBe(0x4C); // L
|
||
|
expect(ram.read(0x08, 0x09)).toBe(0x4C); // L
|
||
|
expect(ram.read(0x08, 0x0a)).toBe(0x4F); // O
|
||
|
expect(ram.read(0x08, 0x0b)).toBe(0x00); // end of line
|
||
|
});
|
||
|
|
||
|
it('parses DATA statements with literals including lower-case', () => {
|
||
|
const ram = new RAM(0, 0xff); // 64K of RAM
|
||
|
|
||
|
compileToMemory(ram, '10 DATAHello');
|
||
|
expect(ram.read(0x08, 0x03)).toBe(10); // line number low
|
||
|
expect(ram.read(0x08, 0x04)).toBe(0); // line number high
|
||
|
expect(ram.read(0x08, 0x05)).toBe(0x83); // DATA
|
||
|
expect(ram.read(0x08, 0x06)).toBe(0x48); // H
|
||
|
expect(ram.read(0x08, 0x07)).toBe(0x65); // e
|
||
|
expect(ram.read(0x08, 0x08)).toBe(0x6C); // l
|
||
|
expect(ram.read(0x08, 0x09)).toBe(0x6C); // l
|
||
|
expect(ram.read(0x08, 0x0a)).toBe(0x6F); // o
|
||
|
expect(ram.read(0x08, 0x0b)).toBe(0x00); // end of line
|
||
|
});
|
||
|
|
||
|
it('parses DATA statements with literals including quotes', () => {
|
||
|
const ram = new RAM(0, 0xff); // 64K of RAM
|
||
|
|
||
|
compileToMemory(ram, '10 DATAAA"B');
|
||
|
expect(ram.read(0x08, 0x03)).toBe(10); // line number low
|
||
|
expect(ram.read(0x08, 0x04)).toBe(0); // line number high
|
||
|
expect(ram.read(0x08, 0x05)).toBe(0x83); // DATA
|
||
|
expect(ram.read(0x08, 0x06)).toBe(0x41); // A
|
||
|
expect(ram.read(0x08, 0x07)).toBe(0x41); // A
|
||
|
expect(ram.read(0x08, 0x08)).toBe(0x22); // "
|
||
|
expect(ram.read(0x08, 0x09)).toBe(0x42); // B
|
||
|
expect(ram.read(0x08, 0x0a)).toBe(0x00); // end of line
|
||
|
});
|
||
|
|
||
|
it('parses DATA statements with literals including spaces', () => {
|
||
|
const ram = new RAM(0, 0xff); // 64K of RAM
|
||
|
|
||
|
compileToMemory(ram, '10 DATAA B');
|
||
|
expect(ram.read(0x08, 0x03)).toBe(10); // line number low
|
||
|
expect(ram.read(0x08, 0x04)).toBe(0); // line number high
|
||
|
expect(ram.read(0x08, 0x05)).toBe(0x83); // DATA
|
||
|
expect(ram.read(0x08, 0x06)).toBe(0x41); // A
|
||
|
expect(ram.read(0x08, 0x07)).toBe(0x20); // space
|
||
|
expect(ram.read(0x08, 0x08)).toBe(0x20); // space
|
||
|
expect(ram.read(0x08, 0x09)).toBe(0x42); // B
|
||
|
expect(ram.read(0x08, 0x0a)).toBe(0x00); // end of line
|
||
|
});
|
||
|
|
||
|
it('terminates DATA statements at colons', () => {
|
||
|
const ram = new RAM(0, 0xff); // 64K of RAM
|
||
|
|
||
|
compileToMemory(ram, '10 DATAAA:FORI=1TO1');
|
||
|
expect(ram.read(0x08, 0x03)).toBe(10); // line number low
|
||
|
expect(ram.read(0x08, 0x04)).toBe(0); // line number high
|
||
|
expect(ram.read(0x08, 0x05)).toBe(0x83); // DATA
|
||
|
expect(ram.read(0x08, 0x06)).toBe(0x41); // A
|
||
|
expect(ram.read(0x08, 0x07)).toBe(0x41); // A
|
||
|
expect(ram.read(0x08, 0x08)).toBe(0x3a); // :
|
||
|
expect(ram.read(0x08, 0x09)).toBe(0x81); // FOR
|
||
|
expect(ram.read(0x08, 0x0a)).toBe(0x49); // I
|
||
|
expect(ram.read(0x08, 0x0b)).toBe(0xd0); // = (token)
|
||
|
expect(ram.read(0x08, 0x0c)).toBe(0x31); // 1
|
||
|
expect(ram.read(0x08, 0x0d)).toBe(0xc1); // TO
|
||
|
expect(ram.read(0x08, 0x0e)).toBe(0x31); // 1
|
||
|
expect(ram.read(0x08, 0x0f)).toBe(0x00); // end of line
|
||
|
});
|
||
|
|
||
|
it('does not terminate DATA statements with a literal with a quote at colon', () => {
|
||
|
const ram = new RAM(0, 0xff); // 64K of RAM
|
||
|
|
||
|
compileToMemory(ram, '10 DATAA":FORI=1TO1');
|
||
|
expect(ram.read(0x08, 0x03)).toBe(10); // line number low
|
||
|
expect(ram.read(0x08, 0x04)).toBe(0); // line number high
|
||
|
expect(ram.read(0x08, 0x05)).toBe(0x83); // DATA
|
||
|
expect(ram.read(0x08, 0x06)).toBe(0x41); // A
|
||
|
expect(ram.read(0x08, 0x07)).toBe(0x22); // "
|
||
|
expect(ram.read(0x08, 0x08)).toBe(0x3a); // :
|
||
|
expect(ram.read(0x08, 0x09)).toBe(0x46); // F
|
||
|
expect(ram.read(0x08, 0x0a)).toBe(0x4F); // O
|
||
|
expect(ram.read(0x08, 0x0b)).toBe(0x52); // R
|
||
|
expect(ram.read(0x08, 0x0c)).toBe(0x49); // I
|
||
|
expect(ram.read(0x08, 0x0d)).toBe(0x3D); // =
|
||
|
expect(ram.read(0x08, 0x0e)).toBe(0x31); // 1
|
||
|
expect(ram.read(0x08, 0x0f)).toBe(0x54); // T
|
||
|
expect(ram.read(0x08, 0x10)).toBe(0x4F); // O
|
||
|
expect(ram.read(0x08, 0x11)).toBe(0x31); // 1
|
||
|
expect(ram.read(0x08, 0x12)).toBe(0x00); // end of line
|
||
|
});
|
||
|
|
||
|
it('does terminate DATA statements with a literal with two quotes at colon', () => {
|
||
|
const ram = new RAM(0, 0xff); // 64K of RAM
|
||
|
|
||
|
compileToMemory(ram, '10 DATAA"":FORI=1TO1');
|
||
|
expect(ram.read(0x08, 0x03)).toBe(10); // line number low
|
||
|
expect(ram.read(0x08, 0x04)).toBe(0); // line number high
|
||
|
expect(ram.read(0x08, 0x05)).toBe(0x83); // DATA
|
||
|
expect(ram.read(0x08, 0x06)).toBe(0x41); // A
|
||
|
expect(ram.read(0x08, 0x07)).toBe(0x22); // "
|
||
|
expect(ram.read(0x08, 0x08)).toBe(0x22); // "
|
||
|
expect(ram.read(0x08, 0x09)).toBe(0x3a); // :
|
||
|
expect(ram.read(0x08, 0x0a)).toBe(0x81); // FOR
|
||
|
expect(ram.read(0x08, 0x0b)).toBe(0x49); // I
|
||
|
expect(ram.read(0x08, 0x0c)).toBe(0xd0); // = (token)
|
||
|
expect(ram.read(0x08, 0x0d)).toBe(0x31); // 1
|
||
|
expect(ram.read(0x08, 0x0e)).toBe(0xc1); // TO
|
||
|
expect(ram.read(0x08, 0x0f)).toBe(0x31); // 1
|
||
|
expect(ram.read(0x08, 0x10)).toBe(0x00); // end of line
|
||
|
});
|
||
|
|
||
|
it('does not require a space after line number', () => {
|
||
|
const compiler = new ApplesoftCompiler();
|
||
|
compiler.compile('10PRINT"HELLO, WORLD!"');
|
||
|
expect(compiler.program()).toEqual(new Uint8Array([
|
||
|
0x16, 0x08, 0x0a, 0x00, 0xba, 0x22, 0x48, 0x45,
|
||
|
0x4c, 0x4c, 0x4f, 0x2c, 0x20, 0x57, 0x4f, 0x52,
|
||
|
0x4c, 0x44, 0x21, 0x22, 0x00, 0x00, 0x00
|
||
|
]));
|
||
|
});
|
||
|
|
||
|
it('parses ? as PRINT', () => {
|
||
|
const compiler = new ApplesoftCompiler();
|
||
|
compiler.compile('10 ?"HELLO, WORLD!"');
|
||
|
expect(compiler.program()).toEqual(new Uint8Array([
|
||
|
0x16, 0x08, 0x0a, 0x00, 0xba, 0x22, 0x48, 0x45,
|
||
|
0x4c, 0x4c, 0x4f, 0x2c, 0x20, 0x57, 0x4f, 0x52,
|
||
|
0x4c, 0x44, 0x21, 0x22, 0x00, 0x00, 0x00
|
||
|
]));
|
||
|
});
|
||
|
|
||
|
it('skips spaces when reading tokens', () => {
|
||
|
const compiler = new ApplesoftCompiler();
|
||
|
compiler.compile('10 T H E N');
|
||
|
expect(compiler.program()).toEqual(new Uint8Array([
|
||
|
0x07, 0x08, 0x0a, 0x00, 0xc4, 0x00, 0x00, 0x00,
|
||
|
]));
|
||
|
});
|
||
|
|
||
|
it('skips spaces and ignores case when reading tokens', () => {
|
||
|
const compiler = new ApplesoftCompiler();
|
||
|
compiler.compile('10 T h E n');
|
||
|
expect(compiler.program()).toEqual(new Uint8Array([
|
||
|
0x07, 0x08, 0x0a, 0x00, 0xc4, 0x00, 0x00, 0x00,
|
||
|
]));
|
||
|
});
|
||
|
|
||
|
it('smashes tokens together', () => {
|
||
|
const compiler = new ApplesoftCompiler();
|
||
|
compiler.compile('10 NOT RACE A THEN B');
|
||
|
expect(compiler.program()).toEqual(new Uint8Array([
|
||
|
0x0c, 0x08, 0x0a, 0x00, 0x9c, 0xc5, 0x48, 0x45,
|
||
|
0x4e, 0x42, 0x00, 0x00, 0x00,
|
||
|
]));
|
||
|
});
|
||
|
|
||
|
it('parses 10ATOZ correctly', () => {
|
||
|
const compiler = new ApplesoftCompiler();
|
||
|
compiler.compile('10ATOZ');
|
||
|
expect(compiler.program()).toEqual(new Uint8Array([
|
||
|
0x09, 0x08, 0x0a, 0x00, 0x41, 0xc1, 0x5a, 0x00,
|
||
|
0x00, 0x00,
|
||
|
]));
|
||
|
});
|
||
|
|
||
|
it('parses a bunch of crazy correctly', () => {
|
||
|
const compiler = new ApplesoftCompiler();
|
||
|
compiler.compile([
|
||
|
'10 A THEN B',
|
||
|
'30 A TO Z',
|
||
|
'40 AT N',
|
||
|
'50 A TN',
|
||
|
'60 N O T R A C E',
|
||
|
'70 NOT RACE'].join('\n'));
|
||
|
expect(compiler.program()).toEqual(new Uint8Array([
|
||
|
0x0b, 0x08, 0x0a, 0x00, 0xc5, 0x48, 0x45,
|
||
|
0x4e, 0x42, 0x00, 0x13, 0x08, 0x1e, 0x00, 0x41,
|
||
|
0xc1, 0x5a, 0x00, 0x1a, 0x08, 0x28, 0x00, 0xc5,
|
||
|
0x4e, 0x00, 0x20, 0x08, 0x32, 0x00, 0xe1, 0x00,
|
||
|
0x26, 0x08, 0x3c, 0x00, 0x9c, 0x00, 0x2c, 0x08,
|
||
|
0x46, 0x00, 0x9c, 0x00, 0x00, 0x00,
|
||
|
]));
|
||
|
});
|
||
|
});
|