arm: arm-tcc

This commit is contained in:
Steven Hugg 2023-12-27 15:24:42 -05:00
parent 8023d56b88
commit c0909bef1b
19 changed files with 498 additions and 62 deletions

View File

@ -84,6 +84,7 @@ The IDE uses custom forks for many of these, found at https://github.com/sehugg?
* https://github.com/wiz-lang/wiz
* https://github.com/sylefeb/Silice
* https://github.com/steux/cc7800
* https://bellard.org/tcc/
### Assemblers/Linkers

9
presets/arm32/crt0.c Normal file
View File

@ -0,0 +1,9 @@
int main();
void _start() {
asm("mov sp, #0x100000"); // stack pointer
main();
asm(".long 0xdeadc0d3");
}

27
presets/arm32/vidfill.c Normal file
View File

@ -0,0 +1,27 @@
//#link "crt0.c"
const char const str[] = "HELLO WORLD!";
int global = 0x1234;
int global2 = 0x123456;
#define VIDBASE ((int*)0x4000080)
int vidbuf[160*128];
int main() {
*VIDBASE = (int)vidbuf;
global += str[0];
global++;
global2++;
int c = 0xff880000;
c += str[0];
int* p = (int*) vidbuf;
for (int i=0; i<160*128; i++) {
p[i] = c++;
}
return 0;
}

View File

@ -27,7 +27,7 @@ ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
*/
import { Bus, CPU, InstructionBased, SavesState } from "../devices";
import { Bus, Bus32, CPU, InstructionBased, SavesState } from "../devices";
import { EmuHalt } from "../emu";
import { hex } from "../util";
@ -103,12 +103,16 @@ export interface ARMCoreState {
bankedRegisters: number[][],
spsr: number,
bankedSPSRs: number[],
sfprs: number[],
dfprs: number[],
cycles: number,
instructionWidth: 2 | 4
}
interface ARMCoreType {
gprs: Int32Array;
sfprs: Float32Array;
dfprs: Float64Array;
PC: number;
SP: number;
LR: number;
@ -659,6 +663,11 @@ ARMCoreArm.prototype.constructAddressingMode4Writeback = function(immediate, off
}
};
ARMCoreArm.prototype.constructNOP = function() {
this.writesPC = false;
return function() { };
}
ARMCoreArm.prototype.constructADC = function(rd, rn, shiftOp, condOp) {
var cpu : ARMCoreType = this.cpu;
var gprs = cpu.gprs;
@ -2658,6 +2667,8 @@ function ARMCore() {
this.generateConds();
this.gprs = new Int32Array(16);
this.dfprs = new Float64Array(16);
this.sfprs = new Float32Array(this.dfprs.buffer); // regs shared with dfprs
};
ARMCore.prototype.resetCPU = function(startOffset) {
@ -2665,6 +2676,7 @@ ARMCore.prototype.resetCPU = function(startOffset) {
this.gprs[i] = 0;
}
this.gprs[ARMRegs.PC] = startOffset + ARMConstants.WORD_SIZE_ARM;
this.dfprs.set(0); // no need to zero the sfprs, since they share the same buffer
this.loadInstruction = this.loadInstructionArm;
this.execMode = ARMMode.MODE_ARM;
@ -2769,6 +2781,8 @@ ARMCore.prototype.freeze = function() : ARMCoreState {
this.gprs[14],
this.gprs[15],
],
'sfprs': this.sfprs.slice(),
'dfprs': this.dfprs.slice(),
'mode': this.mode,
'cpsrI': this.cpsrI,
'cpsrF': this.cpsrF,
@ -2850,6 +2864,9 @@ ARMCore.prototype.defrost = function(frost: ARMCoreState) {
this.gprs[14] = frost.gprs[14];
this.gprs[15] = frost.gprs[15];
//this.sfprs.set(frost.sfprs);
this.dfprs.set(frost.dfprs); // regs shared with sfprs
this.mode = frost.mode;
this.cpsrI = frost.cpsrI;
this.cpsrF = frost.cpsrF;
@ -3667,6 +3684,25 @@ ARMCore.prototype.compileArm = function(instruction) {
break;
case 0x0C000000:
// Coprocessor data transfer
// VSTM, VSTMDB, VSTMIA
if ((instruction & 0x0c100f00) == 0x0c000a00) {
// TODO
op = this.armCompiler.constructNOP();
/* TODO
const rn = (instruction & 0x000F0000) >> 16;
const vd = (instruction & 0x0000F000) >> 12;
const imm = instruction & 0x000000FF;
const writeback = instruction & 0x00200000;
const increment = instruction & 0x00800000;
const load = instruction & 0x00100000;
const user = instruction & 0x00400000;
op.writesPC = false;
*/
}
else if ((instruction & 0x0c100f00) == 0x0c100a00) {
// TODO: VSTR, VLDR
op = this.armCompiler.constructNOP();
}
break;
case 0x0E000000:
// Coprocessor data operation/SWI
@ -4103,10 +4139,12 @@ ARMCore.prototype.compileThumb = function(instruction) {
///////////////////////////////////////////////////////////////////////////
type ARMBus = Bus & Bus32;
export class ARM32CPU implements CPU, InstructionBased, ARMMMUInterface, ARMIRQInterface, SavesState<ARMCoreState> {
core : ARMCoreType;
bus : Bus;
bus : ARMBus;
memory : ARMMemoryRegion[];
BASE_OFFSET = 24;
@ -4122,8 +4160,7 @@ export class ARM32CPU implements CPU, InstructionBased, ARMMMUInterface, ARMIRQI
this.memory = []; // TODO
for (var i=0; i<256; i++) {
// TODO: constant
var bits = 10;
var size = 0x80000;
const bits = 10;
this.memory[i] = {
PAGE_MASK: (2 << bits) - 1,
ICACHE_PAGE_BITS: bits,
@ -4146,12 +4183,13 @@ export class ARM32CPU implements CPU, InstructionBased, ARMMMUInterface, ARMIRQI
isStable(): boolean {
return true; // TODO?
}
connectMemoryBus(bus: Bus): void {
connectMemoryBus(bus: ARMBus): void {
this.bus = bus;
}
reset(): void {
this.resetMemory();
this.core.resetCPU(0);
const resetVector = this.load32(0);
this.core.resetCPU(resetVector);
}
saveState() : ARMCoreState {
return this.core.freeze();
@ -4173,7 +4211,7 @@ export class ARM32CPU implements CPU, InstructionBased, ARMMMUInterface, ARMIRQI
return this.bus.read(a) | (this.bus.read(a+1) << 8);
}
load32(a: number): number {
var v = this.bus.read(a) | (this.bus.read(a+1) << 8) | (this.bus.read(a+2) << 16) | (this.bus.read(a+3) << 24);
var v = this.bus.read32(a);
return v;
}
// TODO: memory.invalidatePage(maskedOffset);
@ -4185,10 +4223,7 @@ export class ARM32CPU implements CPU, InstructionBased, ARMMMUInterface, ARMIRQI
this.bus.write(a+1, (v >> 8) & 0xff);
}
store32(a: number, v: number): void {
this.bus.write(a, v & 0xff);
this.bus.write(a+1, (v >> 8) & 0xff);
this.bus.write(a+2, (v >> 16) & 0xff);
this.bus.write(a+3, (v >> 24) & 0xff);
this.bus.write32(a, v);
}
// TODO
wait(a: number): void {
@ -4253,4 +4288,10 @@ export class ARM32CPU implements CPU, InstructionBased, ARMMMUInterface, ARMIRQI
isThumb() : boolean {
return this.core.instructionWidth == 2;
}
getDebugTree() {
return {
state: this.saveState(),
mmu: this.core.mmu
};
}
}

View File

@ -10,6 +10,12 @@ export interface Bus {
readConst?(a: number): number;
}
export interface Bus32 {
read32(a: number): number;
write32(a: number, v: number): void;
readConst32?(a: number): number;
}
export interface ClockBased {
advanceClock(): void;
}
@ -276,7 +282,7 @@ export abstract class BasicHeadlessMachine implements HasCPU, Bus, AcceptsROM, P
this.probe.logClocks(n);
return n;
}
probeMemoryBus(membus: Bus): Bus {
probeMemoryBus(membus: Bus & Partial<Bus32>): Bus & Partial<Bus32> {
return {
read: (a) => {
let val = membus.read(a);
@ -286,11 +292,20 @@ export abstract class BasicHeadlessMachine implements HasCPU, Bus, AcceptsROM, P
write: (a, v) => {
this.probe.logWrite(a, v);
membus.write(a, v);
},
read32: (a) => {
let val = membus.read32(a);
this.probe.logRead(a, val);
return val;
},
write32: (a, v) => {
this.probe.logWrite(a, v);
membus.write32(a, v);
}
};
}
connectCPUMemoryBus(membus: Bus): void {
this.cpu.connectMemoryBus(this.probeMemoryBus(membus));
this.cpu.connectMemoryBus(this.probeMemoryBus(membus as Bus&Bus32));
}
probeIOBus(iobus: Bus): Bus {
return {
@ -302,7 +317,7 @@ export abstract class BasicHeadlessMachine implements HasCPU, Bus, AcceptsROM, P
write: (a, v) => {
this.probe.logIOWrite(a, v);
iobus.write(a, v);
}
},
};
}
probeDMABus(iobus: Bus): Bus {

159
src/common/elfparser.ts Normal file
View File

@ -0,0 +1,159 @@
export class ELFParser {
readonly dataView: DataView;
readonly sectionHeaders: ElfSectionHeader[];
readonly symbolTable: ElfSymbolTableEntry[];
entry: number;
constructor(data: Uint8Array) {
this.dataView = new DataView(data.buffer);
this.sectionHeaders = [];
this.symbolTable = [];
this.parse();
}
private parse() {
const elfHeader = new DataView(this.dataView.buffer, 0, 52);
// check magic #
const magic = elfHeader.getInt32(0, true);
if (magic !== 0x464c457f) {
throw new Error('Invalid ELF header');
}
// only 32 bit supported
if (elfHeader.getUint8(4) !== 1) {
throw new Error('Only 32-bit ELF supported');
}
// check version = 1
if (elfHeader.getUint8(6) !== 1) {
throw new Error('Invalid ELF version');
}
// get endianness
const endian = elfHeader.getUint8(5) === 1;
if (!endian) {
throw new Error('Big endian not supported');
}
// get entryPoint
this.entry = elfHeader.getUint32(24, endian);
// Parse ELF header and extract section header offset
const sectionHeaderOffset = this.dataView.getUint32(32, endian);
// get section header size
const sectionHeaderSize = this.dataView.getUint16(46, endian);
// get # of section headers
const sectionHeaderCount = this.dataView.getUint16(48, endian);
// get index of section with names
const sectionNameIndex = this.dataView.getUint16(50, endian);
// Parse section headers
for (let i = 0; i < sectionHeaderCount; i++) {
const offset = sectionHeaderOffset + i * sectionHeaderSize; // Each section header is 40 bytes
//const sectionView = new DataView(this.dataView.buffer, offset, sectionHeaderSize);
const section = new ElfSectionHeader(this.dataView, offset);
this.sectionHeaders.push(section);
}
const sectionNameSection = this.sectionHeaders[sectionNameIndex];
if (!sectionNameSection) {
throw new Error('Invalid ELF section name table');
} else {
const stringTableOffset = sectionNameSection.offset;
const stringTableSize = sectionNameSection.size;
const sectionNameView = new DataView(this.dataView.buffer, stringTableOffset, stringTableSize);
for (let i = 0; i < sectionHeaderCount; i++) {
this.sectionHeaders[i].stringView = sectionNameView;
}
}
// Extract the string table
const stringTableSection = this.sectionHeaders.find(
(section) => section.type === ElfSectionType.STRTAB && section.name == '.strtab'
);
if (stringTableSection) {
const stringTableOffset = stringTableSection.offset;
const stringTableSize = stringTableSection.size;
const stringView = new DataView(this.dataView.buffer, stringTableOffset, stringTableSize);
// Find the symbol table section and string table section
const symbolTableSection = this.sectionHeaders.find(
(section) => section.name === '.symtab'
);
if (symbolTableSection) {
// Extract the symbol table
const symbolTableOffset = symbolTableSection.offset;
const symbolTableSize = symbolTableSection.size;
const symbolTableEntryCount = symbolTableSize / 16;
//const symbolTable = new DataView(this.dataView.buffer, symbolTableOffset, symbolTableSize);
for (let i = 0; i < symbolTableEntryCount; i++) {
const offset = symbolTableOffset + i * 16;
const entry = new ElfSymbolTableEntry(this.dataView, offset, stringView);
this.symbolTable.push(entry);
}
}
}
}
getSymbols(): ElfSymbolTableEntry[] {
return this.symbolTable;
}
}
enum ElfSectionType {
SYMTAB = 2,
STRTAB = 3,
}
class ElfSectionHeader {
readonly type: number;
stringView: DataView | null = null;
constructor(readonly dataView: DataView, readonly headerOffset: number) {
this.type = this.dataView.getUint32(this.headerOffset + 0x4, true);
}
get offset(): number {
return this.dataView.getUint32(this.headerOffset + 0x10, true);
}
get size(): number {
return this.dataView.getUint32(this.headerOffset + 0x14, true);
}
get nameOffset(): number {
return this.dataView.getUint32(this.headerOffset + 0x0, true);
}
get name(): string {
return getUTF8(this.stringView!, this.nameOffset);
}
get vaddr(): number {
return this.dataView.getUint32(this.headerOffset + 0xc, true);
}
}
class ElfSymbolTableEntry {
constructor(readonly dataView: DataView,
readonly entryOffset: number,
readonly stringView: DataView) {
}
get nameOffset(): number {
return this.dataView.getUint32(this.entryOffset, true);
}
get name(): string {
return getUTF8(this.stringView, this.nameOffset);
}
get value(): number {
return this.dataView.getUint32(this.entryOffset + 4, true);
}
get size(): number {
return this.dataView.getUint32(this.entryOffset + 8, true);
}
get info(): number {
return this.dataView.getUint8(this.entryOffset + 12);
}
get other(): number {
return this.dataView.getUint8(this.entryOffset + 13);
}
}
function getUTF8(view: DataView, offset: number): string {
let str = '';
for (let i = offset; view.getUint8(i) !== 0; i++) {
str += String.fromCharCode(view.getUint8(i));
}
return str;
}

View File

@ -658,7 +658,7 @@ export function padBytes(data:Uint8Array|number[], len:number, padstart?:boolean
type AddressReadWriteFn = ((a:number) => number) | ((a:number,v:number) => void);
type AddressDecoderEntry = [number, number, number, AddressReadWriteFn];
type AddressDecoderOptions = {gmask?:number};
type AddressDecoderOptions = {gmask?:number, defaultval?:number};
// TODO: better performance, check values
export function AddressDecoder(table : AddressDecoderEntry[], options?:AddressDecoderOptions) {
@ -679,7 +679,7 @@ export function AddressDecoder(table : AddressDecoderEntry[], options?:AddressDe
if (mask) s += "a&="+mask+";";
s += "return this.__fn"+i+"(a,v)&0xff;}\n";
}
s += "return 0;"; // TODO: noise()?
s += "return "+(options?.defaultval|0)+";";
return new Function('a', 'v', s);
}
return makeFunction().bind(self);

View File

@ -274,7 +274,8 @@ export class CodeProject {
var depfiles = [];
msg.updates.push({path:mainfilename, data:maintext});
this.filename2path[mainfilename] = this.mainPath;
let usesRemoteTool = this.getToolForFilename(mainfilename).startsWith('remote:');
const tool = this.getToolForFilename(this.mainPath);
let usesRemoteTool = tool.startsWith('remote:');
for (var dep of depends) {
// remote tools send both includes and linked files in one build step
if (!dep.link || usesRemoteTool) {

View File

@ -136,6 +136,7 @@ const TOOL_TO_SOURCE_STYLE = {
'ecs': 'ecs',
'remote:llvm-mos': 'text/x-csrc',
'cc7800': 'text/x-csrc',
'armtcc': 'text/x-csrc',
}
// TODO: move into tool class

View File

@ -1,6 +1,6 @@
import { ARM32CPU, ARMCoreState } from "../common/cpu/ARM";
import { BasicScanlineMachine, HasSerialIO, SerialEvent, SerialIOInterface } from "../common/devices";
import { BasicScanlineMachine, Bus32, HasSerialIO, SerialEvent, SerialIOInterface } from "../common/devices";
import { newAddressDecoder, Keys, makeKeycodeMap, newKeyboardHandler, EmuHalt } from "../common/emu";
import { Debuggable, EmuState } from "../common/baseplatform";
import { hex, lpad } from "../common/util";
@ -18,36 +18,42 @@ var GBA_KEYCODE_MAP = makeKeycodeMap([
[Keys.DOWN, 0, 0x80],
]);
const ROM_START = 0x0;
const ROM_SIZE = 0x80000;
const RAM_START = 0x2000000;
const RAM_SIZE = 0x80000;
const RAM_START = 0x0;
const RAM_SIZE = 0x100000;
const ROM_BASE = 0x10000;
const ENTRY_POINT = 0x20000;
const IO_START = 0x4000000;
const IO_SIZE = 0x100;
const MAX_SERIAL_CHARS = 1000000;
const CPU_FREQ = 4000000; // 4 MHz
export class ARM32Machine extends BasicScanlineMachine implements Debuggable, HasSerialIO {
const ILLEGAL_OPCODE = 0xedededed;
export class ARM32Machine extends BasicScanlineMachine
implements Debuggable, HasSerialIO, Bus32 {
cpuFrequency = CPU_FREQ; // MHz
canvasWidth = 160;
numTotalScanlines = 256;
numVisibleScanlines = 128;
cpuCyclesPerLine = Math.floor(CPU_FREQ / (256*60));
defaultROMSize = 512*1024;
defaultROMSize = RAM_SIZE - ROM_BASE;
sampleRate = 1;
cpu: ARM32CPU = new ARM32CPU();
ram = new Uint8Array(96*1024);
ram = new Uint8Array(RAM_SIZE);
ram16 = new Uint16Array(this.ram.buffer);
ram32 = new Uint32Array(this.ram.buffer);
pixels32 : Uint32Array;
pixels8 : Uint8Array;
vidbase : number = 0;
rombase : number = ROM_BASE;
brightness : number = 255;
serial : SerialIOInterface;
serialOut : SerialEvent[];
serialIn : SerialEvent[];
ioregs = new Uint8Array(IO_SIZE);
ioregs32 = new Uint32Array(this.ioregs.buffer);
constructor() {
super();
@ -65,7 +71,25 @@ export class ARM32Machine extends BasicScanlineMachine implements Debuggable, Ha
this.serial = serial;
}
loadROM(rom: Uint8Array) {
super.loadROM(rom);
}
reset() {
if (this.rom) {
this.ram.set(this.rom, this.rombase);
// set ARM vectors
const obj32 = new Uint32Array(this.ram.buffer);
const start = ENTRY_POINT;
obj32[0] = start; // set reset vector
obj32[1] = start; // set undefined vector
obj32[2] = start; // set swi vector
obj32[3] = start; // set prefetch abort vector
obj32[4] = start; // set data abort vector
obj32[5] = start; // set reserved vector
obj32[6] = start; // set irq vector
obj32[7] = start; // set fiq vector
}
super.reset();
this.serialOut = [];
this.serialIn = [];
@ -74,19 +98,13 @@ export class ARM32Machine extends BasicScanlineMachine implements Debuggable, Ha
// TODO: 32-bit bus?
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];
}],
[IO_START, IO_START+IO_SIZE-1, IO_SIZE-1, (a, v) => {
return this.readIO(a);
}],
[0, (1<<31)-1, 0, (a, v) => {
throw new EmuHalt(`Address read out of bounds: 0x${hex(a)}`);
}]
]);
], {defaultval: ILLEGAL_OPCODE & 0xff});
write = newAddressDecoder([
[RAM_START, RAM_START+RAM_SIZE-1, RAM_SIZE-1, (a, v) => {
@ -97,6 +115,30 @@ export class ARM32Machine extends BasicScanlineMachine implements Debuggable, Ha
}],
]);
read32 = (a) => {
if (a >= RAM_START && a < RAM_SIZE && (a & 3) == 0) {
return this.ram32[a >> 2];
} else {
return this.read(a) | (this.read(a+1)<<8) | (this.read(a+2)<<16) | (this.read(a+3)<<24);
}
};
write32 = (a, v) => {
if (a >= RAM_START && a < RAM_SIZE && (a & 3) == 0) {
this.ram32[a >> 2] = v;
} else {
this.write(a, v & 0xff);
this.write(a+1, (v>>8) & 0xff);
this.write(a+2, (v>>16) & 0xff);
this.write(a+3, (v>>24) & 0xff);
}
}
readAddress(a : number) : number {
if (a >= RAM_START && a < RAM_START+RAM_SIZE) return this.read(a);
else return ILLEGAL_OPCODE;
}
readIO(a : number) : number {
switch (a) {
case 0x0:
@ -116,6 +158,7 @@ export class ARM32Machine extends BasicScanlineMachine implements Debuggable, Ha
}
writeIO(a : number, v : number) : void {
this.ioregs[a] = v;
switch (a) {
case 0x0:
//this.brightness = v & 0xff;
@ -136,7 +179,8 @@ export class ARM32Machine extends BasicScanlineMachine implements Debuggable, Ha
postFrame() {
var p32 = this.pixels32;
var vbase = (this.vidbase >> 1) & 0xfffff;
const vidbase = this.ioregs32[0x80 >> 2];
var vbase = (vidbase >> 1) & 0xfffff;
var mask = this.brightness << 24;
for (var i=0; i<p32.length; i++) {
var col = this.ram16[i + vbase];
@ -152,13 +196,29 @@ export class ARM32Machine extends BasicScanlineMachine implements Debuggable, Ha
getDebugInfo?(category: string, state: EmuState) : string {
switch (category) {
case 'Stack':
var s = '';
var c = state.c as ARMCoreState;
var sp = c.gprs[13];
var fp = c.gprs[11];
// dump stack using ram32
for (var i=0; i<16; i++) {
s += hex(sp,8) + ' ' + hex(this.ram32[(sp-RAM_START)>>2],8);
if (sp == fp) s += ' FP';
s += '\n';
sp += 4;
if (sp >= RAM_START+RAM_SIZE) break;
}
return s;
case 'CPU':
var s = '';
var c = state.c as ARMCoreState;
const EXEC_MODE = {2:'Thumb',4:'ARM'};
const REGNAMES = {15:'PC',14:'LR',13:'SP',12:'IP',11:'FP',9:'SB'};
for (var i=0; i<16; i++) {
s += lpad(REGNAMES[i]||'',3) + lpad('r'+i, 5) + ' ' + hex(c.gprs[i],8) + '\n';
for (var i=0; i<8; i++) {
let j = i+8;
s += lpad('r'+i, 5) + ' ' + hex(c.gprs[i],8) + ' ';
s += lpad('r'+j, 5) + ' ' + hex(c.gprs[j],8) + lpad(REGNAMES[j]||'',3) + '\n';
}
s += 'Flags ';
s += c.cpsrN ? " N" : " -";

View File

@ -1,40 +1,28 @@
import { BaseDebugPlatform, CpuState, EmuState, Platform, DisasmLine, Debuggable, Machine, BaseMachinePlatform } from "../common/baseplatform";
import { AnimationTimer, EmuHalt, padBytes, PLATFORMS, RasterVideo } from "../common/emu";
import { hex, lpad, loadScript } from "../common/util";
import { ARM32CPU } from "../common/cpu/ARM";
import { Platform, DisasmLine, Machine, BaseMachinePlatform } from "../common/baseplatform";
import { PLATFORMS } from "../common/emu";
import { loadScript } from "../common/util";
import { ARM32Machine } from "../machine/arm32";
declare var uc, cs : any; // Unicorn module
declare var cs : any; // Unicorn module
const ARM32_PRESETS = [
{ id: 'vidfill.vasm', name: 'Video Memory Fill' },
{ id: 'vidfill.c', 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
}
export abstract class BaseARMMachinePlatform<T extends Machine> extends BaseMachinePlatform<T> {
//getOpcodeMetadata = getOpcodeMetadata_z80;
getToolForFilename(fn: string) {
fn = fn.toLowerCase();
if (fn.endsWith('.vasm')) return "vasmarm";
else if (fn.endsWith('.armips')) return "armips";
else return "vasmarm";
if (fn.endsWith('.armips')) return "armips";
if (fn.endsWith('.c')) return "armtcc";
if (fn.endsWith('.s')) return "armtcc";
return "armtcc";
}
getPresets() { return ARM32_PRESETS; }
getDefaultExtension() { return ".vasm"; };
getDefaultExtension() { return ".c"; };
}
class ARM32Platform extends BaseARMMachinePlatform<ARM32Machine> implements Platform {
@ -53,10 +41,10 @@ class ARM32Platform extends BaseARMMachinePlatform<ARM32Machine> implements Plat
newMachine() { return new ARM32Machine(); }
readAddress(a) { return this.machine.read(a); }
getMemoryMap = function() { return { main:[
{name:'ROM',start:0x0000000,size:0x80000,type:'rom'},
{name:'RAM',start:0x2000000,size:0x80000,type:'ram'},
{name:'ROM',start:0x0000000,size:0x100000,type:'ram'},
{name:'I/O',start:0x4000000,size:0x100,type:'io'},
] } };
getDebugTree() { return this.machine.cpu.getDebugTree(); }
disassemble(pc:number, read:(addr:number)=>number) : DisasmLine {
var is_thumb = this.machine.cpu.isThumb();
var capstone = is_thumb ? this.capstone_thumb : this.capstone_arm;

Binary file not shown.

Binary file not shown.

View File

@ -334,6 +334,11 @@ export var PLATFORM_PARAMS = {
extra_link_files: ['crt0.o', 'exidy.cfg'],
//extra_compile_files: ['exidy.h'],
},
'arm32': {
arch: 'arm32',
define: ['__ARM__'],
extra_link_files: ['arm-tcc.o', 'arm-libtcc1.a'],
},
};
PLATFORM_PARAMS['sms-sms-libcv'] = PLATFORM_PARAMS['sms-sg1000-libcv'];

View File

@ -1,7 +1,7 @@
import { hex } from "../../common/util";
import { CodeListingMap, SourceLine, WorkerError, WorkerResult } from "../../common/workertypes";
import { BuildStep, BuildStepResult, gatherFiles, staleFiles, populateFiles, putWorkFile, anyTargetChanged, getPrefix, getWorkFileAsString } from "../builder";
import { BuildStep, BuildStepResult, gatherFiles, staleFiles, populateFiles, putWorkFile, anyTargetChanged, getPrefix, getWorkFileAsString, populateExtraFiles } from "../builder";
import { makeErrorMatcher, re_crlf } from "../listingutils";
import { loadNative, moduleInstFn, execMain, emglobal, EmscriptenModule } from "../wasmutils";
@ -239,3 +239,109 @@ export function assembleVASMARM(step: BuildStep): BuildStepResult {
}
}
function tccErrorMatcher(errors: WorkerError[], mainpath: string) {
return makeErrorMatcher(errors, /(\w+|tcc):(\d+|\s*error): (.+)/, 2, 3, mainpath, 1);;
}
export async function compileARMTCC(step: BuildStep): Promise<BuildStepResult> {
loadNative("arm-tcc");
const params = step.params;
const errors = [];
gatherFiles(step, { mainFilePath: "main.c" });
const objpath = step.prefix + ".o";
const error_fn = tccErrorMatcher(errors, step.path);
if (staleFiles(step, [objpath])) {
const armtcc: EmscriptenModule = await emglobal.armtcc({
instantiateWasm: moduleInstFn('arm-tcc'),
noInitialRun: true,
print: error_fn,
printErr: error_fn,
});
var args = ['-I.', '-c',
//'-std=c11',
'-funsigned-char',
'-Wwrite-strings',
'-gdwarf',
'-o', objpath];
if (params.define) {
params.define.forEach((x) => args.push('-D' + x));
}
args.push(step.path);
const FS = armtcc.FS;
populateExtraFiles(step, FS, params.extra_compile_files);
populateFiles(step, FS);
execMain(step, armtcc, args);
if (errors.length)
return { errors: errors };
var objout = FS.readFile(objpath, { encoding: 'binary' }) as Uint8Array;
putWorkFile(objpath, objout);
}
return {
linktool: "armtcclink",
files: [objpath],
args: [objpath]
}
}
export async function linkARMTCC(step: BuildStep): Promise<WorkerResult> {
loadNative("arm-tcc");
const params = step.params;
const errors = [];
gatherFiles(step, { mainFilePath: "main.c" });
const objpath = "main.bin";
const error_fn = tccErrorMatcher(errors, step.path);
if (staleFiles(step, [objpath])) {
const armtcc: EmscriptenModule = await emglobal.armtcc({
instantiateWasm: moduleInstFn('arm-tcc'),
noInitialRun: true,
print: error_fn,
printErr: error_fn,
});
var args = ['-L.', '-nostdlib', '-nostdinc',
'-Wl,--oformat=binary',
//'-Wl,-section-alignment=0x100000',
'-gdwarf',
'-o', objpath];
if (params.define) {
params.define.forEach((x) => args.push('-D' + x));
}
let objfiles = step.files;
// sort so that crtxxx files are first
objfiles.sort((a, b) => {
let a0 = a.startsWith('crt') ? 0 : 1;
let b0 = b.startsWith('crt') ? 0 : 1;
a = a0 + a;
b = b0 + b;
return a.localeCompare(b);
});
args = args.concat(objfiles);
args.push('arm-libtcc1.a');
const FS = armtcc.FS;
populateExtraFiles(step, FS, params.extra_link_files);
populateFiles(step, FS);
execMain(step, armtcc, args);
if (errors.length)
return { errors: errors };
var objout = FS.readFile(objpath, { encoding: 'binary' }) as Uint8Array;
putWorkFile(objpath, objout);
if (!anyTargetChanged(step, [objpath]))
return;
return {
output: objout, //.slice(0x34),
//listings: listings,
errors: errors,
//symbolmap: symbolmap,
//segments: segments
};
}
}

File diff suppressed because one or more lines are too long

BIN
src/worker/wasm/arm-tcc.wasm Executable file

Binary file not shown.

View File

@ -52,6 +52,8 @@ export const TOOLS = {
'ecs': ecs.assembleECS,
'remote': remote.buildRemote,
'cc7800': cc7800.compileCC7800,
'armtcc': arm.compileARMTCC,
'armtcclink': arm.linkARMTCC,
}
export const TOOL_PRELOADFS = {

BIN
test/exes/arm32.elf Executable file

Binary file not shown.