mirror of
https://github.com/sehugg/8bitworkshop.git
synced 2024-11-25 03:34:05 +00:00
arm32: platform, vasm and armips, unicorn.js
This commit is contained in:
parent
2b5ec48940
commit
7725884eb0
3
.gitmodules
vendored
3
.gitmodules
vendored
@ -16,3 +16,6 @@
|
|||||||
[submodule "nanoasm"]
|
[submodule "nanoasm"]
|
||||||
path = nanoasm
|
path = nanoasm
|
||||||
url = https://github.com/sehugg/nanoasm
|
url = https://github.com/sehugg/nanoasm
|
||||||
|
[submodule "unicorn.js"]
|
||||||
|
path = unicorn.js
|
||||||
|
url = https://github.com/AlexAltea/unicorn.js
|
||||||
|
1
Makefile
1
Makefile
@ -15,6 +15,7 @@ all:
|
|||||||
cp node_modules/localforage/dist/localforage.min.js ./lib/
|
cp node_modules/localforage/dist/localforage.min.js ./lib/
|
||||||
cp node_modules/jszip/dist/jszip.min.js ./lib/
|
cp node_modules/jszip/dist/jszip.min.js ./lib/
|
||||||
cp node_modules/file-saver/dist/*.min.js ./lib/
|
cp node_modules/file-saver/dist/*.min.js ./lib/
|
||||||
|
cp unicorn.js/dist/unicorn-arm.min.js ./unicorn.js/demos/externals/capstone-arm.min.js ./lib/
|
||||||
cp gif.js/dist/* ./lib/
|
cp gif.js/dist/* ./lib/
|
||||||
cd jsnes && npm i
|
cd jsnes && npm i
|
||||||
$(TSC) -v
|
$(TSC) -v
|
||||||
|
@ -437,8 +437,8 @@ $( ".dropdown-submenu" ).click(function(event) {
|
|||||||
|
|
||||||
<!-- Sentry error reporting -->
|
<!-- Sentry error reporting -->
|
||||||
<script
|
<script
|
||||||
src="https://browser.sentry-cdn.com/5.29.0/bundle.min.js"
|
src="https://browser.sentry-cdn.com/6.4.1/bundle.min.js"
|
||||||
integrity="sha384-/dYT/04VSU9ItKRPTkWeVZ0kqRsVh/T/5rNCjzBwpx7sYeeueKgJzGMNXSal3xoo"
|
integrity="sha384-THoc7rflwZFKTdZNgv6jLFFDn299Uv3t1SW5B4yGLvLiCRTYP9ys6vXZcMl95TQF"
|
||||||
crossorigin="anonymous"
|
crossorigin="anonymous"
|
||||||
></script>
|
></script>
|
||||||
<script>
|
<script>
|
||||||
|
@ -597,6 +597,7 @@ function require(modname) {
|
|||||||
<script src="gen/common/devices.js"></script>
|
<script src="gen/common/devices.js"></script>
|
||||||
<script src="gen/common/cpu/MOS6502.js"></script>
|
<script src="gen/common/cpu/MOS6502.js"></script>
|
||||||
<script src="gen/common/cpu/ZilogZ80.js"></script>
|
<script src="gen/common/cpu/ZilogZ80.js"></script>
|
||||||
|
<script src="gen/common/cpu/ARM.js"></script>
|
||||||
<script src="gen/machine/vdp_z80.js"></script>
|
<script src="gen/machine/vdp_z80.js"></script>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
15
presets/arm32/vidfill.vasm
Normal file
15
presets/arm32/vidfill.vasm
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
.set VIDMEM, 0x20000000
|
||||||
|
|
||||||
|
mov r0, #0x884400 ; RGB value
|
||||||
|
mov r1, #VIDMEM ; memory start
|
||||||
|
LOOP2:
|
||||||
|
mov r2, #160*128 ; word count
|
||||||
|
LOOP:
|
||||||
|
str r0, [r1, r2, lsl #2]
|
||||||
|
sub r2, r2, #1
|
||||||
|
cmp r2, #0
|
||||||
|
bge LOOP
|
||||||
|
sub r0, r0, #0xff11
|
||||||
|
cmp r0, #0
|
||||||
|
bgt LOOP2
|
||||||
|
|
@ -21,6 +21,8 @@
|
|||||||
*
|
*
|
||||||
* The data bus is implemented as separate read/write buses. Combine them
|
* The data bus is implemented as separate read/write buses. Combine them
|
||||||
* on the output pads if external memory is required.
|
* on the output pads if external memory is required.
|
||||||
|
*
|
||||||
|
* Also see: https://github.com/sehugg/mango_one
|
||||||
*/
|
*/
|
||||||
|
|
||||||
module cpu6502( clk, reset, AB, DI, DO, WE, IRQ, NMI, RDY );
|
module cpu6502( clk, reset, AB, DI, DO, WE, IRQ, NMI, RDY );
|
||||||
@ -1352,6 +1354,10 @@ wire RDY=1; // Ready signal. Pauses CPU when RDY=0
|
|||||||
end
|
end
|
||||||
|
|
||||||
reg [7:0] rom[0:15];
|
reg [7:0] rom[0:15];
|
||||||
|
// LDY #$13
|
||||||
|
// .loop: DEY
|
||||||
|
// BNE .loop
|
||||||
|
// BRK
|
||||||
initial begin
|
initial begin
|
||||||
rom = '{
|
rom = '{
|
||||||
8'ha0,8'h13,
|
8'ha0,8'h13,
|
||||||
|
@ -75,7 +75,7 @@ export interface Platform {
|
|||||||
isRunning() : boolean;
|
isRunning() : boolean;
|
||||||
getToolForFilename(s:string) : string;
|
getToolForFilename(s:string) : string;
|
||||||
getDefaultExtension() : string;
|
getDefaultExtension() : string;
|
||||||
getPresets() : Preset[];
|
getPresets?() : Preset[];
|
||||||
pause() : void;
|
pause() : void;
|
||||||
resume() : void;
|
resume() : void;
|
||||||
loadROM(title:string, rom:any); // TODO: Uint8Array
|
loadROM(title:string, rom:any); // TODO: Uint8Array
|
||||||
@ -1086,7 +1086,6 @@ export abstract class BaseMachinePlatform<T extends Machine> extends BaseDebugPl
|
|||||||
constructor(mainElement : HTMLElement) {
|
constructor(mainElement : HTMLElement) {
|
||||||
super();
|
super();
|
||||||
this.mainElement = mainElement;
|
this.mainElement = mainElement;
|
||||||
this.machine = this.newMachine();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
reset() { this.machine.reset(); }
|
reset() { this.machine.reset(); }
|
||||||
@ -1099,8 +1098,13 @@ export abstract class BaseMachinePlatform<T extends Machine> extends BaseDebugPl
|
|||||||
loadControlsState(s) { this.machine.loadControlsState(s); }
|
loadControlsState(s) { this.machine.loadControlsState(s); }
|
||||||
saveControlsState() { return this.machine.saveControlsState(); }
|
saveControlsState() { return this.machine.saveControlsState(); }
|
||||||
|
|
||||||
start() {
|
async start() {
|
||||||
|
this.machine = this.newMachine();
|
||||||
const m = this.machine;
|
const m = this.machine;
|
||||||
|
// block on WASM loading
|
||||||
|
if (m instanceof BaseWASMMachine) {
|
||||||
|
await m.loadWASM();
|
||||||
|
}
|
||||||
var videoFrequency;
|
var videoFrequency;
|
||||||
if (hasVideo(m)) {
|
if (hasVideo(m)) {
|
||||||
var vp = m.getVideoParams();
|
var vp = m.getVideoParams();
|
||||||
|
3942
src/common/cpu/ARM.ts
Normal file
3942
src/common/cpu/ARM.ts
Normal file
File diff suppressed because it is too large
Load Diff
@ -83,11 +83,9 @@ export class RasterVideo {
|
|||||||
}
|
}
|
||||||
|
|
||||||
canvas : HTMLCanvasElement;
|
canvas : HTMLCanvasElement;
|
||||||
ctx;
|
ctx : CanvasRenderingContext2D;
|
||||||
imageData;
|
imageData : ImageData;
|
||||||
arraybuf;
|
datau32 : Uint32Array;
|
||||||
buf8;
|
|
||||||
datau32;
|
|
||||||
vcanvas : JQuery;
|
vcanvas : JQuery;
|
||||||
|
|
||||||
paddle_x = 255;
|
paddle_x = 255;
|
||||||
|
@ -84,6 +84,7 @@ var TOOL_TO_SOURCE_STYLE = {
|
|||||||
'basic': 'basic',
|
'basic': 'basic',
|
||||||
'silice': 'verilog',
|
'silice': 'verilog',
|
||||||
'wiz': 'text/x-wiz',
|
'wiz': 'text/x-wiz',
|
||||||
|
'vasmarm': '6502'
|
||||||
}
|
}
|
||||||
|
|
||||||
function gaEvent(category:string, action:string, label?:string, value?:string) {
|
function gaEvent(category:string, action:string, label?:string, value?:string) {
|
||||||
@ -2048,7 +2049,7 @@ async function startPlatform() {
|
|||||||
platform = new PLATFORMS[platform_id]($("#emuscreen")[0]);
|
platform = new PLATFORMS[platform_id]($("#emuscreen")[0]);
|
||||||
setPlatformUI();
|
setPlatformUI();
|
||||||
stateRecorder = new StateRecorderImpl(platform);
|
stateRecorder = new StateRecorderImpl(platform);
|
||||||
PRESETS = platform.getPresets();
|
PRESETS = platform.getPresets ? platform.getPresets() : [];
|
||||||
if (!qs['file']) {
|
if (!qs['file']) {
|
||||||
// try to load last file (redirect)
|
// try to load last file (redirect)
|
||||||
var lastid;
|
var lastid;
|
||||||
|
@ -499,41 +499,40 @@ export class DisassemblerView implements ProjectView {
|
|||||||
|
|
||||||
// TODO: too many globals
|
// TODO: too many globals
|
||||||
refresh(moveCursor: boolean) {
|
refresh(moveCursor: boolean) {
|
||||||
var state = lastDebugState || platform.saveState(); // TODO?
|
let state = lastDebugState || platform.saveState(); // TODO?
|
||||||
var pc = state.c ? state.c.PC : 0;
|
let pc = state.c ? state.c.PC : 0;
|
||||||
var curline = 0;
|
let curline = 0;
|
||||||
var selline = 0;
|
let selline = 0;
|
||||||
var addr2symbol = (platform.debugSymbols && platform.debugSymbols.addr2symbol) || {};
|
let addr2symbol = (platform.debugSymbols && platform.debugSymbols.addr2symbol) || {};
|
||||||
// TODO: not perfect disassembler
|
// TODO: not perfect disassembler
|
||||||
var disassemble = (start, end) => {
|
let disassemble = (start, len) => {
|
||||||
if (start < 0) start = 0;
|
|
||||||
if (end > 0xffff) end = 0xffff;
|
|
||||||
// TODO: use pc2visits
|
// TODO: use pc2visits
|
||||||
var a = start;
|
let s = "";
|
||||||
var s = "";
|
let ofs = 0;
|
||||||
while (a < end) {
|
while (ofs < len) {
|
||||||
var disasm = platform.disassemble(a, platform.readAddress.bind(platform));
|
let a = (start + ofs) | 0;
|
||||||
|
let disasm = platform.disassemble(a, platform.readAddress.bind(platform));
|
||||||
/* TODO: look thru all source files
|
/* TODO: look thru all source files
|
||||||
var srclinenum = sourcefile && this.sourcefile.offset2line[a];
|
let srclinenum = sourcefile && this.sourcefile.offset2line[a];
|
||||||
if (srclinenum) {
|
if (srclinenum) {
|
||||||
var srcline = getActiveEditor().getLine(srclinenum);
|
let srcline = getActiveEditor().getLine(srclinenum);
|
||||||
if (srcline && srcline.trim().length) {
|
if (srcline && srcline.trim().length) {
|
||||||
s += "; " + srclinenum + ":\t" + srcline + "\n";
|
s += "; " + srclinenum + ":\t" + srcline + "\n";
|
||||||
curline++;
|
curline++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
var bytes = "";
|
let bytes = "";
|
||||||
var comment = "";
|
let comment = "";
|
||||||
for (var i=0; i<disasm.nbytes; i++)
|
for (let i=0; i<disasm.nbytes; i++)
|
||||||
bytes += hex(platform.readAddress(a+i));
|
bytes += hex(platform.readAddress(a+i));
|
||||||
while (bytes.length < 14)
|
while (bytes.length < 14)
|
||||||
bytes += ' ';
|
bytes += ' ';
|
||||||
var dstr = disasm.line;
|
let dstr = disasm.line;
|
||||||
if (addr2symbol && disasm.isaddr) { // TODO: move out
|
if (addr2symbol && disasm.isaddr) { // TODO: move out
|
||||||
dstr = dstr.replace(/([^#])[$]([0-9A-F]+)/, (substr:string, ...args:any[]):string => {
|
dstr = dstr.replace(/([^#])[$]([0-9A-F]+)/, (substr:string, ...args:any[]):string => {
|
||||||
var addr = parseInt(args[1], 16);
|
let addr = parseInt(args[1], 16);
|
||||||
var sym = addr2symbol[addr];
|
let sym = addr2symbol[addr];
|
||||||
if (sym) return (args[0] + sym);
|
if (sym) return (args[0] + sym);
|
||||||
sym = addr2symbol[addr-1];
|
sym = addr2symbol[addr-1];
|
||||||
if (sym) return (args[0] + sym + "+1");
|
if (sym) return (args[0] + sym + "+1");
|
||||||
@ -541,20 +540,20 @@ export class DisassemblerView implements ProjectView {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
if (addr2symbol) {
|
if (addr2symbol) {
|
||||||
var sym = addr2symbol[a];
|
let sym = addr2symbol[a];
|
||||||
if (sym) {
|
if (sym) {
|
||||||
comment = "; " + sym;
|
comment = "; " + sym;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
var dline = hex(parseInt(a), 4) + "\t" + rpad(bytes,14) + "\t" + rpad(dstr,30) + comment + "\n";
|
let dline = hex(a, 4) + "\t" + rpad(bytes,14) + "\t" + rpad(dstr,30) + comment + "\n";
|
||||||
s += dline;
|
s += dline;
|
||||||
if (a == pc) selline = curline;
|
if (a == pc) selline = curline;
|
||||||
curline++;
|
curline++;
|
||||||
a += disasm.nbytes || 1;
|
ofs += disasm.nbytes || 1;
|
||||||
}
|
}
|
||||||
return s;
|
return s;
|
||||||
}
|
}
|
||||||
var text = disassemble(pc-disasmWindow, pc) + disassemble(pc, pc+disasmWindow);
|
let text = disassemble(pc-disasmWindow, disasmWindow) + disassemble(pc, disasmWindow);
|
||||||
this.disasmview.setValue(text);
|
this.disasmview.setValue(text);
|
||||||
if (moveCursor) {
|
if (moveCursor) {
|
||||||
this.disasmview.setCursor(selline, 0);
|
this.disasmview.setCursor(selline, 0);
|
||||||
|
248
src/platform/arm32.ts
Normal file
248
src/platform/arm32.ts
Normal file
@ -0,0 +1,248 @@
|
|||||||
|
"use strict";
|
||||||
|
|
||||||
|
import { BaseDebugPlatform, CpuState, EmuState, Platform, DisasmLine, Debuggable } from "../common/baseplatform";
|
||||||
|
import { AnimationTimer, EmuHalt, padBytes, PLATFORMS, RasterVideo } from "../common/emu";
|
||||||
|
import { loadScript } from "../ide/ui";
|
||||||
|
import { hex, lpad } from "../common/util";
|
||||||
|
import { ARM32CPU } from "../common/cpu/ARM";
|
||||||
|
|
||||||
|
declare var uc, cs : any; // Unicorn module
|
||||||
|
|
||||||
|
const ARM32_PRESETS = [
|
||||||
|
{ id: 'vidfill.vasm', name: 'Video Memory Fill' },
|
||||||
|
];
|
||||||
|
|
||||||
|
const SCREEN_WIDTH = 160;
|
||||||
|
const SCREEN_HEIGHT = 128;
|
||||||
|
const ROM_START_ADDR = 0x0;
|
||||||
|
const HIROM_START_ADDR = 0xff800000;
|
||||||
|
const ROM_SIZE = 512*1024;
|
||||||
|
const RAM_START_ADDR = 0x20000000;
|
||||||
|
const RAM_SIZE = 512*1024;
|
||||||
|
const CLOCKS_PER_FRAME = 10000;
|
||||||
|
|
||||||
|
interface ARM32State extends EmuState {
|
||||||
|
r: Uint32Array; // registers
|
||||||
|
}
|
||||||
|
|
||||||
|
class ARM32Platform extends BaseDebugPlatform implements Platform, Debuggable {
|
||||||
|
|
||||||
|
u : any; // Unicorn
|
||||||
|
d : any; // Capstone
|
||||||
|
mainElement : HTMLElement;
|
||||||
|
video : RasterVideo;
|
||||||
|
timer : AnimationTimer;
|
||||||
|
romSize = 0;
|
||||||
|
halted = false;
|
||||||
|
state : ARM32State;
|
||||||
|
cpu : ARM32CPU;
|
||||||
|
|
||||||
|
constructor(mainElement: HTMLElement) {
|
||||||
|
super();
|
||||||
|
this.mainElement = mainElement;
|
||||||
|
}
|
||||||
|
getPresets() { return ARM32_PRESETS };
|
||||||
|
|
||||||
|
async start() {
|
||||||
|
console.log("Loading Unicorn/Capstone");
|
||||||
|
await loadScript('./lib/unicorn-arm.min.js');
|
||||||
|
await loadScript('./lib/capstone-arm.min.js');
|
||||||
|
|
||||||
|
//this.cpu = new ARM32CPU();
|
||||||
|
|
||||||
|
this.u = new uc.Unicorn(uc.ARCH_ARM, uc.MODE_ARM);
|
||||||
|
this.u.mem_map(ROM_START_ADDR, ROM_SIZE, uc.PROT_READ | uc.PROT_EXEC);
|
||||||
|
this.u.mem_map(HIROM_START_ADDR, ROM_SIZE, uc.PROT_READ | uc.PROT_EXEC);
|
||||||
|
this.u.mem_map(RAM_START_ADDR, RAM_SIZE, uc.PROT_READ | uc.PROT_WRITE | uc.PROT_EXEC);
|
||||||
|
|
||||||
|
this.d = new cs.Capstone(cs.ARCH_ARM, cs.MODE_ARM);
|
||||||
|
|
||||||
|
this.video = new RasterVideo(this.mainElement, SCREEN_WIDTH, SCREEN_HEIGHT);
|
||||||
|
this.video.create();
|
||||||
|
|
||||||
|
this.timer = new AnimationTimer(60, this.nextFrame.bind(this));
|
||||||
|
}
|
||||||
|
reset() {
|
||||||
|
//this.cpu.reset();
|
||||||
|
this.u.reg_write_i32(uc.ARM_REG_PC, 0);
|
||||||
|
var cpsr = this.u.reg_read_i32(uc.ARM_REG_CPSR);
|
||||||
|
this.u.reg_write_i32(uc.ARM_REG_CPSR, (cpsr & 0xffffff00) | 0b11010011);
|
||||||
|
this.u.mem_write(RAM_START_ADDR, new Uint8Array(RAM_SIZE)); // clear RAM
|
||||||
|
this.halted = false;
|
||||||
|
this.state = null;
|
||||||
|
}
|
||||||
|
pause(): void {
|
||||||
|
this.timer.stop();
|
||||||
|
}
|
||||||
|
resume(): void {
|
||||||
|
this.timer.start();
|
||||||
|
console.log('resume')
|
||||||
|
}
|
||||||
|
isRunning() {
|
||||||
|
return this.timer.isRunning();
|
||||||
|
}
|
||||||
|
isBlocked() {
|
||||||
|
return this.halted;
|
||||||
|
}
|
||||||
|
checkPCOverflow(pc) {
|
||||||
|
|
||||||
|
}
|
||||||
|
disassemble(pc:number, read:(addr:number)=>number) : DisasmLine {
|
||||||
|
try {
|
||||||
|
var b = this.u.mem_read(pc, 8);
|
||||||
|
var insns = this.d.disasm(b, pc, 8);
|
||||||
|
var i0 = insns[0];
|
||||||
|
return {
|
||||||
|
nbytes: i0.size,
|
||||||
|
line: i0.mnemonic + " " + i0.op_str,
|
||||||
|
isaddr: i0.address > 0
|
||||||
|
};
|
||||||
|
} catch (e) {
|
||||||
|
return {
|
||||||
|
nbytes: 4,
|
||||||
|
line: "???",
|
||||||
|
isaddr: false
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
advance(novideo?: boolean): number {
|
||||||
|
this.state = null;
|
||||||
|
var pc = this.getPC();
|
||||||
|
var endpc = this.romSize;
|
||||||
|
if (pc >= endpc) {
|
||||||
|
this.halted = true;
|
||||||
|
this.haltAndCatchFire("ROM overrun at PC 0x" + hex(pc));
|
||||||
|
this.pause();
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
var debugCond = this.getDebugCallback();
|
||||||
|
try {
|
||||||
|
if (debugCond != null) {
|
||||||
|
for (var i=0; i<CLOCKS_PER_FRAME && pc <= endpc; i++) {
|
||||||
|
if (debugCond()) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
this.u.emu_start(pc, endpc, 0, 1);
|
||||||
|
pc = this.getPC();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
this.u.emu_start(pc, endpc, 0, CLOCKS_PER_FRAME); // TODO
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
throw new EmuHalt(e + " at PC=0x" + hex(this.getPC()));
|
||||||
|
}
|
||||||
|
if (!novideo) {
|
||||||
|
this.updateVideo();
|
||||||
|
}
|
||||||
|
return CLOCKS_PER_FRAME; //throw new Error("Method not implemented.");
|
||||||
|
}
|
||||||
|
updateVideo() {
|
||||||
|
var vmem8 : Uint8Array = this.u.mem_read(RAM_START_ADDR, SCREEN_WIDTH * SCREEN_HEIGHT * 4);
|
||||||
|
var vmem32 = new Uint32Array(vmem8.buffer);
|
||||||
|
var pixels = this.video.getFrameData();
|
||||||
|
for (var i=0; i<vmem32.length; i++)
|
||||||
|
pixels[i] = vmem32[i] | 0xff000000;
|
||||||
|
this.video.updateFrame();
|
||||||
|
}
|
||||||
|
getToolForFilename() {
|
||||||
|
return "vasmarm";
|
||||||
|
}
|
||||||
|
getDefaultExtension() {
|
||||||
|
return ".asm";
|
||||||
|
}
|
||||||
|
loadROM(title, data: Uint8Array) {
|
||||||
|
this.romSize = data.length;
|
||||||
|
data = padBytes(data, ROM_SIZE);
|
||||||
|
this.u.mem_write(ROM_START_ADDR, data);
|
||||||
|
this.u.mem_write(HIROM_START_ADDR, data);
|
||||||
|
this.state = null;
|
||||||
|
this.reset();
|
||||||
|
}
|
||||||
|
readAddress(addr: number) {
|
||||||
|
// TODO: slow
|
||||||
|
try {
|
||||||
|
return this.u.mem_read(addr, 1)[0];
|
||||||
|
} catch (e) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
getCPUState(): CpuState {
|
||||||
|
return {
|
||||||
|
PC: this.getPC(),
|
||||||
|
SP: this.getSP(),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
isStable(): boolean {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
getPC() {
|
||||||
|
return this.u.reg_read_i32(uc.ARM_REG_PC);
|
||||||
|
}
|
||||||
|
getSP() {
|
||||||
|
return this.u.reg_read_i32(uc.ARM_REG_SP);
|
||||||
|
}
|
||||||
|
loadState(state: ARM32State): void {
|
||||||
|
for (var i=0; i<uc.ARM_REG_ENDING; i++) {
|
||||||
|
this.u.reg_write_i32(i, state.r[i]);
|
||||||
|
}
|
||||||
|
this.u.mem_write(RAM_START_ADDR, state.b);
|
||||||
|
}
|
||||||
|
saveState(): ARM32State {
|
||||||
|
var regs = new Uint32Array(uc.ARM_REG_ENDING);
|
||||||
|
for (var i=0; i<uc.ARM_REG_ENDING; i++) {
|
||||||
|
regs[i] = this.u.reg_read_i32(i);
|
||||||
|
}
|
||||||
|
this.state = {
|
||||||
|
c: this.getCPUState(),
|
||||||
|
b: this.u.mem_read(RAM_START_ADDR, RAM_SIZE),
|
||||||
|
r: regs
|
||||||
|
};
|
||||||
|
return this.state;
|
||||||
|
}
|
||||||
|
showHelp(tool: string) {
|
||||||
|
if (tool == 'vasmarm') {
|
||||||
|
window.open('http://sun.hasenbraten.de/vasm/release/vasm.html');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
getDebugCategories() {
|
||||||
|
return ["CPU"];
|
||||||
|
}
|
||||||
|
getDebugInfo?(category:string, state:ARM32State) : string {
|
||||||
|
var s = '';
|
||||||
|
for (var i=0; i<13; i++) {
|
||||||
|
s += lpad('r'+i, 3) + ' ' + hex(state.r[i+uc.ARM_REG_R0],8) + '\n';
|
||||||
|
}
|
||||||
|
s += ' SP ' + hex(state.r[uc.ARM_REG_SP],8) + '\n';
|
||||||
|
s += ' LR ' + hex(state.r[uc.ARM_REG_LR],8) + '\n';
|
||||||
|
s += ' PC ' + hex(state.r[uc.ARM_REG_PC],8) + '\n';
|
||||||
|
s += 'CPSR ' + hex(state.r[uc.ARM_REG_CPSR],8) + '\n';
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
export abstract class BaseARMMachinePlatform<T extends Machine> extends BaseMachinePlatform<T> {
|
||||||
|
|
||||||
|
//getOpcodeMetadata = getOpcodeMetadata_z80;
|
||||||
|
getToolForFilename() { return "armips"; }
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
class ARM32Platform extends BaseARMMachinePlatform<ARM32Console> implements Platform {
|
||||||
|
|
||||||
|
async start() {
|
||||||
|
console.log("Loading Unicorn");
|
||||||
|
await loadScript('./unicorn.js/dist/unicorn-arm.min.js');
|
||||||
|
return super.start();
|
||||||
|
}
|
||||||
|
newMachine() { return new ARM32Console(); }
|
||||||
|
getPresets() { return ARM32_PRESETS; }
|
||||||
|
getDefaultExtension() { return ".asm"; };
|
||||||
|
readAddress(a) { return this.machine.read(a); }
|
||||||
|
getMemoryMap = function() { return { main:[
|
||||||
|
{name:'Frame Buffer',start:0x2400,size:7168,type:'ram'},
|
||||||
|
] } };
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
PLATFORMS['arm32'] = ARM32Platform;
|
@ -42,11 +42,6 @@ class C64WASMPlatform extends Base6502MachinePlatform<C64_WASMMachine> implement
|
|||||||
|
|
||||||
newMachine() { return new C64_WASMMachine('c64'); }
|
newMachine() { return new C64_WASMMachine('c64'); }
|
||||||
|
|
||||||
async start() {
|
|
||||||
// TODO: start() needs to block
|
|
||||||
await this.machine.loadWASM();
|
|
||||||
super.start();
|
|
||||||
}
|
|
||||||
getPresets() { return C64_PRESETS; }
|
getPresets() { return C64_PRESETS; }
|
||||||
getDefaultExtension() { return ".c"; };
|
getDefaultExtension() { return ".c"; };
|
||||||
readAddress(a) { return this.machine.readConst(a); }
|
readAddress(a) { return this.machine.readConst(a); }
|
||||||
|
@ -39,6 +39,7 @@ var VERILOG_PRESETS = [
|
|||||||
{id:'cpu16.v', name:'16-Bit CPU'},
|
{id:'cpu16.v', name:'16-Bit CPU'},
|
||||||
{id:'cpu_platform.v', name:'CPU Platform'},
|
{id:'cpu_platform.v', name:'CPU Platform'},
|
||||||
{id:'test2.asm', name:'16-bit ASM Game'},
|
{id:'test2.asm', name:'16-bit ASM Game'},
|
||||||
|
{id:'cpu6502.v', name:'6502 CPU'},
|
||||||
];
|
];
|
||||||
|
|
||||||
var VERILOG_KEYCODE_MAP = makeKeycodeMap([
|
var VERILOG_KEYCODE_MAP = makeKeycodeMap([
|
||||||
|
@ -23,11 +23,6 @@ class ZXWASMPlatform extends BaseZ80MachinePlatform<ZX_WASMMachine> implements P
|
|||||||
|
|
||||||
newMachine() { return new ZX_WASMMachine('zx'); }
|
newMachine() { return new ZX_WASMMachine('zx'); }
|
||||||
|
|
||||||
async start() {
|
|
||||||
// TODO: start() needs to block
|
|
||||||
await this.machine.loadWASM();
|
|
||||||
super.start();
|
|
||||||
}
|
|
||||||
getPresets() { return ZX_PRESETS; }
|
getPresets() { return ZX_PRESETS; }
|
||||||
getDefaultExtension() { return ".asm"; };
|
getDefaultExtension() { return ".asm"; };
|
||||||
readAddress(a) { return this.machine.readConst(a); }
|
readAddress(a) { return this.machine.readConst(a); }
|
||||||
|
22
src/worker/wasm/armips.js
Normal file
22
src/worker/wasm/armips.js
Normal file
File diff suppressed because one or more lines are too long
BIN
src/worker/wasm/armips.wasm
Normal file
BIN
src/worker/wasm/armips.wasm
Normal file
Binary file not shown.
5313
src/worker/wasm/vasmarm_std.js
Normal file
5313
src/worker/wasm/vasmarm_std.js
Normal file
File diff suppressed because it is too large
Load Diff
BIN
src/worker/wasm/vasmarm_std.wasm
Normal file
BIN
src/worker/wasm/vasmarm_std.wasm
Normal file
Binary file not shown.
@ -6,7 +6,7 @@ declare var WebAssembly;
|
|||||||
declare function importScripts(path:string);
|
declare function importScripts(path:string);
|
||||||
declare function postMessage(msg);
|
declare function postMessage(msg);
|
||||||
|
|
||||||
const emglobal : any = this['window'] || this['global'] || this;
|
const emglobal : any = (this as any)['window'] || (this as any)['global'] || this;
|
||||||
const ENVIRONMENT_IS_WEB = typeof window === 'object';
|
const ENVIRONMENT_IS_WEB = typeof window === 'object';
|
||||||
const ENVIRONMENT_IS_WORKER = typeof importScripts === 'function';
|
const ENVIRONMENT_IS_WORKER = typeof importScripts === 'function';
|
||||||
|
|
||||||
@ -2861,6 +2861,159 @@ function compileWiz(step:BuildStep) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function assembleARMIPS(step:BuildStep) {
|
||||||
|
loadNative("armips");
|
||||||
|
var errors = [];
|
||||||
|
gatherFiles(step, {mainFilePath:"main.asm"});
|
||||||
|
var objpath = step.prefix+".bin";
|
||||||
|
|
||||||
|
if (staleFiles(step, [objpath])) {
|
||||||
|
var args = [ step.path ];
|
||||||
|
var armips = emglobal.armips({
|
||||||
|
instantiateWasm: moduleInstFn('armips'),
|
||||||
|
noInitialRun:true,
|
||||||
|
print:(s:string) => {
|
||||||
|
console.log(s);
|
||||||
|
},
|
||||||
|
printErr:print_fn,
|
||||||
|
});
|
||||||
|
var FS = armips['FS'];
|
||||||
|
console.log("init FS", FS);
|
||||||
|
|
||||||
|
populateFiles(step, FS);
|
||||||
|
execMain(step, armips, args);
|
||||||
|
if (errors.length)
|
||||||
|
return {errors:errors};
|
||||||
|
|
||||||
|
var objout = FS.readFile(objpath, {encoding:'binary'});
|
||||||
|
putWorkFile(objpath, objout);
|
||||||
|
if (!anyTargetChanged(step, [objpath]))
|
||||||
|
return;
|
||||||
|
|
||||||
|
var symbolmap = {};
|
||||||
|
var segments = [];
|
||||||
|
var listings : CodeListingMap = {};
|
||||||
|
return {
|
||||||
|
output:objout, //.slice(0),
|
||||||
|
listings:listings,
|
||||||
|
errors:errors,
|
||||||
|
symbolmap:symbolmap,
|
||||||
|
segments:segments
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function assembleVASMARM(step:BuildStep) {
|
||||||
|
loadNative("vasmarm_std");
|
||||||
|
/// error 2 in line 8 of "gfxtest.c": unknown mnemonic <ew>
|
||||||
|
/// error 3007: undefined symbol <XXLOOP>
|
||||||
|
/// TODO: match undefined symbols
|
||||||
|
var re_err1 = /^(fatal error|error|warning)? (\d+) in line (\d+) of "(.+)": (.+)/;
|
||||||
|
var re_err2 = /^(fatal error|error|warning)? (\d+): (.+)/;
|
||||||
|
var errors : WorkerError[] = [];
|
||||||
|
function match_fn(s) {
|
||||||
|
var matches = re_err1.exec(s);
|
||||||
|
if (matches) {
|
||||||
|
errors.push({
|
||||||
|
line:parseInt(matches[3]),
|
||||||
|
path:matches[2],
|
||||||
|
msg:matches[5],
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
matches = re_err2.exec(s);
|
||||||
|
if (matches) {
|
||||||
|
errors.push({
|
||||||
|
line:0,
|
||||||
|
msg:s,
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
console.log(s);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
gatherFiles(step, {mainFilePath:"main.asm"});
|
||||||
|
var objpath = step.prefix+".bin";
|
||||||
|
var lstpath = step.prefix+".lst";
|
||||||
|
|
||||||
|
if (staleFiles(step, [objpath])) {
|
||||||
|
var args = [ '-Fbin', '-x', '-wfail', step.path, '-o', objpath, '-L', lstpath ];
|
||||||
|
var vasm = emglobal.vasm({
|
||||||
|
instantiateWasm: moduleInstFn('vasmarm_std'),
|
||||||
|
noInitialRun:true,
|
||||||
|
print:match_fn,
|
||||||
|
printErr:match_fn,
|
||||||
|
});
|
||||||
|
|
||||||
|
var FS = vasm['FS'];
|
||||||
|
populateFiles(step, FS);
|
||||||
|
execMain(step, vasm, args);
|
||||||
|
if (errors.length) {
|
||||||
|
return {errors:errors};
|
||||||
|
}
|
||||||
|
|
||||||
|
var objout = FS.readFile(objpath, {encoding:'binary'});
|
||||||
|
putWorkFile(objpath, objout);
|
||||||
|
if (!anyTargetChanged(step, [objpath]))
|
||||||
|
return;
|
||||||
|
|
||||||
|
var lstout = FS.readFile(lstpath, {encoding:'utf8'});
|
||||||
|
//console.log(lstout);
|
||||||
|
// F00:0001 mov r0, #0x884400 ; RGB value
|
||||||
|
// S01:00000000: 11 0B A0 E3 22 07 80 E3
|
||||||
|
// S01 .text
|
||||||
|
// F00 vidfill.vasm
|
||||||
|
// LOOP LAB (0x10) sec=.text
|
||||||
|
var symbolmap = {};
|
||||||
|
var segments = [];
|
||||||
|
var listings : CodeListingMap = {};
|
||||||
|
// TODO: parse listings
|
||||||
|
var re_lstline = /^F(\d+):(\d+)\s+(.+)/;
|
||||||
|
var re_secline = /^\s+S(\d+):([0-9A-F]+):\s*([0-9A-F ]+)/;
|
||||||
|
var re_nameline = /^([SF])(\d+)\s+(.+)/;
|
||||||
|
var files = {};
|
||||||
|
var sections = {};
|
||||||
|
// map file and section indices -> names
|
||||||
|
var lines = lstout.split(re_crlf);
|
||||||
|
for (var line of lines) {
|
||||||
|
var m;
|
||||||
|
if (m = re_nameline.exec(line)) {
|
||||||
|
if (m[1] == 'F') {
|
||||||
|
files[m[2]] = m[3];
|
||||||
|
} else {
|
||||||
|
sections[m[2]] = m[3];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//console.log(files, sections);
|
||||||
|
// parse lines
|
||||||
|
var lstlines : SourceLine[] = [];
|
||||||
|
var linenum = 0;
|
||||||
|
for (var line of lines) {
|
||||||
|
var m;
|
||||||
|
if (m = re_lstline.exec(line)) {
|
||||||
|
linenum = parseInt(m[2]);
|
||||||
|
} else if (m = re_secline.exec(line)) {
|
||||||
|
lstlines.push({
|
||||||
|
line: linenum,
|
||||||
|
offset: parseInt(m[2], 16),
|
||||||
|
path: step.path,
|
||||||
|
insns: m[3].replaceAll(' ','')
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
listings[lstpath] = {lines:lstlines};
|
||||||
|
|
||||||
|
return {
|
||||||
|
output:objout, //.slice(0),
|
||||||
|
listings:listings,
|
||||||
|
errors:errors,
|
||||||
|
symbolmap:symbolmap,
|
||||||
|
segments:segments
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
////////////////////////////
|
////////////////////////////
|
||||||
|
|
||||||
var TOOLS = {
|
var TOOLS = {
|
||||||
@ -2896,6 +3049,8 @@ var TOOLS = {
|
|||||||
'basic': compileBASIC,
|
'basic': compileBASIC,
|
||||||
'silice': compileSilice,
|
'silice': compileSilice,
|
||||||
'wiz': compileWiz,
|
'wiz': compileWiz,
|
||||||
|
'armips': assembleARMIPS,
|
||||||
|
'vasmarm': assembleVASMARM,
|
||||||
}
|
}
|
||||||
|
|
||||||
var TOOL_PRELOADFS = {
|
var TOOL_PRELOADFS = {
|
||||||
|
@ -85,7 +85,7 @@ describe('Worker', function() {
|
|||||||
compile('dasm', '\tprocessor 6502\n\torg $f000\n MAC mack\n lda #0\n ENDM\nfoo: mack\n mack\n', 'vcs.mame', done, 4, 4);
|
compile('dasm', '\tprocessor 6502\n\torg $f000\n MAC mack\n lda #0\n ENDM\nfoo: mack\n mack\n', 'vcs.mame', done, 4, 4);
|
||||||
});
|
});
|
||||||
it('should NOT assemble DASM', function(done) {
|
it('should NOT assemble DASM', function(done) {
|
||||||
compile('dasm', '\tprocessor 6502\n\torg $f000 ; this is a comment\nfoo asl a\n', 'vcs', done, 0, 0, 3);
|
compile('dasm', '\tprocessor 6502\n\torg $f000 ; this is a comment\nfoo asl a\n', 'vcs', done, 0, 0, 2);
|
||||||
});
|
});
|
||||||
/*
|
/*
|
||||||
it('should assemble ACME', function(done) {
|
it('should assemble ACME', function(done) {
|
||||||
@ -142,7 +142,7 @@ describe('Worker', function() {
|
|||||||
});
|
});
|
||||||
it('should compile galaxian skeleton', function(done) {
|
it('should compile galaxian skeleton', function(done) {
|
||||||
var csource = ab2str(fs.readFileSync('presets/galaxian-scramble/skeleton.sdcc'));
|
var csource = ab2str(fs.readFileSync('presets/galaxian-scramble/skeleton.sdcc'));
|
||||||
compile('sdcc', csource, 'galaxian-scramble', done, 20512, 29, 0);
|
compile('sdcc', csource, 'galaxian-scramble', done, 20512, 34, 0);
|
||||||
});
|
});
|
||||||
it('should compile vector skeleton', function(done) {
|
it('should compile vector skeleton', function(done) {
|
||||||
var csource = ab2str(fs.readFileSync('presets/vector-z80color/skeleton.sdcc'));
|
var csource = ab2str(fs.readFileSync('presets/vector-z80color/skeleton.sdcc'));
|
||||||
@ -150,7 +150,7 @@ describe('Worker', function() {
|
|||||||
});
|
});
|
||||||
it('should compile williams skeleton', function(done) {
|
it('should compile williams skeleton', function(done) {
|
||||||
var csource = ab2str(fs.readFileSync('presets/williams-z80/skeleton.sdcc'));
|
var csource = ab2str(fs.readFileSync('presets/williams-z80/skeleton.sdcc'));
|
||||||
compile('sdcc', csource, 'williams-z80', done, 38912, 38, 0);
|
compile('sdcc', csource, 'williams-z80', done, 38912, 40, 0);
|
||||||
});
|
});
|
||||||
it('should compile williams_sound skeleton', function(done) {
|
it('should compile williams_sound skeleton', function(done) {
|
||||||
var csource = ab2str(fs.readFileSync('presets/sound_williams-z80/skeleton.sdcc'));
|
var csource = ab2str(fs.readFileSync('presets/sound_williams-z80/skeleton.sdcc'));
|
||||||
|
1
unicorn.js
Submodule
1
unicorn.js
Submodule
@ -0,0 +1 @@
|
|||||||
|
Subproject commit a872af72bc1c538a1f9ab73a030192f366a0912e
|
Loading…
Reference in New Issue
Block a user