arm32: working w/ gba emu

This commit is contained in:
Steven Hugg 2021-06-06 00:50:45 -05:00
parent 7725884eb0
commit 76d29c6026
10 changed files with 661 additions and 241 deletions

View File

@ -1,6 +1,6 @@
.set VIDMEM, 0x20000000
.set VIDMEM, 0x40000000
mov r0, #0x884400 ; RGB value
mov r0, #0xff880000 ; RGB value
mov r1, #VIDMEM ; memory start
LOOP2:
mov r2, #160*128 ; word count
@ -9,7 +9,8 @@ LOOP:
sub r2, r2, #1
cmp r2, #0
bge LOOP
sub r0, r0, #0xff11
add r0, r0, #0xf002
cmp r0, #0
bgt LOOP2
blt LOOP2
.ualong 0x11223344 ; illegal instruction

View File

@ -1220,6 +1220,14 @@ export abstract class BaseMachinePlatform<T extends Machine> extends BaseDebugPl
readAddress(addr : number) : number {
return this.machine.read(addr);
}
getDebugCategories() {
if (isDebuggable(this.machine))
return this.machine.getDebugCategories();
}
getDebugInfo(category:string, state:EmuState) : string {
return isDebuggable(this.machine) && this.machine.getDebugInfo(category, state);
}
}
// TODO: move debug info into CPU?

File diff suppressed because it is too large Load Diff

View File

@ -626,16 +626,16 @@ type AddressDecoderOptions = {gmask?:number};
// TODO: better performance, check values
export function AddressDecoder(table : AddressDecoderEntry[], options?:AddressDecoderOptions) {
var self = this;
function makeFunction(lo, hi) {
function makeFunction() {
var s = "";
if (options && options.gmask) {
s += "a&=" + options.gmask + ";";
}
for (var i=0; i<table.length; i++) {
var entry = table[i];
var start = entry[0];
var end = entry[1];
var mask = entry[2];
var start = entry[0]|0;
var end = entry[1]|0;
var mask = entry[2]|0;
var func = entry[3];
self['__fn'+i] = func;
s += "if (a>=" + start + " && a<="+end + "){";
@ -645,7 +645,7 @@ export function AddressDecoder(table : AddressDecoderEntry[], options?:AddressDe
s += "return 0;"; // TODO: noise()?
return new Function('a', 'v', s);
}
return makeFunction(0x0, 0xffff).bind(self);
return makeFunction().bind(self);
}
export function newAddressDecoder(table : AddressDecoderEntry[], options?:AddressDecoderOptions) : (a:number,v?:number) => number {

View File

@ -32,7 +32,11 @@ export function getFilenamePrefix(s:string):string {
export function hex(v:number, nd?:number) {
if (!nd) nd = 2;
return toradix(v,nd,16);
if (nd == 8) {
return hex((v>>16)&0xffff,4) + hex(v&0xffff,4);
} else {
return toradix(v,nd,16);
}
}
export function tobin(v:number, nd?:number) {

View File

@ -55,7 +55,7 @@ var debugCategory; // current debug category
var debugTickPaused = false;
var recorderActive = false;
var lastViewClicked = null;
var errorWasRuntime = false;
var lastBreakExpr = "c.PC == 0x6000";
// TODO: codemirror multiplex support?
@ -1106,14 +1106,16 @@ function getErrorElement(err : WorkerError) {
function hideErrorAlerts() {
$("#error_alert").hide();
errorWasRuntime = false;
}
function showErrorAlert(errors : WorkerError[]) {
function showErrorAlert(errors : WorkerError[], runtime : boolean) {
var div = $("#error_alert_msg").empty();
for (var err of errors.slice(0,10)) {
div.append(getErrorElement(err));
}
$("#error_alert").show();
errorWasRuntime = runtime;
}
function showExceptionAsError(err, msg:string) {
@ -1125,7 +1127,7 @@ function showExceptionAsError(err, msg:string) {
console.log(werr);
projectWindows.refresh(false);
}
showErrorAlert([werr]);
showErrorAlert([werr], true);
}
}
@ -1146,7 +1148,7 @@ function setCompileOutput(data: WorkerResult) {
if (data && data.errors && data.errors.length > 0) {
toolbar.addClass("has-errors");
projectWindows.setErrors(data.errors);
showErrorAlert(data.errors);
showErrorAlert(data.errors, false);
} else {
toolbar.removeClass("has-errors"); // may be added in next callback
projectWindows.setErrors(null);
@ -1286,7 +1288,7 @@ function setupDebugCallback(btnid? : string) {
if (platform.setupDebug) platform.setupDebug((state:EmuState, msg:string) => {
uiDebugCallback(state);
setDebugButtonState(btnid||"pause", "stopped");
msg && showErrorAlert([{msg:"STOPPED: " + msg, line:0}]);
msg && showErrorAlert([{msg:"STOPPED: " + msg, line:0}], true);
});
}
@ -1318,7 +1320,7 @@ function _resume() {
console.log("Resumed");
}
setDebugButtonState("go", "active");
// TODO: hide alerts, but only if exception generated them
if (errorWasRuntime) { hideErrorAlerts(); }
}
function resume() {

View File

@ -553,7 +553,8 @@ export class DisassemblerView implements ProjectView {
}
return s;
}
let text = disassemble(pc-disasmWindow, disasmWindow) + disassemble(pc, disasmWindow);
var startpc = pc < 0 ? pc-disasmWindow : Math.max(0, pc-disasmWindow); // for 32-bit PCs w/ hi bit set
let text = disassemble(startpc, pc-startpc) + disassemble(pc, disasmWindow);
this.disasmview.setValue(text);
if (moveCursor) {
this.disasmview.setCursor(selline, 0);

121
src/machine/arm32.ts Normal file
View File

@ -0,0 +1,121 @@
import { ARM32CPU, ARMCoreState } from "../common/cpu/ARM";
import { BasicScanlineMachine } from "../common/devices";
import { KeyFlags, newAddressDecoder, padBytes, Keys, makeKeycodeMap, newKeyboardHandler } from "../common/emu";
import { TssChannelAdapter, MasterAudio, AY38910_Audio } from "../common/audio";
import { Debuggable, EmuState } from "../common/baseplatform";
import { hex, lpad, printFlags } from "../common/util";
const SPACEINV_KEYCODE_MAP = makeKeycodeMap([
[Keys.A, 1, 0x10], // P1
[Keys.LEFT, 1, 0x20],
[Keys.RIGHT, 1, 0x40],
[Keys.P2_A, 2, 0x10], // P2
[Keys.P2_LEFT, 2, 0x20],
[Keys.P2_RIGHT, 2, 0x40],
[Keys.SELECT, 1, 0x1],
[Keys.START, 1, 0x4],
[Keys.P2_START, 1, 0x2],
]);
const ROM_START = 0x0;
const ROM2_START= 0xff800000;
const ROM_SIZE = 0x80000;
const RAM_START = 0x20000000;
const RAM_SIZE = 0x80000;
const VID_START = 0x40000000;
const VID_SIZE = 0x20000;
const CPU_FREQ = 4000000;
export class ARM32Machine extends BasicScanlineMachine implements Debuggable {
cpuFrequency = CPU_FREQ; // MHz
canvasWidth = 160;
numTotalScanlines = 256;
numVisibleScanlines = 128;
cpuCyclesPerLine = Math.floor(CPU_FREQ / (256*60));
defaultROMSize = 512*1024;
sampleRate = 1;
cpu: ARM32CPU = new ARM32CPU();
ram = new Uint8Array(512*1024);
pixels8 : Uint8Array;
constructor() {
super();
this.connectCPUMemoryBus(this);
this.handler = newKeyboardHandler(this.inputs, SPACEINV_KEYCODE_MAP);
}
connectVideo(pixels:Uint32Array) : void {
super.connectVideo(pixels);
this.pixels8 = new Uint8Array(pixels.buffer);
//this.pixels.fill(0xff000000);
}
read = newAddressDecoder([
[ROM_START, ROM_START+ROM_SIZE-1, ROM_SIZE-1, (a) => {
return this.rom ? this.rom[a] : 0;
}],
[RAM_START, RAM_START+RAM_SIZE-1, RAM_SIZE-1, (a) => {
return this.ram[a];
}],
[ROM2_START, ROM2_START+ROM_SIZE-1, ROM_SIZE-1, (a) => {
return this.rom ? this.rom[a] : 0;
}],
]);
write = newAddressDecoder([
[RAM_START, RAM_START+RAM_SIZE-1, RAM_SIZE-1, (a, v) => {
this.ram[a] = v;
}],
[VID_START, VID_START+VID_SIZE-1, VID_SIZE-1, (a, v) => {
this.pixels8[a] = v;
}],
]);
startScanline() {
}
drawScanline() {
// at end of scanline
}
getDebugCategories() {
return ['CPU'];
}
getDebugInfo?(category: string, state: EmuState) : string {
var s = '';
var c = state.c as ARMCoreState;
for (var i=0; i<13; i++) {
s += lpad('r'+i, 3) + ' ' + hex(c.gprs[i],8) + '\n';
}
s += ' SP ' + hex(c.SP,8) + '\n';
s += ' LR ' + hex(c.gprs[14],8) + '\n';
s += ' PC ' + hex(c.PC,8) + '\n';
s += c.cpsrN ? " N" : " -";
s += c.cpsrV ? " V" : " -";
s += c.cpsrF ? " F" : " -";
s += c.cpsrZ ? " Z" : " -";
s += c.cpsrC ? " C" : " -";
s += c.cpsrI ? " I" : " -";
s += '\n';
s += 'MODE ' + MODE_NAMES[c.mode];
s += '\n';
s += 'cycl ' + c.cycles;
s += '\n';
return s;
}
}
const MODE_NAMES = {
0x10: "USER",
0x11: "FIQ",
0x12: "IRQ",
0x13: "SUPERVISOR",
0x17: "ABORT",
0x1b: "UNDEFINED",
0x1f: "SYSTEM",
};

View File

@ -6,12 +6,6 @@ import { TssChannelAdapter, MasterAudio, AY38910_Audio } from "../common/audio";
// http://www.computerarcheology.com/Arcade/
const MW8080BW_PRESETS = [
{ id: 'gfxtest.c', name: 'Graphics Test' },
{ id: 'shifter.c', name: 'Sprite w/ Bit Shifter' },
{ id: 'game2.c', name: 'Cosmic Impalas' },
];
const SPACEINV_KEYCODE_MAP = makeKeycodeMap([
[Keys.A, 1, 0x10], // P1
[Keys.LEFT, 1, 0x20],

View File

@ -1,10 +1,10 @@
"use strict";
import { BaseDebugPlatform, CpuState, EmuState, Platform, DisasmLine, Debuggable } from "../common/baseplatform";
import { BaseDebugPlatform, CpuState, EmuState, Platform, DisasmLine, Debuggable, Machine, BaseMachinePlatform } 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";
import { ARM32Machine } from "../machine/arm32";
declare var uc, cs : any; // Unicorn module
@ -25,7 +25,7 @@ interface ARM32State extends EmuState {
r: Uint32Array; // registers
}
class ARM32Platform extends BaseDebugPlatform implements Platform, Debuggable {
class ARM32UnicornPlatform extends BaseDebugPlatform implements Platform, Debuggable {
u : any; // Unicorn
d : any; // Capstone
@ -48,7 +48,7 @@ class ARM32Platform extends BaseDebugPlatform implements Platform, Debuggable {
await loadScript('./lib/unicorn-arm.min.js');
await loadScript('./lib/capstone-arm.min.js');
//this.cpu = new ARM32CPU();
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);
@ -63,7 +63,7 @@ class ARM32Platform extends BaseDebugPlatform implements Platform, Debuggable {
this.timer = new AnimationTimer(60, this.nextFrame.bind(this));
}
reset() {
//this.cpu.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);
@ -89,8 +89,8 @@ class ARM32Platform extends BaseDebugPlatform implements Platform, Debuggable {
}
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 b = this.u.mem_read(pc, 4);
var insns = this.d.disasm(b, pc, 4);
var i0 = insns[0];
return {
nbytes: i0.size,
@ -220,29 +220,59 @@ class ARM32Platform extends BaseDebugPlatform implements Platform, Debuggable {
}
}
/*
////
export abstract class BaseARMMachinePlatform<T extends Machine> extends BaseMachinePlatform<T> {
//getOpcodeMetadata = getOpcodeMetadata_z80;
getToolForFilename() { return "armips"; }
getToolForFilename() { return "vasmarm"; }
}
class ARM32Platform extends BaseARMMachinePlatform<ARM32Console> implements Platform {
class ARM32Platform extends BaseARMMachinePlatform<ARM32Machine> implements Platform {
capstone : any;
async start() {
console.log("Loading Unicorn");
await loadScript('./unicorn.js/dist/unicorn-arm.min.js');
return super.start();
super.start();
console.log("Loading Capstone");
await loadScript('./lib/capstone-arm.min.js');
this.capstone = new cs.Capstone(cs.ARCH_ARM, cs.MODE_ARM);
}
newMachine() { return new ARM32Console(); }
newMachine() { return new ARM32Machine(); }
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'},
{name:'ROM',start:0x00000000,size:0x80000,type:'rom'},
{name:'RAM',start:0x20000000,size:0x80000,type:'ram'},
{name:'Video RAM',start:0x40000000,size:0x20000,type:'ram'},
] } };
disassemble(pc:number, read:(addr:number)=>number) : DisasmLine {
var buf = [];
for (var i=0; i<4; i++) {
buf[i] = read(pc+i);
}
var insns = this.capstone.disasm(buf, pc, 4);
var i0 = insns && insns[0];
if (i0) {
return {
nbytes: i0.size,
line: i0.mnemonic + " " + i0.op_str,
isaddr: i0.address > 0
};
} else {
return {
nbytes: 4,
line: "???",
isaddr: false
};
}
}
}
*/
////
PLATFORMS['arm32.u'] = ARM32UnicornPlatform;
PLATFORMS['arm32'] = ARM32Platform;