aboutsummaryrefslogtreecommitdiffgithub
diff options
context:
space:
mode:
authorAustin Adams <git@austinjadams.com>2019-01-27 23:38:19 -0600
committerAustin Adams <git@austinjadams.com>2019-01-27 23:38:19 -0600
commitdfbcaa97fbec2cb79e89fd4bdd037e65581cc4d9 (patch)
tree7cefbd97b5f6ba1e7072290df0769284a89b38c6
parentb178d160633bac3a1fe4415c20d5a0bdc9741dd7 (diff)
downloadnovice-dfbcaa97fbec2cb79e89fd4bdd037e65581cc4d9.tar.gz
novice-dfbcaa97fbec2cb79e89fd4bdd037e65581cc4d9.tar.xz
Generate symbol tables
-rw-r--r--novice/assembler/assembler.test.ts549
-rw-r--r--novice/assembler/assembler.ts15
-rw-r--r--novice/assembler/codegen/base.ts8
-rw-r--r--novice/assembler/codegen/codegen.ts5
-rw-r--r--novice/assembler/serializers/complx.ts14
-rw-r--r--novice/assembler/serializers/serializer.ts6
-rw-r--r--novice/cli.test.ts48
-rw-r--r--novice/cli.ts6
-rw-r--r--novice/isa/index.ts5
-rw-r--r--novice/isa/isa.ts6
10 files changed, 416 insertions, 246 deletions
diff --git a/novice/assembler/assembler.test.ts b/novice/assembler/assembler.test.ts
index ca7fd6c..2084f4a 100644
--- a/novice/assembler/assembler.test.ts
+++ b/novice/assembler/assembler.test.ts
@@ -333,12 +333,15 @@ describe('assembler', () => {
fp.push(null)
return expect(assembler.assemble(fp)).resolves.toEqual([
- {
- startAddr: 0x3000,
- words: [
- 0xf025,
- ],
- },
+ {},
+ [
+ {
+ startAddr: 0x3000,
+ words: [
+ 0xf025,
+ ],
+ },
+ ]
]);
});
@@ -351,11 +354,16 @@ describe('assembler', () => {
return expect(assembler.assemble(fp)).resolves.toEqual([
{
- startAddr: 0x3000,
- words: [
- 0b0000111111111111,
- ],
+ fun: 0x3000,
},
+ [
+ {
+ startAddr: 0x3000,
+ words: [
+ 0b0000111111111111,
+ ],
+ },
+ ]
]);
});
@@ -372,26 +380,31 @@ describe('assembler', () => {
return expect(assembler.assemble(fp)).resolves.toEqual([
{
- startAddr: 0x3000,
- words: [
- 0b1110000000000010,
- 0xf022,
- 0xf025,
- 'h'.charCodeAt(0),
- 'e'.charCodeAt(0),
- 'l'.charCodeAt(0),
- 'l'.charCodeAt(0),
- 'o'.charCodeAt(0),
- ' '.charCodeAt(0),
- 'w'.charCodeAt(0),
- 'o'.charCodeAt(0),
- 'r'.charCodeAt(0),
- 'l'.charCodeAt(0),
- 'd'.charCodeAt(0),
- '!'.charCodeAt(0),
- 0,
- ],
+ mystring: 0x3003,
},
+ [
+ {
+ startAddr: 0x3000,
+ words: [
+ 0b1110000000000010,
+ 0xf022,
+ 0xf025,
+ 'h'.charCodeAt(0),
+ 'e'.charCodeAt(0),
+ 'l'.charCodeAt(0),
+ 'l'.charCodeAt(0),
+ 'o'.charCodeAt(0),
+ ' '.charCodeAt(0),
+ 'w'.charCodeAt(0),
+ 'o'.charCodeAt(0),
+ 'r'.charCodeAt(0),
+ 'l'.charCodeAt(0),
+ 'd'.charCodeAt(0),
+ '!'.charCodeAt(0),
+ 0,
+ ],
+ },
+ ],
]);
});
@@ -408,18 +421,24 @@ describe('assembler', () => {
return expect(assembler.assemble(fp)).resolves.toEqual([
{
- startAddr: 0x3000,
- words: [
- 0xf025,
- ],
- },
- {
- startAddr: 0x4000,
- words: [
- 0b0101001010111101,
- 0xf025,
- ],
+ haltme: 0x3000,
+ halt2: 0x4001,
},
+ [
+ {
+ startAddr: 0x3000,
+ words: [
+ 0xf025,
+ ],
+ },
+ {
+ startAddr: 0x4000,
+ words: [
+ 0b0101001010111101,
+ 0xf025,
+ ],
+ },
+ ],
]);
});
@@ -435,15 +454,20 @@ describe('assembler', () => {
return expect(assembler.assemble(fp)).resolves.toEqual([
{
- startAddr: 0x3000,
- words: [
- 0b0001100101000011,
- 0b0001100101100011,
- 0b0101110011000010,
- 0b0101110011100010,
- 0b1001011100111111,
- ],
+ asdf: 0x3004,
},
+ [
+ {
+ startAddr: 0x3000,
+ words: [
+ 0b0001100101000011,
+ 0b0001100101100011,
+ 0b0101110011000010,
+ 0b0101110011100010,
+ 0b1001011100111111,
+ ],
+ },
+ ],
]);
});
@@ -471,31 +495,44 @@ describe('assembler', () => {
return expect(assembler.assemble(fp)).resolves.toEqual([
{
- startAddr: 0x3000,
- words: [
- // nop
- 0b0000000000000000,
-
- // br
- 0b0000001111111111,
- 0b0000010111111111,
- 0b0000011111111111,
- 0b0000100111111111,
- 0b0000101111111111,
- 0b0000110111111111,
- 0b0000111111111111,
- 0b0000111111111111,
-
- // jmp/jsr/ret
- 0b1100000011000000,
- 0b0100111111111110,
- 0b0100000101000000,
- 0b1100000111000000,
-
- // trap
- 0b1111000001101001,
- ],
+ asdf0: 0x3001,
+ asdf1: 0x3002,
+ asdf2: 0x3003,
+ asdf3: 0x3004,
+ asdf4: 0x3005,
+ asdf5: 0x3006,
+ asdf6: 0x3007,
+ asdf7: 0x3008,
+ subr: 0x3009,
},
+ [
+ {
+ startAddr: 0x3000,
+ words: [
+ // nop
+ 0b0000000000000000,
+
+ // br
+ 0b0000001111111111,
+ 0b0000010111111111,
+ 0b0000011111111111,
+ 0b0000100111111111,
+ 0b0000101111111111,
+ 0b0000110111111111,
+ 0b0000111111111111,
+ 0b0000111111111111,
+
+ // jmp/jsr/ret
+ 0b1100000011000000,
+ 0b0100111111111110,
+ 0b0100000101000000,
+ 0b1100000111000000,
+
+ // trap
+ 0b1111000001101001,
+ ],
+ },
+ ],
]);
});
@@ -514,20 +551,29 @@ describe('assembler', () => {
return expect(assembler.assemble(fp)).resolves.toEqual([
{
- startAddr: 0x3000,
- words: [
- // loads
- 0b0010011111111111,
- 0b1010100111111111,
- 0b1110010111111111,
- 0b0110001101111100,
-
- // stores
- 0b0011011111111111,
- 0b1011100111111111,
- 0b0111001101111100,
- ],
+ asdf0: 0x3000,
+ asdf1: 0x3001,
+ asdf2: 0x3002,
+ asdf3: 0x3004,
+ asdf4: 0x3005,
},
+ [
+ {
+ startAddr: 0x3000,
+ words: [
+ // loads
+ 0b0010011111111111,
+ 0b1010100111111111,
+ 0b1110010111111111,
+ 0b0110001101111100,
+
+ // stores
+ 0b0011011111111111,
+ 0b1011100111111111,
+ 0b0111001101111100,
+ ],
+ },
+ ],
]);
});
@@ -542,16 +588,19 @@ describe('assembler', () => {
fp.push(null)
return expect(assembler.assemble(fp)).resolves.toEqual([
- {
- startAddr: 0x3000,
- words: [
- 0xf020,
- 0xf021,
- 0xf022,
- 0xf023,
- 0xf025,
- ],
- },
+ {},
+ [
+ {
+ startAddr: 0x3000,
+ words: [
+ 0xf020,
+ 0xf021,
+ 0xf022,
+ 0xf023,
+ 0xf025,
+ ],
+ },
+ ],
]);
});
@@ -569,21 +618,26 @@ describe('assembler', () => {
return expect(assembler.assemble(fp)).resolves.toEqual([
{
- startAddr: 0x5000,
- words: [
- 0x0,
- 0x0,
- 0x0,
- 0x1337,
- 0x0,
- 0xfffe,
- 0x5006,
- 0x0,
- 'h'.charCodeAt(0),
- 'i'.charCodeAt(0),
- 0x0,
- ],
+ label: 0x5006,
},
+ [
+ {
+ startAddr: 0x5000,
+ words: [
+ 0x0,
+ 0x0,
+ 0x0,
+ 0x1337,
+ 0x0,
+ 0xfffe,
+ 0x5006,
+ 0x0,
+ 'h'.charCodeAt(0),
+ 'i'.charCodeAt(0),
+ 0x0,
+ ],
+ },
+ ],
]);
});
@@ -622,12 +676,15 @@ describe('assembler', () => {
fp.push(null);
return expect(assembler.assemble(fp)).resolves.toEqual([
- {
- startAddr: 0x3000,
- words: [
- 0b0001000000110000,
- ],
- },
+ {},
+ [
+ {
+ startAddr: 0x3000,
+ words: [
+ 0b0001000000110000,
+ ],
+ },
+ ],
]);
});
@@ -656,12 +713,15 @@ describe('assembler', () => {
fp.push(null);
return expect(assembler.assemble(fp)).resolves.toEqual([
- {
- startAddr: 0x3000,
- words: [
- 0b0001000000101111,
- ],
- },
+ {},
+ [
+ {
+ startAddr: 0x3000,
+ words: [
+ 0b0001000000101111,
+ ],
+ },
+ ],
]);
});
@@ -719,18 +779,21 @@ describe('assembler', () => {
fp.push(null);
return expect(assembler.assemble(fp)).resolves.toEqual([
- {
- startAddr: 0x4001,
- words: [
- 0x0,
- ],
- },
- {
- startAddr: 0x4000,
- words: [
- 0x0,
- ],
- },
+ {},
+ [
+ {
+ startAddr: 0x4001,
+ words: [
+ 0x0,
+ ],
+ },
+ {
+ startAddr: 0x4000,
+ words: [
+ 0x0,
+ ],
+ },
+ ],
]);
});
@@ -858,25 +921,38 @@ describe('assembler', () => {
});
describe('assembleTo(inFp, outFp)', () => {
+ interface Buf {
+ buf: Uint8Array;
+ len: number;
+ }
+
let outFp: Writable;
- let outActual: Buffer;
- let outActualLen: number;
+ let outBuf: Buf;
+ let symFp: Writable;
+ let symBuf: Buf;
- beforeEach(() => {
- outActualLen = 0;
- outActual = Buffer.alloc(1024);
- outFp = new Writable({
+ const encode = (s: string) => new Uint8Array(s.split('').map(c => c.charCodeAt(0)));
+
+ function mockFp(): [Writable, Buf] {
+ const buf: Buf = {len: 0, buf: new Uint8Array(1024)};
+ const writable = new Writable({
write(arr, encoding, callback) {
for (let i = 0; i < arr.length; i++) {
- if (outActualLen === outActual.length) {
+ if (buf.len === buf.buf.length) {
throw new Error('object file too big');
}
- outActual[outActualLen++] = arr[i];
+ buf.buf[buf.len++] = arr[i];
}
callback();
},
});
+ return [writable, buf];
+ }
+
+ beforeEach(() => {
+ [outFp, outBuf] = mockFp();
+ [symFp, symBuf] = mockFp();
});
it('generates object file for minimal program', () => {
@@ -885,14 +961,18 @@ describe('assembler', () => {
fp.push('.end\n');
fp.push(null);
- return assembler.assembleTo(fp, outFp).then(() => {
+ return assembler.assembleTo(fp, outFp, symFp).then(() => {
let exp = new Uint8Array([
0x30,0x00,
0x00,0x01,
0xf0,0x25,
]);
- expect(outActualLen).toEqual(exp.length);
- expect(outActual.slice(0, outActualLen).equals(exp)).toBe(true);
+ expect(outBuf.len).toEqual(exp.length);
+ expect(outBuf.buf.slice(0, outBuf.len)).toEqual(exp);
+
+ let expSym = new Uint8Array();
+ expect(symBuf.len).toEqual(expSym.length);
+ expect(symBuf.buf.slice(0, symBuf.len)).toEqual(expSym);
});
});
@@ -907,7 +987,7 @@ describe('assembler', () => {
fp.push('.end\n')
fp.push(null)
- return assembler.assembleTo(fp, outFp).then(() => {
+ return assembler.assembleTo(fp, outFp, symFp).then(() => {
let exp = new Uint8Array([
0x30,0x00,
0x00,0x10,
@@ -928,8 +1008,12 @@ describe('assembler', () => {
0x00,'!'.charCodeAt(0),
0x00,0x00,
]);
- expect(outActualLen).toEqual(exp.length);
- expect(outActual.slice(0, outActualLen).equals(exp)).toBe(true);
+ expect(outBuf.len).toEqual(exp.length);
+ expect(outBuf.buf.slice(0, outBuf.len)).toEqual(exp);
+
+ let expSym = encode("3003\tmystring\n");
+ expect(symBuf.len).toEqual(expSym.length);
+ expect(symBuf.buf.slice(0, symBuf.len)).toEqual(expSym);
});
});
@@ -943,16 +1027,16 @@ describe('assembler', () => {
fp.push('.fill 0\n');
fp.push('.end\n');
fp.push('.orig x3000\n');
- fp.push('halt\n');
+ fp.push('dinkleberg halt\n');
fp.push('.end\n');
fp.push('.orig x5000\n');
fp.push('and r0, r0, 0\n');
- fp.push('add r0, r0, 1\n');
+ fp.push('tuba add r0, r0, 1\n');
fp.push('halt\n');
fp.push('.end\n');
fp.push(null);
- return assembler.assembleTo(fp, outFp).then(() => {
+ return assembler.assembleTo(fp, outFp, symFp).then(() => {
let exp = new Uint8Array([
0x80,0x00,
0x00,0x06,
@@ -973,8 +1057,14 @@ describe('assembler', () => {
0x10,0x21,
0xf0,0x25,
]);
- expect(outActualLen).toEqual(exp.length);
- expect(outActual.slice(0, outActualLen).equals(exp)).toBe(true);
+ expect(outBuf.len).toEqual(exp.length);
+ expect(outBuf.buf.slice(0, outBuf.len)).toEqual(exp);
+
+ let expSym = encode("3000\tdinkleberg\n" +
+ "5001\ttuba\n" +
+ "8003\thi\n");
+ expect(symBuf.len).toEqual(expSym.length);
+ expect(symBuf.buf.slice(0, symBuf.len)).toEqual(expSym);
});
});
});
@@ -1071,12 +1161,15 @@ describe('assembler', () => {
fp.push(null)
return expect(assembler.assemble(fp)).resolves.toEqual([
- {
- startAddr: 0x0,
- words: [
- 0x70000000,
- ],
- },
+ {},
+ [
+ {
+ startAddr: 0x0,
+ words: [
+ 0x70000000,
+ ],
+ },
+ ],
]);
});
@@ -1087,11 +1180,16 @@ describe('assembler', () => {
return expect(assembler.assemble(fp)).resolves.toEqual([
{
- startAddr: 0x0,
- words: [
- 0x500fffff,
- ],
+ fun: 0x00,
},
+ [
+ {
+ startAddr: 0x0,
+ words: [
+ 0x500fffff,
+ ],
+ },
+ ],
]);
});
@@ -1104,14 +1202,19 @@ describe('assembler', () => {
return expect(assembler.assemble(fp)).resolves.toEqual([
{
- startAddr: 0x0,
- words: [
- 0x00000000,
- 0x06900003,
- 0x2b700025,
- 0x12a00001,
- ],
+ asdf: 0x03,
},
+ [
+ {
+ startAddr: 0x0,
+ words: [
+ 0x00000000,
+ 0x06900003,
+ 0x2b700025,
+ 0x12a00001,
+ ],
+ },
+ ],
]);
});
@@ -1122,12 +1225,17 @@ describe('assembler', () => {
return expect(assembler.assemble(fp)).resolves.toEqual([
{
- startAddr: 0x0,
- words: [
- 0x526fffff,
- 0x61f00000,
- ],
+ asdf0: 0x00,
},
+ [
+ {
+ startAddr: 0x0,
+ words: [
+ 0x526fffff,
+ 0x61f00000,
+ ],
+ },
+ ],
]);
});
@@ -1137,13 +1245,16 @@ describe('assembler', () => {
fp.push(null)
return expect(assembler.assemble(fp)).resolves.toEqual([
- {
- startAddr: 0x0,
- words: [
- 0x36200021,
- 0x498ffffb,
- ],
- },
+ {},
+ [
+ {
+ startAddr: 0x0,
+ words: [
+ 0x36200021,
+ 0x498ffffb,
+ ],
+ },
+ ],
]);
});
@@ -1156,15 +1267,20 @@ describe('assembler', () => {
return expect(assembler.assemble(fp)).resolves.toEqual([
{
- startAddr: 0x0,
- words: [
- // Sign extends
- 0xf0006969 & -1,
- 0xfffffffe & -1,
- 0x00000420,
- 0x00000001,
- ],
+ xd: 0x01,
},
+ [
+ {
+ startAddr: 0x0,
+ words: [
+ // Sign extends
+ 0xf0006969 & -1,
+ 0xfffffffe & -1,
+ 0x00000420,
+ 0x00000001,
+ ],
+ },
+ ],
]);
});
@@ -1179,17 +1295,22 @@ describe('assembler', () => {
return expect(assembler.assemble(fp)).resolves.toEqual([
{
- startAddr: 0x0,
- words: [
- 0x66600000,
- 0x26000005,
- 0x00000000,
- 0x00000000,
- 0x00000000,
- 0x00000000,
- 0x00000069,
- ],
+ trevor: 0x06,
},
+ [
+ {
+ startAddr: 0x0,
+ words: [
+ 0x66600000,
+ 0x26000005,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000069,
+ ],
+ },
+ ],
]);
});
@@ -1228,12 +1349,15 @@ describe('assembler', () => {
fp.push(null);
return expect(assembler.assemble(fp)).resolves.toEqual([
- {
- startAddr: 0x0,
- words: [
- 0x20080000,
- ],
- },
+ {},
+ [
+ {
+ startAddr: 0x0,
+ words: [
+ 0x20080000,
+ ],
+ },
+ ],
]);
});
@@ -1256,12 +1380,15 @@ describe('assembler', () => {
fp.push(null);
return expect(assembler.assemble(fp)).resolves.toEqual([
- {
- startAddr: 0x0,
- words: [
- 0x2007ffff,
- ],
- },
+ {},
+ [
+ {
+ startAddr: 0x0,
+ words: [
+ 0x2007ffff,
+ ],
+ },
+ ],
]);
});
diff --git a/novice/assembler/assembler.ts b/novice/assembler/assembler.ts
index 0ce1bfa..1f4c0c4 100644
--- a/novice/assembler/assembler.ts
+++ b/novice/assembler/assembler.ts
@@ -1,5 +1,5 @@
import { Readable, Writable } from 'stream';
-import { Assembly, Isa } from '../isa';
+import { Assembly, Isa, SymbTable } from '../isa';
import { MachineCodeGenerator, MachineCodeSection } from './codegen';
import { PseudoOpSpec } from './opspec';
import { Parser } from './parsers';
@@ -24,17 +24,20 @@ class Assembler {
return await this.cfg.parser.parse(fp);
}
- public codegen(asm: Assembly): MachineCodeSection[] {
+ public codegen(asm: Assembly): [SymbTable, MachineCodeSection[]] {
return this.cfg.generator.gen(this.cfg.isa, this.cfg.opSpec, asm);
}
- public async assemble(fp: Readable): Promise<MachineCodeSection[]> {
+ public async assemble(fp: Readable):
+ Promise<[SymbTable, MachineCodeSection[]]> {
return this.codegen(await this.parse(fp));
}
- public async assembleTo(inFp: Readable, outFp: Writable): Promise<void> {
- this.cfg.serializer.serialize(
- this.cfg.isa, await this.assemble(inFp), outFp);
+ public async assembleTo(inFp: Readable, outFp: Writable,
+ symbFp: Writable): Promise<void> {
+ const [symbtable, code] = await this.assemble(inFp);
+ this.cfg.serializer.serialize(this.cfg.isa, code, outFp);
+ this.cfg.serializer.serializeSymb(symbtable, symbFp);
}
}
diff --git a/novice/assembler/codegen/base.ts b/novice/assembler/codegen/base.ts
index 1f53f10..6d661f1 100644
--- a/novice/assembler/codegen/base.ts
+++ b/novice/assembler/codegen/base.ts
@@ -1,5 +1,5 @@
import { AliasFields, AliasSpec, Assembly, Instruction, InstructionSpec,
- Isa, PseudoOp, Section } from '../../isa';
+ Isa, PseudoOp, Section, SymbTable } from '../../isa';
import { AsmContext, OpOperands, OpSpec, PseudoOpSpec } from '../opspec';
import { MachineCodeGenerator, MachineCodeSection } from './codegen';
@@ -10,11 +10,9 @@ interface ReassembleVictim {
instrIdx: number;
}
-type SymbTable = {[s: string]: number};
-
class BaseMachineCodeGenerator implements MachineCodeGenerator {
public gen(isa: Isa, opSpec: PseudoOpSpec, asm: Assembly):
- MachineCodeSection[] {
+ [SymbTable, MachineCodeSection[]] {
const sections: MachineCodeSection[] = [];
const symbtable: SymbTable = {};
const reassemble: ReassembleVictim[] = [];
@@ -78,7 +76,7 @@ class BaseMachineCodeGenerator implements MachineCodeGenerator {
}
}
- return sections;
+ return [symbtable, sections];
}
private inflateInstr(isa: Isa,
diff --git a/novice/assembler/codegen/codegen.ts b/novice/assembler/codegen/codegen.ts
index 9c0cef0..4723664 100644
--- a/novice/assembler/codegen/codegen.ts
+++ b/novice/assembler/codegen/codegen.ts
@@ -1,4 +1,4 @@
-import { Assembly, Isa } from '../../isa';
+import { Assembly, Isa, SymbTable } from '../../isa';
import { PseudoOpSpec } from '../opspec';
interface MachineCodeSection {
@@ -7,7 +7,8 @@ interface MachineCodeSection {
}
interface MachineCodeGenerator {
- gen(isa: Isa, opSpec: PseudoOpSpec, asm: Assembly): MachineCodeSection[];
+ gen(isa: Isa, opSpec: PseudoOpSpec, asm: Assembly):
+ [SymbTable, MachineCodeSection[]];
}
export { MachineCodeSection, MachineCodeGenerator };
diff --git a/novice/assembler/serializers/complx.ts b/novice/assembler/serializers/complx.ts
index a9cf581..f22512a 100644
--- a/novice/assembler/serializers/complx.ts
+++ b/novice/assembler/serializers/complx.ts
@@ -1,5 +1,5 @@
import { Writable } from 'stream';
-import { Isa } from '../../isa';
+import { Isa, SymbTable } from '../../isa';
import { MachineCodeSection } from '../codegen';
import { Serializer } from './serializer';
@@ -17,6 +17,18 @@ class ComplxObjectFileSerializer implements Serializer {
}
}
+ public symbFileExt(): string { return 'sym'; }
+
+ public serializeSymb(symbtable: SymbTable, fp: Writable) {
+ const symbols = Object.keys(symbtable);
+ symbols.sort((leftSymb, rightSymb) =>
+ symbtable[leftSymb] - symbtable[rightSymb]);
+
+ for (const symb of symbols) {
+ fp.write(`${symbtable[symb].toString(16)}\t${symb}\n`);
+ }
+ }
+
// Big endian
private writeWord(isa: Isa, word: number, fp: Writable): void {
const numBytes = Math.ceil(isa.mem.word / 8);
diff --git a/novice/assembler/serializers/serializer.ts b/novice/assembler/serializers/serializer.ts
index 333c643..4b6d715 100644
--- a/novice/assembler/serializers/serializer.ts
+++ b/novice/assembler/serializers/serializer.ts
@@ -1,11 +1,15 @@
import { Writable } from 'stream';
-import { Isa } from '../../isa';
+import { Isa, SymbTable } from '../../isa';
import { MachineCodeSection } from '../codegen';
interface Serializer {
serialize(isa: Isa, code: MachineCodeSection[], fp: Writable): void;
// default file extension for this format. No leading dot please
fileExt(): string;
+
+ serializeSymb(symbtable: SymbTable, fp: Writable): void;
+ // default file extension for this format. No leading dot please
+ symbFileExt(): string;
}
export { Serializer };
diff --git a/novice/cli.test.ts b/novice/cli.test.ts
index ecfd215..6c060e1 100644
--- a/novice/cli.test.ts
+++ b/novice/cli.test.ts
@@ -62,6 +62,7 @@ describe('cli', () => {
describe('asm subcommand', () => {
let mockInFp: Readable;
let mockOutFp: Writable;
+ let mockSymbFp: Writable;
let mockAssembleTo: (inFp: Readable, outFp: Writable) => Promise<void>;
let mockConfig: AssemblerConfig;
let mockSerializer: Serializer;
@@ -81,15 +82,28 @@ describe('cli', () => {
// @ts-ignore
mockOutFp = {
+ // @ts-ignore
+ whoami: 'out',
+ cork: () => {},
+ uncork: () => {},
+ end: () => {},
+ };
+ // @ts-ignore
+ fs.createWriteStream.mockReturnValueOnce(mockOutFp);
+
+ // @ts-ignore
+ mockSymbFp = {
+ // @ts-ignore
+ whoami: 'symb',
cork: () => {},
uncork: () => {},
end: () => {},
};
// @ts-ignore
- fs.createWriteStream.mockReturnValue(mockOutFp);
+ fs.createWriteStream.mockReturnValueOnce(mockSymbFp);
// @ts-ignore
- mockAssembleTo = jest.fn((inFp: Readable, outFp: Writable) => Promise.resolve());
+ mockAssembleTo = jest.fn((inFp: Readable, outFp: Writable, symbFp: Writable) => Promise.resolve());
// @ts-ignore
Assembler.mockImplementation((cfg: AssemblerConfig) => {
return { assembleTo: mockAssembleTo };
@@ -99,13 +113,17 @@ describe('cli', () => {
// @ts-ignore
serializer: {
fileExt: () => 'star',
+ symbFileExt: () => 'squidward',
},
};
// @ts-ignore
getConfig.mockReturnValue(mockConfig);
// @ts-ignore
- mockSerializer = {fileExt: () => 'tl6'};
+ mockSerializer = {
+ fileExt: () => 'tl6',
+ symbFileExt: () => 'anime'
+ };
// @ts-ignore
getSerializer.mockReturnValue(mockSerializer);
});
@@ -116,7 +134,7 @@ describe('cli', () => {
// @ts-ignore
expect(fs.createReadStream.mock.calls).toEqual([['patrick.asm']]);
// @ts-ignore
- expect(fs.createWriteStream.mock.calls).toEqual([['patrick.star']]);
+ expect(fs.createWriteStream.mock.calls).toEqual([['patrick.star'], ['patrick.squidward']]);
// @ts-ignore
expect(getConfig.mock.calls).toEqual([['bread']]);
// @ts-ignore
@@ -124,7 +142,7 @@ describe('cli', () => {
// @ts-ignore
expect(Assembler.mock.calls).toEqual([[mockConfig]]);
// @ts-ignore
- expect(mockAssembleTo.mock.calls).toEqual([[mockInFp, mockOutFp]]);
+ expect(mockAssembleTo.mock.calls).toEqual([[mockInFp, mockOutFp, mockSymbFp]]);
expect(exitCode).toEqual(0);
expect(stdoutActual).toEqual('');
@@ -138,7 +156,7 @@ describe('cli', () => {
// @ts-ignore
expect(fs.createReadStream.mock.calls).toEqual([['chickenworld']]);
// @ts-ignore
- expect(fs.createWriteStream.mock.calls).toEqual([['chickenworld.star']]);
+ expect(fs.createWriteStream.mock.calls).toEqual([['chickenworld.star'], ['chickenworld.squidward']]);
// @ts-ignore
expect(getConfig.mock.calls).toEqual([['bread']]);
// @ts-ignore
@@ -146,7 +164,7 @@ describe('cli', () => {
// @ts-ignore
expect(Assembler.mock.calls).toEqual([[mockConfig]]);
// @ts-ignore
- expect(mockAssembleTo.mock.calls).toEqual([[mockInFp, mockOutFp]]);
+ expect(mockAssembleTo.mock.calls).toEqual([[mockInFp, mockOutFp, mockSymbFp]]);
expect(exitCode).toEqual(0);
expect(stdoutActual).toEqual('');
@@ -160,7 +178,7 @@ describe('cli', () => {
// @ts-ignore
expect(fs.createReadStream.mock.calls).toEqual([['/home/travis.adams/chickenworld']]);
// @ts-ignore
- expect(fs.createWriteStream.mock.calls).toEqual([['/home/travis.adams/chickenworld.star']]);
+ expect(fs.createWriteStream.mock.calls).toEqual([['/home/travis.adams/chickenworld.star'], ['/home/travis.adams/chickenworld.squidward']]);
// @ts-ignore
expect(getConfig.mock.calls).toEqual([['bread']]);
// @ts-ignore
@@ -168,7 +186,7 @@ describe('cli', () => {
// @ts-ignore
expect(Assembler.mock.calls).toEqual([[mockConfig]]);
// @ts-ignore
- expect(mockAssembleTo.mock.calls).toEqual([[mockInFp, mockOutFp]]);
+ expect(mockAssembleTo.mock.calls).toEqual([[mockInFp, mockOutFp, mockSymbFp]]);
expect(exitCode).toEqual(0);
expect(stdoutActual).toEqual('');
@@ -182,7 +200,7 @@ describe('cli', () => {
// @ts-ignore
expect(fs.createReadStream.mock.calls).toEqual([['chickenworld.asm']]);
// @ts-ignore
- expect(fs.createWriteStream.mock.calls).toEqual([['jeff']]);
+ expect(fs.createWriteStream.mock.calls).toEqual([['jeff'], ['jeff.squidward']]);
// @ts-ignore
expect(getConfig.mock.calls).toEqual([['bread']]);
// @ts-ignore
@@ -190,7 +208,7 @@ describe('cli', () => {
// @ts-ignore
expect(Assembler.mock.calls).toEqual([[mockConfig]]);
// @ts-ignore
- expect(mockAssembleTo.mock.calls).toEqual([[mockInFp, mockOutFp]]);
+ expect(mockAssembleTo.mock.calls).toEqual([[mockInFp, mockOutFp, mockSymbFp]]);
expect(exitCode).toEqual(0);
expect(stdoutActual).toEqual('');
@@ -204,7 +222,7 @@ describe('cli', () => {
// @ts-ignore
expect(fs.createReadStream.mock.calls).toEqual([['chickenworld.asm']]);
// @ts-ignore
- expect(fs.createWriteStream.mock.calls).toEqual([['chickenworld.star']]);
+ expect(fs.createWriteStream.mock.calls).toEqual([['chickenworld.star'], ['chickenworld.squidward']]);
// @ts-ignore
expect(getConfig.mock.calls).toEqual([['lc3']]);
// @ts-ignore
@@ -212,7 +230,7 @@ describe('cli', () => {
// @ts-ignore
expect(Assembler.mock.calls).toEqual([[mockConfig]]);
// @ts-ignore
- expect(mockAssembleTo.mock.calls).toEqual([[mockInFp, mockOutFp]]);
+ expect(mockAssembleTo.mock.calls).toEqual([[mockInFp, mockOutFp, mockSymbFp]]);
expect(exitCode).toEqual(0);
expect(stdoutActual).toEqual('');
@@ -226,7 +244,7 @@ describe('cli', () => {
// @ts-ignore
expect(fs.createReadStream.mock.calls).toEqual([['chickenworld.asm']]);
// @ts-ignore
- expect(fs.createWriteStream.mock.calls).toEqual([['chickenworld.tl6']]);
+ expect(fs.createWriteStream.mock.calls).toEqual([['chickenworld.tl6'], ['chickenworld.anime']]);
// @ts-ignore
expect(getConfig.mock.calls).toEqual([['lc3']]);
// @ts-ignore
@@ -234,7 +252,7 @@ describe('cli', () => {
// @ts-ignore
expect(Assembler.mock.calls).toEqual([[mockConfig]]);
// @ts-ignore
- expect(mockAssembleTo.mock.calls).toEqual([[mockInFp, mockOutFp]]);
+ expect(mockAssembleTo.mock.calls).toEqual([[mockInFp, mockOutFp, mockSymbFp]]);
expect(mockConfig.serializer).toBe(mockSerializer);
diff --git a/novice/cli.ts b/novice/cli.ts
index 33ff5b1..2385477 100644
--- a/novice/cli.ts
+++ b/novice/cli.ts
@@ -84,6 +84,7 @@ async function asm(configName: string, inPath: string,
if (!outPath) {
outPath = removeExt(inPath) + '.' + cfg.serializer.fileExt();
}
+ const symbPath = removeExt(outPath) + '.' + cfg.serializer.symbFileExt();
const inFp = fs.createReadStream(inPath);
await new Promise((resolve, reject) => {
@@ -91,12 +92,15 @@ async function asm(configName: string, inPath: string,
inFp.on('error', reject);
});
const outFp = fs.createWriteStream(outPath);
+ const symbFp = fs.createWriteStream(symbPath);
const assembler = new Assembler(cfg);
// Buffer so we don't make 1,000,000 syscalls
outFp.cork();
- await assembler.assembleTo(inFp, outFp);
+ symbFp.cork();
+ await assembler.assembleTo(inFp, outFp, symbFp);
outFp.uncork();
+ symbFp.uncork();
outFp.end();
return 0;
diff --git a/novice/isa/index.ts b/novice/isa/index.ts
index 1fe4eb6..ce21cd8 100644
--- a/novice/isa/index.ts
+++ b/novice/isa/index.ts
@@ -3,7 +3,8 @@ import { Assembly, Instruction, IntegerOperand, LabelOperand, PseudoOp,
import { DummyIsa } from './dummy';
import { IO, StreamIO } from './io';
import { AliasContext, AliasFields, AliasSpec, Fields, getRegAliases,
- InstructionSpec, Isa, isInstruction, Reg, regPrefixes } from './isa';
+ InstructionSpec, Isa, isInstruction, Reg, regPrefixes,
+ SymbTable } from './isa';
import { Lc2200Isa } from './lc2200';
import { Lc3Isa } from './lc3';
import { MachineStateDelta, MachineStateLogEntry } from './log';
@@ -31,7 +32,7 @@ export { getIsa, isas,
IO, StreamIO,
// isa
Isa, InstructionSpec, Fields, Reg, regPrefixes, getRegAliases,
- isInstruction, AliasContext, AliasFields, AliasSpec,
+ isInstruction, AliasContext, AliasFields, AliasSpec, SymbTable,
// state
RegIdentifier, MachineState, MachineStateUpdate,
// log
diff --git a/novice/isa/isa.ts b/novice/isa/isa.ts
index 6734e2f..9d7a2e7 100644
--- a/novice/isa/isa.ts
+++ b/novice/isa/isa.ts
@@ -79,10 +79,12 @@ interface AliasFields {
labels: {[s: string]: string};
}
+type SymbTable = {[s: string]: number};
+
interface AliasContext {
pc: number;
line: number;
- symbtable: {[s: string]: number};
+ symbtable: SymbTable;
}
interface AliasSpec {
@@ -126,4 +128,4 @@ function isInstruction(isa: Isa, op: string): boolean {
}
export { Isa, Fields, InstructionSpec, Reg, regPrefixes, getRegAliases,
- isInstruction, AliasContext, AliasFields, AliasSpec };
+ isInstruction, AliasContext, AliasFields, AliasSpec, SymbTable };