mirror of
https://github.com/sehugg/8bitworkshop.git
synced 2025-04-06 17:39:40 +00:00
new inline verilog assembler
This commit is contained in:
parent
1790ca1747
commit
f6d320a05b
@ -41,7 +41,7 @@ module frame_buffer_top(clk, reset, hsync, vsync, hpaddle, vpaddle,
|
||||
parameter IN_VPU = 8'b01000011;
|
||||
|
||||
reg [7:0] ram[0:63];
|
||||
reg [7:0] rom[0:255];
|
||||
reg [7:0] rom[0:127];
|
||||
|
||||
output wire [7:0] address_bus;
|
||||
output reg [7:0] to_cpu;
|
||||
@ -95,7 +95,7 @@ module frame_buffer_top(clk, reset, hsync, vsync, hpaddle, vpaddle,
|
||||
vsync, hsync, vpaddle, hpaddle, display_on};
|
||||
IN_VPU: to_cpu = {vpu_ram[vpu_write], vpu_ram[vpu_write+1]};
|
||||
// ROM
|
||||
8'b1???????: to_cpu = rom[address_bus[6:0] + 128];
|
||||
8'b1???????: to_cpu = rom[address_bus[7:0] + 128];
|
||||
default: ;
|
||||
endcase
|
||||
|
||||
@ -131,7 +131,8 @@ module frame_buffer_top(clk, reset, hsync, vsync, hpaddle, vpaddle,
|
||||
__asm
|
||||
.arch femto8
|
||||
.org 128
|
||||
|
||||
.len 128
|
||||
|
||||
.define VPU_LO 8
|
||||
.define VPU_HI 9
|
||||
.define VPU_WRITE 10
|
||||
@ -152,25 +153,24 @@ Start:
|
||||
sta VPU_LO
|
||||
sta VPU_HI
|
||||
sta 0
|
||||
sta 1
|
||||
DisplayLoop:
|
||||
zero B
|
||||
movrb A
|
||||
mov A,[b]
|
||||
sta VPU_WRITE
|
||||
sta VPU_MOVE
|
||||
sta VPU_WRITE
|
||||
sta VPU_MOVE
|
||||
sta VPU_WRITE
|
||||
sta VPU_MOVE
|
||||
lia F_VSYNC
|
||||
lib IN_FLAGS
|
||||
andrb NOP
|
||||
lda #F_VSYNC
|
||||
ldb #IN_FLAGS
|
||||
and none,[B]
|
||||
bz DisplayLoop
|
||||
WaitVsync:
|
||||
andrb NOP
|
||||
and none,[B]
|
||||
bnz WaitVsync
|
||||
zero B
|
||||
movrb A
|
||||
mov A,[b]
|
||||
inc A
|
||||
sta 0
|
||||
jmp DisplayLoop
|
||||
|
@ -52,7 +52,7 @@ module racing_game_cpu_top(clk, reset, hsync, vsync, hpaddle, vpaddle,
|
||||
parameter IN_FLAGS = 8'b01000010;
|
||||
|
||||
reg [7:0] ram[0:63];
|
||||
reg [7:0] rom[0:255];
|
||||
reg [7:0] rom[0:127];
|
||||
|
||||
output wire [7:0] address_bus;
|
||||
output reg [7:0] to_cpu;
|
||||
@ -80,7 +80,7 @@ module racing_game_cpu_top(clk, reset, hsync, vsync, hpaddle, vpaddle,
|
||||
IN_FLAGS: to_cpu = {2'b0, frame_collision,
|
||||
vsync, hsync, vpaddle, hpaddle, display_on};
|
||||
// ROM
|
||||
8'b1???????: to_cpu = rom[address_bus[6:0] + 128];
|
||||
8'b1???????: to_cpu = rom[address_bus[6:0]];
|
||||
default: ;
|
||||
endcase
|
||||
|
||||
@ -154,6 +154,7 @@ module racing_game_cpu_top(clk, reset, hsync, vsync, hpaddle, vpaddle,
|
||||
__asm
|
||||
.arch femto8
|
||||
.org 128
|
||||
.len 128
|
||||
|
||||
.define PADDLE_X 0
|
||||
.define PADDLE_Y 1
|
||||
@ -178,11 +179,11 @@ module racing_game_cpu_top(clk, reset, hsync, vsync, hpaddle, vpaddle,
|
||||
.define F_COLLIDE 32
|
||||
|
||||
Start:
|
||||
lia 128
|
||||
lda #128
|
||||
sta PLAYER_X
|
||||
sta ENEMY_X
|
||||
sta ENEMY_Y
|
||||
lia 180
|
||||
sta ENEMY_Y
|
||||
lda #180
|
||||
sta PLAYER_Y
|
||||
zero A
|
||||
sta SPEED
|
||||
@ -190,67 +191,67 @@ Start:
|
||||
sta ENEMY_DIR
|
||||
; test hpaddle flag
|
||||
DisplayLoop:
|
||||
lia F_HPADDLE
|
||||
lib IN_FLAGS
|
||||
andrb NOP
|
||||
lda #F_HPADDLE
|
||||
ldb #IN_FLAGS
|
||||
and none,[B]
|
||||
bz DisplayLoop
|
||||
; [vpos] -> paddle_x
|
||||
lib IN_VPOS
|
||||
movrb A
|
||||
ldb #IN_VPOS
|
||||
mov A,[B]
|
||||
sta PLAYER_X
|
||||
; wait for vsync=1 then vsync=0
|
||||
lia F_VSYNC
|
||||
lib IN_FLAGS
|
||||
lda #F_VSYNC
|
||||
ldb #IN_FLAGS
|
||||
WaitForVsyncOn:
|
||||
andrb NOP
|
||||
and none,[B]
|
||||
bz WaitForVsyncOn
|
||||
WaitForVsyncOff:
|
||||
andrb NOP
|
||||
and none,[B]
|
||||
bnz WaitForVsyncOff
|
||||
; check collision
|
||||
lia F_COLLIDE
|
||||
lib IN_FLAGS
|
||||
andrb NOP
|
||||
lda #F_COLLIDE
|
||||
ldb #IN_FLAGS
|
||||
and none,[B]
|
||||
bz NoCollision
|
||||
; load slow speed
|
||||
lia 16
|
||||
lda #16
|
||||
sta SPEED
|
||||
NoCollision:
|
||||
; update speed
|
||||
lib SPEED
|
||||
movrb A
|
||||
ldb #SPEED
|
||||
mov A,[B]
|
||||
inc A
|
||||
; don't store if == 0
|
||||
bz MaxSpeed
|
||||
sta SPEED
|
||||
MaxSpeed:
|
||||
movrb A
|
||||
mov A,[B]
|
||||
lsr A
|
||||
lsr A
|
||||
lsr A
|
||||
lsr A
|
||||
; add to lo byte of track pos
|
||||
lib TRACKPOS_LO
|
||||
addrb B
|
||||
ldb #TRACKPOS_LO
|
||||
add B,[B]
|
||||
swapab
|
||||
sta TRACKPOS_LO
|
||||
swapab
|
||||
; update enemy vert pos
|
||||
lib ENEMY_Y
|
||||
addrb A
|
||||
ldb #ENEMY_Y
|
||||
add A,[B]
|
||||
sta ENEMY_Y
|
||||
; update enemy horiz pos
|
||||
lib ENEMY_X
|
||||
movrb A
|
||||
lib ENEMY_DIR
|
||||
addrb A
|
||||
ldb #ENEMY_X
|
||||
mov A,[B]
|
||||
ldb #ENEMY_DIR
|
||||
add A,[B]
|
||||
sta ENEMY_X
|
||||
subi A 64
|
||||
andi A 127
|
||||
sub A,#64
|
||||
and A,#127
|
||||
bnz SkipXReverse
|
||||
; load ENEMY_DIR and negate
|
||||
zero A
|
||||
subrb A
|
||||
sub A,[B]
|
||||
sta ENEMY_DIR
|
||||
; back to display loop
|
||||
SkipXReverse:
|
||||
|
@ -31,9 +31,6 @@ module starfield_top(clk, reset, hsync, vsync, rgb);
|
||||
.lfsr(lfsr));
|
||||
|
||||
wire star_on = &lfsr[15:9];
|
||||
wire r = display_on && star_on && lfsr[0];
|
||||
wire g = display_on && star_on && lfsr[1];
|
||||
wire b = display_on && star_on && lfsr[2];
|
||||
assign rgb = {b,g,r};
|
||||
assign rgb = display_on && star_on ? lfsr[2:0] : 0;
|
||||
|
||||
endmodule
|
||||
|
189
src/assembler.js
189
src/assembler.js
@ -1,189 +0,0 @@
|
||||
|
||||
EXAMPLE_SPEC = {
|
||||
vars:{
|
||||
reg:{bits:2, toks:['a', 'b', 'ip', 'none']},
|
||||
unop:{bits:3, toks:['mova','movb','inc','dec','asl','lsr','rol','ror']},
|
||||
binop:{bits:3, toks:['or','and','xor','zero','add','sub','adc','sbb']},
|
||||
imm4:{bits:4},
|
||||
imm8:{bits:8},
|
||||
rel:{bits:8, iprel:true, ipofs:1},
|
||||
},
|
||||
rules:[
|
||||
{fmt:'~binop ~reg,b', bits:['00',1,'1',0]},
|
||||
{fmt:'~binop ~reg,#~imm8', bits:['01',1,'1',0,2]},
|
||||
{fmt:'~binop ~reg,[b]', bits:['11',1,'1',0]},
|
||||
{fmt:'~unop ~reg', bits:['00',1,'0',0]},
|
||||
{fmt:'lda #~imm8', bits:['01','00','0001',0]},
|
||||
{fmt:'ldb #~imm8', bits:['01','01','0001',0]},
|
||||
{fmt:'jmp ~imm8', bits:['01','10','0001',0]},
|
||||
{fmt:'sta ~imm4', bits:['1001',0]},
|
||||
{fmt:'bcc ~rel', bits:['1010','0001',0]},
|
||||
{fmt:'bcs ~rel', bits:['1010','0011',0]},
|
||||
{fmt:'bz ~rel', bits:['1010','1101',0]},
|
||||
{fmt:'bnz ~rel', bits:['1010','0101',0]},
|
||||
{fmt:'clc', bits:['10001000']},
|
||||
{fmt:'swapab', bits:['10000001']},
|
||||
{fmt:'reset', bits:['10001111']},
|
||||
]
|
||||
}
|
||||
|
||||
function rule2regex(rule, vars) {
|
||||
var s = rule.fmt;
|
||||
var varlist = [];
|
||||
rule.prefix = s.split(/\s+/)[0];
|
||||
s = s.replace(/\s+/g, '\\s');
|
||||
s = s.replace(/\[/g, '\\[');
|
||||
s = s.replace(/\]/g, '\\]');
|
||||
s = s.replace(/\./g, '\\.');
|
||||
s = s.replace(/~\w+/g, function(varname) {
|
||||
varname = varname.substr(1);
|
||||
var v = vars[varname];
|
||||
varlist.push(varname);
|
||||
if (!v)
|
||||
throw Error("Could not find rule for ~" + varname);
|
||||
else if (v.toks)
|
||||
return '(\\w+)';
|
||||
else
|
||||
return '([0-9]+|[$][0-9a-f]+|\\w+)';
|
||||
});
|
||||
rule.re = new RegExp('^'+s+'$', 'i');
|
||||
rule.varlist = varlist;
|
||||
// TODO: check rule constraints
|
||||
return rule;
|
||||
}
|
||||
|
||||
var Assembler = function(spec) {
|
||||
var self = this;
|
||||
var ip = 0;
|
||||
var linenum = 0;
|
||||
var symbols = {};
|
||||
var errors = [];
|
||||
var outwords = [];
|
||||
var fixups = [];
|
||||
var width = 8;
|
||||
|
||||
for (var i=0; i<spec.rules.length; i++)
|
||||
rule2regex(spec.rules[i], spec.vars);
|
||||
|
||||
function warning(msg) {
|
||||
errors.push({msg:msg, line:linenum});
|
||||
}
|
||||
|
||||
function addBytes(result) {
|
||||
var op = result.opcode;
|
||||
var nb = result.nbits/width;
|
||||
for (var i=0; i<nb; i++)
|
||||
outwords[ip++] = (op >> (nb-1-i)*width) & ((1<<width)-1);
|
||||
}
|
||||
|
||||
self.buildInstruction = function(rule, m) {
|
||||
var opcode = 0;
|
||||
var oplen = 0;
|
||||
for (var i=0; i<rule.bits.length; i++) {
|
||||
var b = rule.bits[i];
|
||||
var n,x;
|
||||
if (b.length) {
|
||||
n = b.length;
|
||||
x = parseInt(b,2);
|
||||
} else {
|
||||
var id = m[b+1];
|
||||
var v = spec.vars[rule.varlist[b]];
|
||||
n = v.bits;
|
||||
if (v.toks) {
|
||||
x = v.toks.indexOf(id);
|
||||
if (x < 0)
|
||||
return null;
|
||||
} else {
|
||||
if (id.startsWith("$"))
|
||||
x = parseInt(id.substr(1), 16);
|
||||
else
|
||||
x = parseInt(id);
|
||||
// is it a label? add fixup
|
||||
if (isNaN(x)) {
|
||||
fixups.push({sym:id, ofs:ip, bitlen:n, bitofs:oplen, line:linenum, iprel:!!v.iprel, ipofs:(v.ipofs+0)});
|
||||
x = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
var mask = (1<<n)-1;
|
||||
if ((x&mask) != x)
|
||||
warning("Value " + x + " could not fit in " + n + " bits");
|
||||
opcode = (opcode << n) | x;
|
||||
oplen += n;
|
||||
}
|
||||
if (oplen == 0)
|
||||
warning("Opcode had zero length");
|
||||
else if ((oplen % width) != 0)
|
||||
warning("Opcode was not word-aligned (" + oplen + " bits)");
|
||||
return {opcode:opcode, nbits:oplen};
|
||||
}
|
||||
self.assemble = function(line) {
|
||||
linenum++;
|
||||
// remove comments
|
||||
line = line.replace(/[;].*/g, '');
|
||||
line = line.trim().toLowerCase();
|
||||
// is it a directive?
|
||||
if (line[0] == '.') {
|
||||
var tokens = line.split(/\s+/);
|
||||
if (tokens[0] == '.define')
|
||||
symbols[tokens[1]] = {value:tokens[2]};
|
||||
else
|
||||
warning("Unrecognized directive: " + line);
|
||||
return;
|
||||
}
|
||||
// find labels
|
||||
line = line.replace(/(\w+):/, function(_label, label) {
|
||||
symbols[label] = {value:ip};
|
||||
return '';
|
||||
});
|
||||
line = line.trim();
|
||||
// look at each rule in order
|
||||
for (var i=0; i<spec.rules.length; i++) {
|
||||
var rule = spec.rules[i];
|
||||
var m = rule.re.exec(line);
|
||||
if (m) {
|
||||
var result = self.buildInstruction(rule, m);
|
||||
if (result) {
|
||||
addBytes(result);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
warning("Could not decode instruction: " + line);
|
||||
}
|
||||
self.finish = function() {
|
||||
for (var i=0; i<fixups.length; i++) {
|
||||
var fix = fixups[i];
|
||||
var sym = symbols[fix.sym];
|
||||
if (sym) {
|
||||
var ofs = fix.ofs + (fix.bitofs>>3);
|
||||
var shift = fix.bitofs&7;
|
||||
var mask = ((1<<fix.bitlen)-1);
|
||||
var value = sym.value;
|
||||
if (fix.iprel) value -= fix.ofs + fix.ipofs;
|
||||
value &= mask;
|
||||
// TODO: check range
|
||||
// TODO: span multiple words?
|
||||
outwords[ofs] ^= value;
|
||||
} else {
|
||||
warning("Symbol '" + fix.sym + "' not found");
|
||||
}
|
||||
}
|
||||
fixups = [];
|
||||
}
|
||||
self.state = function() {
|
||||
return {ip:ip, line:linenum, output:outwords, errors:errors, fixups:fixups};
|
||||
}
|
||||
}
|
||||
|
||||
var asm = new Assembler(EXAMPLE_SPEC);
|
||||
console.log(EXAMPLE_SPEC);
|
||||
console.log(asm.assemble(".define FOO 0xa")); // TODO
|
||||
console.log(asm.assemble(" sta FOO"));
|
||||
console.log(asm.assemble(" sta 10"));
|
||||
console.log(asm.assemble(" add a,#25 ; comment "));
|
||||
console.log(asm.assemble("Label: asl a "));
|
||||
console.log(asm.assemble(" sub b,[b] "));
|
||||
console.log(asm.assemble(" bz Label "));
|
||||
asm.finish();
|
||||
console.log(asm.state());
|
@ -433,6 +433,12 @@ var VerilogPlatform = function(mainElement, options) {
|
||||
mouse_pressed = false;
|
||||
if (e.target.setCapture) e.target.releaseCapture();
|
||||
});
|
||||
$(video.canvas).keydown(function(e) {
|
||||
switch (e.keyCode) {
|
||||
case 37: scope_time_x--; dirty=true; break;
|
||||
case 39: scope_time_x++; dirty=true; break;
|
||||
}
|
||||
});
|
||||
audio = new SampleAudio(AUDIO_FREQ);
|
||||
idata = video.getFrameData();
|
||||
// TODO: 15.7 kHz?
|
||||
@ -499,8 +505,7 @@ var VerilogPlatform = function(mainElement, options) {
|
||||
trace_index = scope_x_offset = 0;
|
||||
trace_buffer.fill(0);
|
||||
dirty = true;
|
||||
console.log(gen.rotate);
|
||||
video.setRotate(gen.rotate ? -90 : 0);
|
||||
if (video) video.setRotate(gen.rotate ? -90 : 0);
|
||||
}
|
||||
this.tick = function() {
|
||||
gen.tick2();
|
||||
|
@ -180,7 +180,7 @@ function scrollProfileView(_ed) {
|
||||
}
|
||||
|
||||
function newEditor(mode) {
|
||||
var isAsm = mode=='6502' || mode =='z80';
|
||||
var isAsm = mode=='6502' || mode =='z80' || mode=='verilog'; // TODO
|
||||
editor = CodeMirror(document.getElementById('editor'), {
|
||||
theme: 'mbo',
|
||||
lineNumbers: true,
|
||||
|
271
src/worker/assembler.js
Normal file
271
src/worker/assembler.js
Normal file
@ -0,0 +1,271 @@
|
||||
|
||||
EXAMPLE_SPEC = {
|
||||
name:'femto8',
|
||||
vars:{
|
||||
reg:{bits:2, toks:['a', 'b', 'ip', 'none']},
|
||||
unop:{bits:3, toks:['mova','movb','inc','dec','asl','lsr','rol','ror']},
|
||||
binop:{bits:3, toks:['or','and','xor','zero','add','sub','adc','sbb']},
|
||||
imm4:{bits:4},
|
||||
imm8:{bits:8},
|
||||
rel:{bits:8, iprel:true, ipofs:1},
|
||||
},
|
||||
rules:[
|
||||
{fmt:'~binop ~reg,b', bits:['00',1,'1',0]},
|
||||
{fmt:'~binop ~reg,#~imm8', bits:['01',1,'1',0,2]},
|
||||
{fmt:'~binop ~reg,[b]', bits:['11',1,'1',0]},
|
||||
{fmt:'~unop ~reg', bits:['00',1,'0',0]},
|
||||
{fmt:'mov ~reg,[b]', bits:['11',0,'0001']},
|
||||
{fmt:'zero ~reg', bits:['00',0,'1011']},
|
||||
{fmt:'lda #~imm8', bits:['01','00','0001',0]},
|
||||
{fmt:'ldb #~imm8', bits:['01','01','0001',0]},
|
||||
{fmt:'jmp ~imm8', bits:['01','10','0001',0]},
|
||||
{fmt:'sta ~imm4', bits:['1001',0]},
|
||||
{fmt:'bcc ~imm8', bits:['1010','0001',0]},
|
||||
{fmt:'bcs ~imm8', bits:['1010','0011',0]},
|
||||
{fmt:'bz ~imm8', bits:['1010','1100',0]},
|
||||
{fmt:'bnz ~imm8', bits:['1010','0100',0]},
|
||||
{fmt:'clc', bits:['10001000']},
|
||||
{fmt:'swapab', bits:['10000001']},
|
||||
{fmt:'reset', bits:['10001111']},
|
||||
]
|
||||
}
|
||||
|
||||
var Assembler = function(spec) {
|
||||
var self = this;
|
||||
var ip = 0;
|
||||
var origin = 0;
|
||||
var linenum = 0;
|
||||
var symbols = {};
|
||||
var errors = [];
|
||||
var outwords = [];
|
||||
var asmlines = [];
|
||||
var fixups = [];
|
||||
var width = 8;
|
||||
var codelen = 0;
|
||||
var aborted = false;
|
||||
|
||||
function rule2regex(rule, vars) {
|
||||
var s = rule.fmt;
|
||||
var varlist = [];
|
||||
rule.prefix = s.split(/\s+/)[0];
|
||||
s = s.replace(/\s+/g, '\\s+');
|
||||
s = s.replace(/\[/g, '\\[');
|
||||
s = s.replace(/\]/g, '\\]');
|
||||
s = s.replace(/\./g, '\\.');
|
||||
s = s.replace(/~\w+/g, function(varname) {
|
||||
varname = varname.substr(1);
|
||||
var v = vars[varname];
|
||||
varlist.push(varname);
|
||||
if (!v)
|
||||
throw Error("Could not find rule for ~" + varname);
|
||||
else if (v.toks)
|
||||
return '(\\w+)';
|
||||
else
|
||||
return '([0-9]+|[$][0-9a-f]+|\\w+)';
|
||||
});
|
||||
rule.re = new RegExp('^'+s+'$', 'i');
|
||||
rule.varlist = varlist;
|
||||
// TODO: check rule constraints
|
||||
return rule;
|
||||
}
|
||||
|
||||
function preprocessRules() {
|
||||
for (var i=0; i<spec.rules.length; i++)
|
||||
rule2regex(spec.rules[i], spec.vars);
|
||||
}
|
||||
if (spec) preprocessRules();
|
||||
|
||||
function warning(msg) {
|
||||
errors.push({msg:msg, line:linenum});
|
||||
}
|
||||
function fatal(msg) {
|
||||
warning(msg);
|
||||
aborted = true;
|
||||
}
|
||||
function hex(v, nd) {
|
||||
try {
|
||||
if (!nd) nd = 2;
|
||||
var s = v.toString(16).toUpperCase();
|
||||
while (s.length < nd)
|
||||
s = "0" + s;
|
||||
return s;
|
||||
} catch (e) {
|
||||
return v+"";
|
||||
}
|
||||
}
|
||||
function addBytes(result) {
|
||||
asmlines.push({
|
||||
line:linenum,
|
||||
offset:ip,
|
||||
nbits:result.nbits
|
||||
});
|
||||
var op = result.opcode;
|
||||
var nb = result.nbits/width;
|
||||
for (var i=0; i<nb; i++) {
|
||||
outwords[ip++ - origin] = (op >> (nb-1-i)*width) & ((1<<width)-1);
|
||||
}
|
||||
}
|
||||
|
||||
function parseConst(s) {
|
||||
if (!s.length)
|
||||
return s;
|
||||
else if (s.startsWith("$"))
|
||||
return parseInt(s.substr(1), 16);
|
||||
else
|
||||
return parseInt(s);
|
||||
}
|
||||
|
||||
self.buildInstruction = function(rule, m) {
|
||||
var opcode = 0;
|
||||
var oplen = 0;
|
||||
for (var i=0; i<rule.bits.length; i++) {
|
||||
var b = rule.bits[i];
|
||||
var n,x;
|
||||
if (b.length) {
|
||||
n = b.length;
|
||||
x = parseInt(b,2);
|
||||
} else {
|
||||
var id = m[b+1];
|
||||
var v = spec.vars[rule.varlist[b]];
|
||||
if (!v) {
|
||||
warning("Could not find matching identifier for '" + m[0] + "'");
|
||||
return;
|
||||
}
|
||||
n = v.bits;
|
||||
if (v.toks) {
|
||||
x = v.toks.indexOf(id);
|
||||
if (x < 0)
|
||||
return null;
|
||||
} else {
|
||||
x = parseConst(id);
|
||||
// is it a label? add fixup
|
||||
if (isNaN(x)) {
|
||||
fixups.push({sym:id, ofs:ip, bitlen:n, bitofs:oplen, line:linenum, iprel:!!v.iprel, ipofs:(v.ipofs+0)});
|
||||
x = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
var mask = (1<<n)-1;
|
||||
if ((x&mask) != x)
|
||||
warning("Value " + x + " could not fit in " + n + " bits");
|
||||
opcode = (opcode << n) | x;
|
||||
oplen += n;
|
||||
}
|
||||
if (oplen == 0)
|
||||
warning("Opcode had zero length");
|
||||
else if ((oplen % width) != 0)
|
||||
warning("Opcode was not word-aligned (" + oplen + " bits)");
|
||||
return {opcode:opcode, nbits:oplen};
|
||||
}
|
||||
|
||||
function loadArch(arch) {
|
||||
if (self.loadFile) {
|
||||
var json = self.loadFile(arch + ".json");
|
||||
if (json && json.vars && json.rules) {
|
||||
spec = json;
|
||||
preprocessRules();
|
||||
} else {
|
||||
fatal("Could not load arch file '" + arch + ".json'");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function parseDirective(tokens) {
|
||||
if (tokens[0] == '.define')
|
||||
symbols[tokens[1]] = {value:tokens[2]};
|
||||
else if (tokens[0] == '.org')
|
||||
ip = origin = parseInt(tokens[1]);
|
||||
else if (tokens[0] == '.len')
|
||||
codelen = parseInt(tokens[1]);
|
||||
else if (tokens[0] == '.width')
|
||||
width = parseInt(tokens[1]);
|
||||
else if (tokens[0] == '.arch')
|
||||
loadArch(tokens[1]);
|
||||
else
|
||||
warning("Unrecognized directive: " + tokens);
|
||||
}
|
||||
|
||||
self.assemble = function(line) {
|
||||
linenum++;
|
||||
// remove comments
|
||||
line = line.replace(/[;].*/g, '');
|
||||
line = line.trim().toLowerCase();
|
||||
// is it a directive?
|
||||
if (line[0] == '.') {
|
||||
var tokens = line.split(/\s+/);
|
||||
parseDirective(tokens);
|
||||
return;
|
||||
}
|
||||
// find labels
|
||||
line = line.replace(/(\w+):/, function(_label, label) {
|
||||
symbols[label] = {value:ip};
|
||||
return ''; // replace label with blank
|
||||
});
|
||||
line = line.trim();
|
||||
if (line == '')
|
||||
return; // empty line
|
||||
// look at each rule in order
|
||||
if (!spec) { fatal("Need to load .spec first"); return; }
|
||||
for (var i=0; i<spec.rules.length; i++) {
|
||||
var rule = spec.rules[i];
|
||||
var m = rule.re.exec(line);
|
||||
if (m) {
|
||||
var result = self.buildInstruction(rule, m);
|
||||
if (result) {
|
||||
addBytes(result);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
warning("Could not decode instruction: " + line);
|
||||
}
|
||||
|
||||
self.finish = function() {
|
||||
// apply fixups
|
||||
for (var i=0; i<fixups.length; i++) {
|
||||
var fix = fixups[i];
|
||||
var sym = symbols[fix.sym];
|
||||
if (sym) {
|
||||
var ofs = fix.ofs + (fix.bitofs>>3);
|
||||
var shift = fix.bitofs&7;
|
||||
var mask = ((1<<fix.bitlen)-1);
|
||||
var value = parseConst(sym.value);
|
||||
if (fix.iprel) value -= fix.ofs + fix.ipofs;
|
||||
value &= mask;
|
||||
// TODO: check range
|
||||
// TODO: span multiple words?
|
||||
outwords[ofs - origin] ^= value;
|
||||
} else {
|
||||
warning("Symbol '" + fix.sym + "' not found");
|
||||
}
|
||||
}
|
||||
// update asmlines
|
||||
for (var i=0; i<asmlines.length; i++) {
|
||||
var al = asmlines[i];
|
||||
al.insns = '';
|
||||
for (var j=0; j<al.nbits/width; j++) {
|
||||
var word = outwords[al.offset + j - origin];
|
||||
if (j>0) al.insns += ' ';
|
||||
al.insns += hex(word,width/4);
|
||||
}
|
||||
}
|
||||
while (outwords.length < codelen) {
|
||||
outwords.push(0);
|
||||
}
|
||||
fixups = [];
|
||||
return self.state();
|
||||
}
|
||||
|
||||
self.assembleFile = function(text) {
|
||||
var lines = text.split(/\n/g);
|
||||
for (var i=0; i<lines.length && !aborted; i++) {
|
||||
self.assemble(lines[i]);
|
||||
}
|
||||
return self.finish();
|
||||
}
|
||||
|
||||
self.state = function() {
|
||||
return {ip:ip, line:linenum, origin:origin, codelen:codelen,
|
||||
output:outwords, asmlines:asmlines, errors:errors, fixups:fixups};
|
||||
}
|
||||
}
|
@ -1113,25 +1113,47 @@ function compileCASPR(code, platform, options) {
|
||||
}
|
||||
}
|
||||
|
||||
function compileASM(asmcode, platform, options) {
|
||||
load("assembler");
|
||||
var asm = new Assembler();
|
||||
asm.loadFile = function(filename) {
|
||||
// TODO: what if it comes from dependencies?
|
||||
var path = '../../presets/' + platform + '/' + filename;
|
||||
var xhr = new XMLHttpRequest();
|
||||
xhr.responseType = 'json';
|
||||
xhr.open("GET", path, false); // synchronous request
|
||||
xhr.send(null);
|
||||
return xhr.response;
|
||||
};
|
||||
var result = asm.assembleFile(asmcode);
|
||||
return result;
|
||||
}
|
||||
|
||||
function compileVerilator(code, platform, options) {
|
||||
loadNative("verilator_bin");
|
||||
load("verilator2js");
|
||||
var errors = [];
|
||||
var asmlines = [];
|
||||
// compile inline asm
|
||||
// TODO: keep line numbers
|
||||
code = code.replace(/__asm\b([\s\S]+?)\b__endasm\b/g, function(s,asmcode) {
|
||||
var asmout = compileCASPR(asmcode, platform, options);
|
||||
code = code.replace(/__asm\b([\s\S]+?)\b__endasm\b/g, function(s,asmcode,index) {
|
||||
var firstline = code.substr(0,index).match(/\n/g).length;
|
||||
var asmout = compileASM(asmcode, platform, options);
|
||||
if (asmout.errors && asmout.errors.length) {
|
||||
errors = asmout.errors;
|
||||
for (var i=0; i<errors.length; i++)
|
||||
errors[i].line += firstline;
|
||||
return "";
|
||||
} else if (asmout.output) {
|
||||
var s = "";
|
||||
var out = asmout.output;
|
||||
for (var i=0; i<out.length; i++) {
|
||||
if (i>0) s += ",";
|
||||
s += out[i];
|
||||
s += 0|out[i];
|
||||
}
|
||||
//console.log(s);
|
||||
asmlines = asmout.asmlines;
|
||||
for (var i=0; i<asmlines.length; i++)
|
||||
asmlines[i].line += firstline;
|
||||
return s;
|
||||
}
|
||||
});
|
||||
@ -1162,6 +1184,7 @@ function compileVerilator(code, platform, options) {
|
||||
rtn.errors = errors;
|
||||
rtn.lines = [];// TODO
|
||||
rtn.intermediate = {listing:h_file + cpp_file};
|
||||
rtn.lines = asmlines;
|
||||
return rtn;
|
||||
} catch(e) {
|
||||
console.log(e);
|
||||
|
84
test/cli/testasm.js
Normal file
84
test/cli/testasm.js
Normal file
@ -0,0 +1,84 @@
|
||||
|
||||
var vm = require('vm');
|
||||
var fs = require('fs');
|
||||
var assert = require('assert');
|
||||
var includeInThisContext = function(path) {
|
||||
var code = fs.readFileSync(path);
|
||||
vm.runInThisContext(code, path);
|
||||
};
|
||||
|
||||
includeInThisContext("src/worker/assembler.js");
|
||||
|
||||
describe('Assemble', function() {
|
||||
it('Should assemble', function() {
|
||||
var source = `.arch femto8
|
||||
.org 128
|
||||
.len 128
|
||||
|
||||
.define VPU_LO 8
|
||||
.define VPU_HI 9
|
||||
.define VPU_WRITE 10
|
||||
.define VPU_MOVE 11
|
||||
.define IN_FLAGS $42
|
||||
.define F_VSYNC 16
|
||||
|
||||
Start:
|
||||
zero A
|
||||
sta VPU_LO
|
||||
sta VPU_HI
|
||||
sta 0
|
||||
DisplayLoop:
|
||||
zero B
|
||||
mov A,[b]
|
||||
sta VPU_WRITE
|
||||
sta VPU_MOVE
|
||||
lda #F_VSYNC
|
||||
ldb #IN_FLAGS
|
||||
and none,[B]
|
||||
bz DisplayLoop
|
||||
WaitVsync:
|
||||
and none,[B]
|
||||
bnz WaitVsync
|
||||
zero B
|
||||
mov A,[b]
|
||||
inc A
|
||||
sta 0
|
||||
jmp DisplayLoop
|
||||
`;
|
||||
var asm = new Assembler(EXAMPLE_SPEC);
|
||||
var result = asm.assembleFile(source);
|
||||
//console.log(result);
|
||||
//assert.equal(result, {});
|
||||
assert.equal(128, result.origin);
|
||||
assert.equal(152, result.ip);
|
||||
assert.deepEqual({
|
||||
insns: "0B",
|
||||
line: 13,
|
||||
nbits: 8,
|
||||
offset: 128
|
||||
}, result.asmlines[0]);
|
||||
assert.deepEqual(
|
||||
[ { line: 13, offset: 128, nbits: 8, insns: '0B' },
|
||||
{ line: 14, offset: 129, nbits: 8, insns: '98' },
|
||||
{ line: 15, offset: 130, nbits: 8, insns: '99' },
|
||||
{ line: 16, offset: 131, nbits: 8, insns: '90' },
|
||||
{ line: 18, offset: 132, nbits: 8, insns: '1B' },
|
||||
{ line: 19, offset: 133, nbits: 8, insns: 'C1' },
|
||||
{ line: 20, offset: 134, nbits: 8, insns: '9A' },
|
||||
{ line: 21, offset: 135, nbits: 8, insns: '9B' },
|
||||
{ line: 22, offset: 136, nbits: 16, insns: '41 10' },
|
||||
{ line: 23, offset: 138, nbits: 16, insns: '51 42' },
|
||||
{ line: 24, offset: 140, nbits: 8, insns: 'F9' },
|
||||
{ line: 25, offset: 141, nbits: 16, insns: 'AC 84' },
|
||||
{ line: 27, offset: 143, nbits: 8, insns: 'F9' },
|
||||
{ line: 28, offset: 144, nbits: 16, insns: 'A4 8F' },
|
||||
{ line: 29, offset: 146, nbits: 8, insns: '1B' },
|
||||
{ line: 30, offset: 147, nbits: 8, insns: 'C1' },
|
||||
{ line: 31, offset: 148, nbits: 8, insns: '02' },
|
||||
{ line: 32, offset: 149, nbits: 8, insns: '90' },
|
||||
{ line: 33, offset: 150, nbits: 16, insns: '61 84' },
|
||||
], result.asmlines);
|
||||
assert.equal(11, result.output[0]);
|
||||
assert.equal(128, result.output.length);
|
||||
});
|
||||
});
|
Loading…
x
Reference in New Issue
Block a user