mirror of
https://github.com/sehugg/8bitworkshop.git
synced 2025-04-28 20:38:09 +00:00
arm: arm-tcc
This commit is contained in:
parent
8023d56b88
commit
c0909bef1b
@ -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
9
presets/arm32/crt0.c
Normal 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
27
presets/arm32/vidfill.c
Normal 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;
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
};
|
||||
}
|
||||
}
|
||||
|
@ -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
159
src/common/elfparser.ts
Normal 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;
|
||||
}
|
@ -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);
|
||||
|
@ -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) {
|
||||
|
@ -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
|
||||
|
@ -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" : " -";
|
||||
|
@ -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;
|
||||
|
BIN
src/worker/lib/arm32/arm-libtcc1.a
Normal file
BIN
src/worker/lib/arm32/arm-libtcc1.a
Normal file
Binary file not shown.
BIN
src/worker/lib/arm32/arm-tcc.o
Normal file
BIN
src/worker/lib/arm32/arm-tcc.o
Normal file
Binary file not shown.
@ -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'];
|
||||
|
@ -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
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
|
21
src/worker/wasm/arm-tcc.js
Normal file
21
src/worker/wasm/arm-tcc.js
Normal file
File diff suppressed because one or more lines are too long
BIN
src/worker/wasm/arm-tcc.wasm
Executable file
BIN
src/worker/wasm/arm-tcc.wasm
Executable file
Binary file not shown.
@ -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
BIN
test/exes/arm32.elf
Executable file
Binary file not shown.
Loading…
x
Reference in New Issue
Block a user