Compare commits
14 Commits
8023d56b88
...
d733a2cd27
Author | SHA1 | Date |
---|---|---|
Steven Hugg | d733a2cd27 | |
Steven Hugg | 098dcda93a | |
Steven Hugg | 886e19611e | |
Steven Hugg | 02986aed38 | |
Steven Hugg | d5b5734ef9 | |
Steven Hugg | 579e58e966 | |
Steven Hugg | 47a7aa5a83 | |
Steven Hugg | 9142328468 | |
Steven Hugg | 70eee2bdae | |
Steven Hugg | 949e216c69 | |
Steven Hugg | b160fb2ef2 | |
Steven Hugg | 63ee25741b | |
Steven Hugg | 8bdbae36e3 | |
Steven Hugg | c0909bef1b |
19
README.md
19
README.md
|
@ -45,20 +45,22 @@ Note: Github tests may fail due to lack of API key.
|
|||
|
||||
## License
|
||||
|
||||
Copyright © 2016-2022 [Steven Hugg](https://github.com/sehugg).
|
||||
Copyright © 2016-2024 [Steven E. Hugg](https://github.com/sehugg).
|
||||
|
||||
This project is [GPL-3.0](https://github.com/sehugg/8bitworkshop/blob/master/LICENSE) licensed.
|
||||
|
||||
Dependencies retain their original licenses.
|
||||
This project, unless specifically noted, is multi-licensed.
|
||||
You may choose to adhere to the terms of either the [GPL-3.0](https://github.com/sehugg/8bitworkshop/blob/master/LICENSE) License for the entire project or respect the individual licenses of its dependencies and included code samples, as applicable.
|
||||
|
||||
All included code samples (all files under the presets/ directory) are licensed under
|
||||
This project includes various dependencies, modules, and components that retain their original licenses.
|
||||
For detailed licensing information for each dependency, please refer to the respective files and documentation.
|
||||
|
||||
All included code samples located in the presets/ directory are licensed under
|
||||
[CC0](https://creativecommons.org/publicdomain/zero/1.0/)
|
||||
unless otherwise licensed.
|
||||
unless a different license is explicitly stated within the specific code sample.
|
||||
|
||||
|
||||
## Dependencies
|
||||
|
||||
The IDE uses custom forks for many of these, found at https://github.com/sehugg?tab=repositories
|
||||
|
||||
### Emulators
|
||||
|
||||
* https://javatari.org/
|
||||
|
@ -84,6 +86,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
|
||||
|
||||
|
@ -112,6 +115,8 @@ The IDE uses custom forks for many of these, found at https://github.com/sehugg?
|
|||
* https://github.com/sehugg/8bitworkshop-compilers
|
||||
* https://github.com/sehugg/8bit-tools
|
||||
* https://github.com/sehugg/awesome-8bitgamedev
|
||||
* https://github.com/sehugg?tab=repositories
|
||||
|
||||
|
||||
## Tool Server (experimental)
|
||||
|
||||
|
|
11
css/ui.css
11
css/ui.css
|
@ -127,13 +127,15 @@ div.mem_info {
|
|||
bottom: 10px;
|
||||
background-color: #333;
|
||||
color: #66ff66;
|
||||
white-space: pre;
|
||||
padding: 20px;
|
||||
z-index: 12;
|
||||
font-family: "Andale Mono", "Menlo", "Lucida Console", monospace;
|
||||
font-size: 12pt;
|
||||
box-shadow: 0px 0px 8px rgba(0,0,0,.5);
|
||||
max-height: 90vh;
|
||||
}
|
||||
div.mem_info_msg {
|
||||
white-space: pre;
|
||||
padding: 20px;
|
||||
max-height: 80vh;
|
||||
overflow-y: auto;
|
||||
}
|
||||
div.mem_info a {
|
||||
|
@ -148,6 +150,9 @@ div.mem_info a:hover {
|
|||
div.mem_info a.selected {
|
||||
color: #ffffff;
|
||||
}
|
||||
div.mem_info button {
|
||||
color: #fff;
|
||||
}
|
||||
.mem_info_links {
|
||||
text-align:right;
|
||||
}
|
||||
|
|
|
@ -378,6 +378,8 @@ body {
|
|||
</div>
|
||||
</div>
|
||||
<div id="mem_info" class="mem_info" style="display:none">
|
||||
<div><button type="button" class="close" onclick="$('.mem_info').hide()" aria-hidden="true">×</button></div>
|
||||
<div id="mem_info_msg" class="mem_info_msg"></div>
|
||||
</div>
|
||||
<div id="error_alert" class="alert alert-danger alert-dismissable" style="position:absolute;right:0;top:0;display:none">
|
||||
<button type="button" class="close" onclick="$('.alert').hide()" aria-hidden="true">×</button>
|
||||
|
|
|
@ -0,0 +1,7 @@
|
|||
|
||||
#define SERIAL_OUT ((int*)0x4000048)
|
||||
|
||||
void putchar_(char c) {
|
||||
*SERIAL_OUT = c;
|
||||
}
|
||||
|
|
@ -0,0 +1,35 @@
|
|||
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#define true 1
|
||||
#define false 0
|
||||
#define size 8190
|
||||
#define sizepl 8191
|
||||
|
||||
//#link "serialout.c"
|
||||
|
||||
main() {
|
||||
char flags[sizepl];
|
||||
int i, prime, k, count, iter;
|
||||
printf("Running benchmark...\n");
|
||||
for (iter = 1; iter <= 10; iter ++) {
|
||||
count=0;
|
||||
for (i = 0; i <= size; i++)
|
||||
flags[i] = true;
|
||||
for (i = 0; i <= size; i++) {
|
||||
if (flags[i]) {
|
||||
prime = i + i + 3;
|
||||
k = i + prime;
|
||||
while (k <= size) {
|
||||
flags[k] = false;
|
||||
k += prime;
|
||||
}
|
||||
count = count + 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
printf("Primes: %d\n", count);
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
|
||||
//#link "serialout.c"
|
||||
|
||||
int main() {
|
||||
int x = 2024;
|
||||
printf("Hello World! %d\n", x);
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,25 @@
|
|||
|
||||
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;
|
||||
}
|
||||
|
||||
|
|
@ -873,15 +873,19 @@ export abstract class BaseMachinePlatform<T extends Machine> extends BaseDebugPl
|
|||
|
||||
advance(novideo:boolean) {
|
||||
let trap = this.getDebugCallback();
|
||||
var steps = this.machine.advanceFrame(trap);
|
||||
if (!novideo && this.video) {
|
||||
this.video.updateFrame();
|
||||
this.updateVideoDebugger();
|
||||
try {
|
||||
var steps = this.machine.advanceFrame(trap);
|
||||
return steps;
|
||||
} finally {
|
||||
// in case EmuHalt is thrown...
|
||||
if (!novideo && this.video) {
|
||||
this.video.updateFrame();
|
||||
this.updateVideoDebugger();
|
||||
}
|
||||
if (!novideo && this.serialVisualizer) {
|
||||
this.serialVisualizer.refresh();
|
||||
}
|
||||
}
|
||||
if (!novideo && this.serialVisualizer) {
|
||||
this.serialVisualizer.refresh();
|
||||
}
|
||||
return steps;
|
||||
}
|
||||
|
||||
updateVideoDebugger() {
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -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,18 @@ export interface ARMCoreState {
|
|||
bankedRegisters: number[][],
|
||||
spsr: number,
|
||||
bankedSPSRs: number[],
|
||||
sfprs: number[],
|
||||
dfprs: number[],
|
||||
ifprs: number[],
|
||||
cycles: number,
|
||||
instructionWidth: 2 | 4
|
||||
}
|
||||
|
||||
interface ARMCoreType {
|
||||
gprs: Int32Array;
|
||||
sfprs: Float32Array;
|
||||
dfprs: Float64Array;
|
||||
ifprs: Int32Array;
|
||||
PC: number;
|
||||
SP: number;
|
||||
LR: number;
|
||||
|
@ -659,6 +665,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;
|
||||
|
@ -1760,6 +1771,265 @@ ARMCoreArm.prototype.constructUMULLS = function(rd, rn, rs, rm, condOp) {
|
|||
};
|
||||
};
|
||||
|
||||
ARMCoreArm.prototype.constructVFP3Register = function(condOp, opcode, nOperandReg, destReg, sz, opcode2, mOperandReg) {
|
||||
var cpu : ARMCoreType = this.cpu;
|
||||
var fpregs = sz ? cpu.dfprs : cpu.sfprs;
|
||||
//console.log("VFP3Register: " + hex(opcode) + " " + hex(nOperandReg) + " " + hex(destReg) + " " + hex(number) + " " + hex(opcode2) + " " + hex(mOperandReg));
|
||||
return function() {
|
||||
cpu.mmu.waitPrefetch32(cpu.gprs[ARMRegs.PC]);
|
||||
if (condOp && !condOp()) {
|
||||
return;
|
||||
}
|
||||
switch (opcode) {
|
||||
case 2: // VMUL
|
||||
switch (opcode2) {
|
||||
case 0:
|
||||
fpregs[destReg] = fpregs[nOperandReg] * fpregs[mOperandReg];
|
||||
return;
|
||||
}
|
||||
break;
|
||||
case 3: // VADD/VSUB
|
||||
switch (opcode2) {
|
||||
case 0:
|
||||
fpregs[destReg] = fpregs[nOperandReg] + fpregs[mOperandReg];
|
||||
return;
|
||||
case 2:
|
||||
fpregs[destReg] = fpregs[nOperandReg] - fpregs[mOperandReg];
|
||||
return;
|
||||
}
|
||||
break;
|
||||
case 8: // VDIV
|
||||
switch (opcode2) {
|
||||
case 0:
|
||||
fpregs[destReg] = fpregs[nOperandReg] / fpregs[mOperandReg];
|
||||
return;
|
||||
}
|
||||
break;
|
||||
}
|
||||
console.log("Unsupported instruction: " + hex(opcode) + " " + hex(opcode2));
|
||||
};
|
||||
};
|
||||
|
||||
/*
|
||||
if ConditionPassed() then
|
||||
EncodingSpecificOperations(); CheckVFPEnabled(TRUE);
|
||||
if to_integer then
|
||||
if dp_operation then
|
||||
S[d] = FPToFixed(D[m], 32, 0, unsigned, round_zero, TRUE);
|
||||
else
|
||||
S[d] = FPToFixed(S[m], 32, 0, unsigned, round_zero, TRUE);
|
||||
else
|
||||
if dp_operation then
|
||||
D[d] = FixedToFP(S[m], 64, 0, unsigned, round_nearest, TRUE);
|
||||
else
|
||||
S[d] = FixedToFP(S[m], 32, 0, unsigned, round_nearest, TRUE);
|
||||
*/
|
||||
ARMCoreArm.prototype.constructVCVT = function(condOp, d, m, to_integer, dp_operation, unsigned, round_zero, round_nearest) {
|
||||
var cpu : ARMCoreType = this.cpu;
|
||||
var sregs = cpu.sfprs;
|
||||
var dregs = cpu.dfprs;
|
||||
var iregs = cpu.ifprs;
|
||||
return function() {
|
||||
cpu.mmu.waitPrefetch32(cpu.gprs[ARMRegs.PC]);
|
||||
if (condOp && !condOp()) {
|
||||
return;
|
||||
}
|
||||
var src : number;
|
||||
var dest : number;
|
||||
// get source
|
||||
if (to_integer && dp_operation) {
|
||||
src = dregs[m];
|
||||
} else if (to_integer) {
|
||||
src = sregs[m];
|
||||
} else {
|
||||
src = iregs[m];
|
||||
}
|
||||
// convert
|
||||
if (to_integer) {
|
||||
dest = round_zero ? Math.floor(src) : Math.round(src);
|
||||
} else {
|
||||
dest = src;
|
||||
}
|
||||
// store result
|
||||
if (to_integer) {
|
||||
iregs[d] = dest;
|
||||
} else if (dp_operation) {
|
||||
dregs[d] = dest;
|
||||
} else {
|
||||
sregs[d] = dest;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
ARMCoreArm.prototype.constructVCVTF = function(condOp, d, m, double_to_single) {
|
||||
var cpu : ARMCoreType = this.cpu;
|
||||
var sregs = cpu.sfprs;
|
||||
var dregs = cpu.dfprs;
|
||||
return function() {
|
||||
cpu.mmu.waitPrefetch32(cpu.gprs[ARMRegs.PC]);
|
||||
if (condOp && !condOp()) {
|
||||
return;
|
||||
}
|
||||
var n = double_to_single ? dregs[m] : sregs[m];
|
||||
// store result
|
||||
if (double_to_single) {
|
||||
sregs[d] = n;
|
||||
} else {
|
||||
dregs[d] = n;
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
ARMCoreArm.prototype.constructVLDR = function(condOp, destReg, address, single_reg) {
|
||||
var cpu : ARMCoreType = this.cpu;
|
||||
var iregs = cpu.ifprs;
|
||||
return function() {
|
||||
cpu.mmu.waitPrefetch32(cpu.gprs[ARMRegs.PC]);
|
||||
if (condOp && !condOp()) {
|
||||
return;
|
||||
}
|
||||
let addr = address();
|
||||
if (single_reg) {
|
||||
iregs[destReg] = cpu.mmu.load32(addr);
|
||||
} else {
|
||||
iregs[destReg] = cpu.mmu.load32(addr);
|
||||
iregs[destReg+1] = cpu.mmu.load32(addr+4);
|
||||
}
|
||||
cpu.mmu.wait32(addr);
|
||||
cpu.mmu.wait32(cpu.gprs[ARMRegs.PC]);
|
||||
};
|
||||
};
|
||||
|
||||
ARMCoreArm.prototype.constructVSTR = function(condOp, srcReg, address, single_reg) {
|
||||
var cpu : ARMCoreType = this.cpu;
|
||||
var iregs = cpu.ifprs;
|
||||
return function() {
|
||||
cpu.mmu.waitPrefetch32(cpu.gprs[ARMRegs.PC]);
|
||||
if (condOp && !condOp()) {
|
||||
return;
|
||||
}
|
||||
let addr = address();
|
||||
if (single_reg) {
|
||||
cpu.mmu.store32(addr, iregs[srcReg]);
|
||||
} else {
|
||||
cpu.mmu.store32(addr, iregs[srcReg]);
|
||||
cpu.mmu.store32(addr+4, iregs[srcReg+1]);
|
||||
}
|
||||
cpu.mmu.wait32(addr);
|
||||
cpu.mmu.wait32(cpu.gprs[ARMRegs.PC]);
|
||||
};
|
||||
}
|
||||
|
||||
ARMCoreArm.prototype.constructVPUSH = function(condOp, d, regs, single_regs) {
|
||||
var cpu : ARMCoreType = this.cpu;
|
||||
var iregs = cpu.ifprs;
|
||||
return function() {
|
||||
cpu.mmu.waitPrefetch32(cpu.gprs[ARMRegs.PC]);
|
||||
if (condOp && !condOp()) {
|
||||
return;
|
||||
}
|
||||
let addr = cpu.gprs[ARMRegs.SP] - regs * 4;
|
||||
cpu.gprs[ARMRegs.SP] = addr;
|
||||
for (let i = 0; i < regs; ++i) {
|
||||
cpu.mmu.store32(addr, iregs[d+i]);
|
||||
addr += 4;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
ARMCoreArm.prototype.constructVPOP = function(condOp, d, regs, single_regs) {
|
||||
var cpu : ARMCoreType = this.cpu;
|
||||
var iregs = cpu.ifprs;
|
||||
return function() {
|
||||
cpu.mmu.waitPrefetch32(cpu.gprs[ARMRegs.PC]);
|
||||
if (condOp && !condOp()) {
|
||||
return;
|
||||
}
|
||||
let addr = cpu.gprs[ARMRegs.SP];
|
||||
cpu.gprs[ARMRegs.SP] += regs * 4;
|
||||
for (let i = 0; i < regs; ++i) {
|
||||
iregs[d+i] = cpu.mmu.load32(addr);
|
||||
addr += 4;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
function FPCompare(op1: number, op2: number) {
|
||||
/* assert N IN {32,64};
|
||||
fpscr_val = if fpscr_controlled then FPSCR else StandardFPSCRValue();
|
||||
(type1,sign1,value1) = FPUnpack(op1, fpscr_val);
|
||||
(type2,sign2,value2) = FPUnpack(op2, fpscr_val); */
|
||||
if (isNaN(op1) || isNaN(op2)) {
|
||||
return 0b0011;
|
||||
}
|
||||
if (op1 == op2) return 0b0110;
|
||||
if (op1 < op2) return 0b1000;
|
||||
else return 0b0010;
|
||||
}
|
||||
|
||||
ARMCoreArm.prototype.constructVCMP = function(condOp, d, Vd, sz, E, m, Vm) {
|
||||
var cpu : ARMCoreType = this.cpu;
|
||||
var sregs = cpu.sfprs;
|
||||
var dregs = cpu.dfprs;
|
||||
return function() {
|
||||
cpu.mmu.waitPrefetch32(cpu.gprs[ARMRegs.PC]);
|
||||
if (condOp && !condOp()) {
|
||||
return;
|
||||
}
|
||||
let op1, op2;
|
||||
if (sz) {
|
||||
op1 = dregs[d];
|
||||
op2 = dregs[m];
|
||||
} else {
|
||||
op1 = sregs[d];
|
||||
op2 = sregs[m];
|
||||
}
|
||||
let result = FPCompare(op1, op2);
|
||||
cpu.cpsrN = (result & 8) != 0;
|
||||
cpu.cpsrZ = (result & 4) != 0;
|
||||
cpu.cpsrC = (result & 2) != 0;
|
||||
cpu.cpsrV = (result & 1) != 0;
|
||||
}
|
||||
}
|
||||
|
||||
ARMCoreArm.prototype.constructVCMP0 = function(condOp, d, Vd, sz, E) {
|
||||
var cpu : ARMCoreType = this.cpu;
|
||||
var sregs = cpu.sfprs;
|
||||
var dregs = cpu.dfprs;
|
||||
return function() {
|
||||
cpu.mmu.waitPrefetch32(cpu.gprs[ARMRegs.PC]);
|
||||
if (condOp && !condOp()) {
|
||||
return;
|
||||
}
|
||||
let op1, op2=0;
|
||||
if (sz) {
|
||||
op1 = dregs[d];
|
||||
} else {
|
||||
op1 = sregs[d];
|
||||
}
|
||||
let result = FPCompare(op1, op2);
|
||||
cpu.cpsrN = (result & 8) != 0;
|
||||
cpu.cpsrZ = (result & 4) != 0;
|
||||
cpu.cpsrC = (result & 2) != 0;
|
||||
cpu.cpsrV = (result & 1) != 0;
|
||||
}
|
||||
}
|
||||
ARMCoreArm.prototype.constructVMOV = function(condOp, to_arm_reg, n, t) {
|
||||
var cpu : ARMCoreType = this.cpu;
|
||||
return function() {
|
||||
cpu.mmu.waitPrefetch32(cpu.gprs[ARMRegs.PC]);
|
||||
if (condOp && !condOp()) {
|
||||
return;
|
||||
}
|
||||
if (to_arm_reg) {
|
||||
cpu.gprs[t] = cpu.ifprs[n];
|
||||
} else {
|
||||
cpu.ifprs[n] = cpu.gprs[t];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
|
||||
function ARMCoreThumb(cpu) {
|
||||
|
@ -2658,6 +2928,9 @@ 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
|
||||
this.ifprs = new Int32Array(this.dfprs.buffer); // regs shared with dfprs
|
||||
};
|
||||
|
||||
ARMCore.prototype.resetCPU = function(startOffset) {
|
||||
|
@ -2665,6 +2938,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 +3043,9 @@ ARMCore.prototype.freeze = function() : ARMCoreState {
|
|||
this.gprs[14],
|
||||
this.gprs[15],
|
||||
],
|
||||
'sfprs': this.sfprs.slice(),
|
||||
'dfprs': this.dfprs.slice(),
|
||||
'ifprs': this.ifprs.slice(),
|
||||
'mode': this.mode,
|
||||
'cpsrI': this.cpsrI,
|
||||
'cpsrF': this.cpsrF,
|
||||
|
@ -2850,6 +3127,8 @@ ARMCore.prototype.defrost = function(frost: ARMCoreState) {
|
|||
this.gprs[14] = frost.gprs[14];
|
||||
this.gprs[15] = frost.gprs[15];
|
||||
|
||||
this.ifprs.set(frost.ifprs); // regs shared with sfprs
|
||||
|
||||
this.mode = frost.mode;
|
||||
this.cpsrI = frost.cpsrI;
|
||||
this.cpsrF = frost.cpsrF;
|
||||
|
@ -3538,6 +3817,11 @@ ARMCore.prototype.compileArm = function(instruction) {
|
|||
var load = instruction & 0x00100000;
|
||||
var b = instruction & 0x00400000;
|
||||
var i = instruction & 0x02000000;
|
||||
// test for UDF instruction
|
||||
if ((instruction & 0xfff000f0) == (0xe7f000f0|0)) {
|
||||
var immediate = instruction & 0x0000000f; // TODO: full range
|
||||
throw new EmuHalt("Program exited (" + immediate + ")");
|
||||
}
|
||||
|
||||
var address : AddressFunction = function() {
|
||||
throw new EmuHalt("Unimplemented memory access: 0x" + instruction.toString(16));
|
||||
|
@ -3667,6 +3951,68 @@ ARMCore.prototype.compileArm = function(instruction) {
|
|||
break;
|
||||
case 0x0C000000:
|
||||
// Coprocessor data transfer
|
||||
var load = instruction & 0x00100000;
|
||||
var w = instruction & 0x00200000;
|
||||
var user = instruction & 0x00400000;
|
||||
var u = instruction & 0x00800000;
|
||||
var p = instruction & 0x01000000;
|
||||
var rn = (instruction & 0x000F0000) >> 16;
|
||||
var crd = (instruction & 0x0000F000) >> 12;
|
||||
var cpnum = (instruction & 0x00000F00) >> 8;
|
||||
var immediate = instruction & 0x000000FF;
|
||||
var cond = (instruction >> 28) & 0xf;
|
||||
var condOp = this.conds[cond];
|
||||
|
||||
// VPUSH, VPOP
|
||||
if ((instruction & 0x0fbf0f00) == 0x0d2d0a00) {
|
||||
op = this.armCompiler.constructVPUSH(condOp, (crd<<1)|(user?1:0), immediate, true);
|
||||
}
|
||||
else if ((instruction & 0x0fbf0f00) == 0x0d2d0b00) {
|
||||
op = this.armCompiler.constructVPUSH(condOp, ((user?16:0)|crd)*2, immediate, false);
|
||||
}
|
||||
else if ((instruction & 0x0fbf0f00) == 0x0cbd0a00) {
|
||||
op = this.armCompiler.constructVPOP(condOp, (crd<<1)|(user?1:0), immediate, true);
|
||||
}
|
||||
else if ((instruction & 0x0fbf0f00) == 0x0cbd0b00) {
|
||||
op = this.armCompiler.constructVPOP(condOp, ((user?16:0)|crd)*2, immediate, false);
|
||||
}
|
||||
// VLDR, VSTR
|
||||
// https://developer.arm.com/documentation/ddi0406/c/Application-Level-Architecture/Instruction-Details/Alphabetical-list-of-instructions/VSTR?lang=en
|
||||
else if ((instruction & 0x0f200f00) == 0x0d000a00) {
|
||||
immediate *= 4;
|
||||
if (!u) immediate = -immediate;
|
||||
var overlap = false;
|
||||
var d = (crd<<1)|(user?1:0);
|
||||
|
||||
var address : AddressFunction;
|
||||
if (w) {
|
||||
address = this.armCompiler.constructAddressingMode4Writeback(immediate, offset, rn, overlap);
|
||||
} else {
|
||||
address = this.armCompiler.constructAddressingMode4(immediate, rn);
|
||||
}
|
||||
if (load) {
|
||||
op = this.armCompiler.constructVLDR(condOp, d, address, true);
|
||||
} else {
|
||||
op = this.armCompiler.constructVSTR(condOp, d, address, true);
|
||||
}
|
||||
} else if ((instruction & 0x0f200f00) == 0x0d000b00) {
|
||||
immediate *= 4;
|
||||
if (!u) immediate = -immediate;
|
||||
var overlap = false;
|
||||
var d = ((user?16:0)|crd)*2;
|
||||
|
||||
var address : AddressFunction;
|
||||
if (w) {
|
||||
address = this.armCompiler.constructAddressingMode4Writeback(immediate, offset, rn, overlap);
|
||||
} else {
|
||||
address = this.armCompiler.constructAddressingMode4(immediate, rn);
|
||||
}
|
||||
if (load) {
|
||||
op = this.armCompiler.constructVLDR(condOp, d, address, false);
|
||||
} else {
|
||||
op = this.armCompiler.constructVSTR(condOp, d, address, false);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 0x0E000000:
|
||||
// Coprocessor data operation/SWI
|
||||
|
@ -3676,6 +4022,148 @@ ARMCore.prototype.compileArm = function(instruction) {
|
|||
op = this.armCompiler.constructSWI(immediate, condOp);
|
||||
op.writesPC = false;
|
||||
}
|
||||
// VCVT, VCVTR, VCVT
|
||||
// https://developer.arm.com/documentation/ddi0406/c/Application-Level-Architecture/Instruction-Details/Alphabetical-list-of-instructions/VCVT--VCVTR--between-floating-point-and-integer--Floating-point-
|
||||
/*
|
||||
if opc2 != '000' && !(opc2 IN "10x") then SEE "Related encodings";
|
||||
to_integer = (opc2<2> == '1'); dp_operation = (sz == 1);
|
||||
if to_integer then
|
||||
unsigned = (opc2<0> == '0'); round_zero = (op == '1');
|
||||
d = UInt(Vd:D); m = if dp_operation then UInt(M:Vm) else UInt(Vm:M);
|
||||
else
|
||||
unsigned = (op == '0'); round_nearest = FALSE; // FALSE selects FPSCR rounding
|
||||
m = UInt(Vm:M); d = if dp_operation then UInt(D:Vd) else UInt(Vd:D);
|
||||
*/
|
||||
else if ((instruction & 0x0FB80E50) == 0x0EB80A40) {
|
||||
const cond = (instruction >> 28) & 0xf;
|
||||
const D = (instruction >> 22) & 0x1;
|
||||
const opc2 = (instruction >> 16) & 0x7;
|
||||
const Vd = (instruction >> 12) & 0xf;
|
||||
const sz = (instruction >> 8) & 0x1;
|
||||
const op0 = (instruction >> 7) & 0x1;
|
||||
const M = (instruction >> 5) & 0x1;
|
||||
const Vm = instruction & 0xf;
|
||||
const to_integer = opc2 & 0x4;
|
||||
const dp_operation = sz != 0;
|
||||
const unsigned = to_integer ? opc2 & 0x1 : 0;
|
||||
const round_zero = op0 != 0;
|
||||
const round_nearest = false;
|
||||
const d = sz ? (D?16:0)|Vd : (Vd<<1)|(D?1:0);
|
||||
const m = sz ? (M?16:0)|Vm : (Vm<<1)|(M?1:0);
|
||||
//console.log("VCVT", d, m, opc2, to_integer, dp_operation, unsigned, round_zero, round_nearest);
|
||||
op = this.armCompiler.constructVCVT(condOp, d, m, to_integer, dp_operation, unsigned, round_zero, round_nearest);
|
||||
op.writesPC = false;
|
||||
}
|
||||
// VCVT f64/f32
|
||||
else if ((instruction & 0x0FBF0ED0) == 0x0EB70AC0) {
|
||||
const cond = (instruction >> 28) & 0xf;
|
||||
const D = (instruction >> 22) & 0x1;
|
||||
const Vd = (instruction >> 12) & 0xf;
|
||||
const sz = (instruction >> 8) & 0x1;
|
||||
const M = (instruction >> 5) & 0x1;
|
||||
const Vm = instruction & 0xf;
|
||||
const double_to_single = sz != 0;
|
||||
const d = sz ? (D?16:0)|Vd : (Vd<<1)|(D?1:0);
|
||||
const m = sz ? (M?16:0)|Vm : (Vm<<1)|(M?1:0);
|
||||
op = this.armCompiler.constructVCVTF(condOp, d, m, double_to_single);
|
||||
op.writesPC = false;
|
||||
}
|
||||
// 3-op floating point vector instructions (VADD, etc)
|
||||
else if ((instruction & 0x0FA00E10) == 0x0E200A00) {
|
||||
const cond = (instruction >> 28) & 0xf;
|
||||
const D = (instruction >> 22) & 0x1;
|
||||
const N = (instruction >> 7) & 0x1;
|
||||
const M = (instruction >> 5) & 0x1;
|
||||
const opcode = (instruction & 0x0F00000) >> 20;
|
||||
const Vn = (instruction & 0x000F0000) >> 16;
|
||||
const Vd = (instruction & 0x0000F000) >> 12;
|
||||
const opcode2 = (instruction & 0b11100000) >> 5;
|
||||
const Vm = instruction & 0x0000000F;
|
||||
const sz = (instruction >> 8) & 0x1;
|
||||
const d = sz ? (D?16:0)|Vd : (Vd<<1)|(D?1:0);
|
||||
const m = sz ? (M?16:0)|Vm : (Vm<<1)|(M?1:0);
|
||||
const n = sz ? (N?16:0)|Vn : (Vn<<1)|(N?1:0);
|
||||
|
||||
var condOp = this.conds[cond];
|
||||
op = this.armCompiler.constructVFP3Register(condOp, opcode, n, d, sz, opcode2, m);
|
||||
op.writesPC = false;
|
||||
}
|
||||
// VDIV - https://developer.arm.com/documentation/ddi0597/2023-12/SIMD-FP-Instructions/VDIV--Divide-?lang=en
|
||||
else if ((instruction & 0x0FB00C50) == 0x0E800800) {
|
||||
const cond = (instruction >> 28) & 0xf;
|
||||
const D = (instruction >> 22) & 0x1;
|
||||
const Vn = (instruction >> 16) & 0xf;
|
||||
const Vd = (instruction >> 12) & 0xf;
|
||||
const size = (instruction >> 8) & 0x3;
|
||||
const N = (instruction >> 7) & 0x1;
|
||||
const M = (instruction >> 5) & 0x1;
|
||||
const Vm = instruction & 0xf;
|
||||
/*
|
||||
case size of
|
||||
when '01' esize = 16; d = UInt(Vd:D); n = UInt(Vn:N); m = UInt(Vm:M);
|
||||
when '10' esize = 32; d = UInt(Vd:D); n = UInt(Vn:N); m = UInt(Vm:M);
|
||||
when '11' esize = 64; d = UInt(D:Vd); n = UInt(N:Vn); m = UInt(M:Vm);
|
||||
*/
|
||||
const d = size==3 ? (D?16:0)|Vd : (Vd<<1)|(D?1:0);
|
||||
const m = size==3 ? (M?16:0)|Vm : (Vm<<1)|(M?1:0);
|
||||
const n = size==3 ? (N?16:0)|Vn : (Vn<<1)|(N?1:0);
|
||||
op = this.armCompiler.constructVFP3Register(condOp, 8, n, d, size==3, 0, m);
|
||||
op.writesPC = false;
|
||||
}
|
||||
// 2-op floating point vector instructions (VCMP, etc)
|
||||
else if ((instruction & 0x0FBF0E50) == 0x0EB40A40) {
|
||||
const cond = (instruction >> 28) & 0xf;
|
||||
const D = (instruction >> 22) & 0x1;
|
||||
const Vd = (instruction >> 12) & 0xf;
|
||||
const sz = (instruction >> 8) & 0x1;
|
||||
const E = (instruction >> 7) & 0x1;
|
||||
const M = (instruction >> 5) & 0x1;
|
||||
const Vm = instruction & 0x0000000F;
|
||||
const d = sz ? (D?16:0)|Vd : (Vd<<1)|(D?1:0);
|
||||
const m = sz ? (M?16:0)|Vm : (Vm<<1)|(M?1:0);
|
||||
|
||||
var condOp = this.conds[cond];
|
||||
op = this.armCompiler.constructVCMP(condOp, d, Vd, sz, E, m, Vm);
|
||||
op.writesPC = false;
|
||||
}
|
||||
// VCMP #0
|
||||
else if ((instruction & 0x0FBF0EFF) == 0x0EB50A40) {
|
||||
const cond = (instruction >> 28) & 0xf;
|
||||
const D = (instruction >> 22) & 0x1;
|
||||
const Vd = (instruction >> 12) & 0xf;
|
||||
const sz = (instruction >> 8) & 0x1;
|
||||
const E = (instruction >> 7) & 0x1;
|
||||
const M = (instruction >> 5) & 0x1;
|
||||
const Vm = instruction & 0x0000000F;
|
||||
const d = sz ? (D?16:0)|Vd : (Vd<<1)|(D?1:0);
|
||||
const m = sz ? (M?16:0)|Vm : (Vm<<1)|(M?1:0);
|
||||
|
||||
var condOp = this.conds[cond];
|
||||
op = this.armCompiler.constructVCMP0(condOp, d, Vd, sz, E, m, Vm);
|
||||
op.writesPC = false;
|
||||
}
|
||||
// vmrs apsr_nzcv, fpscr (ignore, we always call this after CMP)
|
||||
else if (instruction == 0xeef1fa10) {
|
||||
op = this.armCompiler.constructNOP();
|
||||
}
|
||||
// VMOV - https://developer.arm.com/documentation/ddi0406/c/Application-Level-Architecture/Instruction-Details/Alphabetical-list-of-instructions/VMOV--between-ARM-core-register-and-single-precision-register-
|
||||
else if ((instruction & 0x0FE00F10) == 0x0E000A10) {
|
||||
const cond = (instruction >> 28) & 0xf;
|
||||
const opc1 = (instruction >> 20) & 0x1;
|
||||
const Vn = (instruction >> 16) & 0xf;
|
||||
const Rt = (instruction >> 12) & 0xf;
|
||||
const N = (instruction >> 7) & 0x1;
|
||||
var condOp = this.conds[cond];
|
||||
//console.log("VMOV", instruction.toString(16), opc1, Vn, Rt, N);
|
||||
op = this.armCompiler.constructVMOV(condOp, opc1, (Vn<<1)|(N?1:0), Rt);
|
||||
}
|
||||
// vmov.32 dn[i], rn
|
||||
else if (instruction == 0xee000b10) {
|
||||
op = this.armCompiler.constructVMOV(condOp, false, 0, 0);
|
||||
}
|
||||
else if (instruction == 0xee201b10) {
|
||||
op = this.armCompiler.constructVMOV(condOp, false, 1, 1);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
throw new EmuHalt('Bad opcode: 0x' + instruction.toString(16));
|
||||
|
@ -4103,11 +4591,16 @@ 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[];
|
||||
f64arr = new Float64Array(1);
|
||||
f32arr = new Float32Array(this.f64arr.buffer);
|
||||
i32arr = new Int32Array(this.f64arr.buffer);
|
||||
|
||||
BASE_OFFSET = 24;
|
||||
OFFSET_MASK = 0x00FFFFFF;
|
||||
|
@ -4122,8 +4615,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 +4638,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 +4666,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 +4678,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 +4743,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 {
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -1,3 +1,24 @@
|
|||
/*
|
||||
* Copyright (c) 2024 Steven E. Hugg
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
// https://dev.to/ndesmic/building-a-minimal-wasi-polyfill-for-browsers-4nel
|
||||
// http://www.wasmtutor.com/webassembly-barebones-wasi
|
||||
|
@ -154,7 +175,7 @@ export enum WASIErrors {
|
|||
|
||||
export class WASIFileDescriptor {
|
||||
fdindex: number = -1;
|
||||
data: Uint8Array = new Uint8Array(16);
|
||||
protected data: Uint8Array = new Uint8Array(16);
|
||||
flags: number = 0;
|
||||
size: number = 0;
|
||||
offset: number = 0;
|
||||
|
@ -224,6 +245,8 @@ class WASIStreamingFileDescriptor extends WASIFileDescriptor {
|
|||
|
||||
export interface WASIFilesystem {
|
||||
getFile(name: string) : WASIFileDescriptor;
|
||||
getFiles() : WASIFileDescriptor[];
|
||||
getDirectories() : WASIFileDescriptor[];
|
||||
}
|
||||
|
||||
export class WASIMemoryFilesystem implements WASIFilesystem {
|
||||
|
@ -239,6 +262,13 @@ export class WASIMemoryFilesystem implements WASIFilesystem {
|
|||
}
|
||||
putDirectory(name: string, rights?: number) {
|
||||
if (!rights) rights = FDRights.PATH_OPEN | FDRights.PATH_CREATE_DIRECTORY | FDRights.PATH_CREATE_FILE;
|
||||
if (name != '/' && name.endsWith('/')) name = name.substring(0, name.length - 1);
|
||||
// add parent directory(s)
|
||||
const parent = name.substring(0, name.lastIndexOf('/'));
|
||||
if (parent && parent != name) {
|
||||
this.putDirectory(parent, rights);
|
||||
}
|
||||
// add directory
|
||||
const dir = new WASIFileDescriptor(name, FDType.DIRECTORY, rights);
|
||||
this.dirs.set(name, dir);
|
||||
return dir;
|
||||
|
@ -261,6 +291,12 @@ export class WASIMemoryFilesystem implements WASIFilesystem {
|
|||
}
|
||||
return file;
|
||||
}
|
||||
getDirectories() {
|
||||
return [...this.dirs.values()];
|
||||
}
|
||||
getFiles() {
|
||||
return [...this.files.values()];
|
||||
}
|
||||
}
|
||||
|
||||
export class WASIRunner {
|
||||
|
|
|
@ -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
|
||||
|
@ -908,13 +909,15 @@ function hideDebugInfo() {
|
|||
function showDebugInfo(state?) {
|
||||
if (!isDebuggable(platform)) return;
|
||||
var meminfo = $("#mem_info");
|
||||
var meminfomsg = $("#mem_info_msg");
|
||||
var allcats = platform.getDebugCategories();
|
||||
if (allcats && !debugCategory)
|
||||
debugCategory = allcats[0];
|
||||
var s = state && platform.getDebugInfo(debugCategory, state);
|
||||
if (s) {
|
||||
if (typeof s === 'string') {
|
||||
var hs = lastDebugInfo ? highlightDifferences(lastDebugInfo, s) : s;
|
||||
meminfo.show().html(hs);
|
||||
meminfo.show();
|
||||
meminfomsg.html(hs);
|
||||
var catspan = $('<div class="mem_info_links">');
|
||||
var addCategoryLink = (cat:string) => {
|
||||
var catlink = $('<a>'+cat+'</a>');
|
||||
|
@ -931,8 +934,8 @@ function showDebugInfo(state?) {
|
|||
for (var cat of allcats) {
|
||||
addCategoryLink(cat);
|
||||
}
|
||||
meminfo.append('<br>');
|
||||
meminfo.append(catspan);
|
||||
meminfomsg.append('<br>');
|
||||
meminfomsg.append(catspan);
|
||||
lastDebugInfo = s;
|
||||
} else {
|
||||
hideDebugInfo();
|
||||
|
|
|
@ -23,7 +23,8 @@ export class MemoryView implements ProjectView {
|
|||
dumplines;
|
||||
maindiv : HTMLElement;
|
||||
recreateOnResize = true;
|
||||
totalRows = 0x1400;
|
||||
hibits = 0; // a hack to make it work with 32-bit addresses
|
||||
totalRows = 0x1400; // a little more room in case we split lots of lines
|
||||
|
||||
createDiv(parent : HTMLElement) {
|
||||
var div = document.createElement('div');
|
||||
|
@ -44,7 +45,7 @@ export class MemoryView implements ProjectView {
|
|||
var linediv = document.createElement("div");
|
||||
if (this.dumplines) {
|
||||
var dlr = this.dumplines[row];
|
||||
if (dlr) linediv.classList.add('seg_' + this.getMemorySegment(this.dumplines[row].a));
|
||||
if (dlr) linediv.classList.add('seg_' + this.getMemorySegment(this.dumplines[row].a | this.hibits));
|
||||
}
|
||||
linediv.appendChild(document.createTextNode(s));
|
||||
return linediv;
|
||||
|
@ -59,7 +60,8 @@ export class MemoryView implements ProjectView {
|
|||
|
||||
scrollToAddress(addr : number) {
|
||||
if (this.dumplines) {
|
||||
this.memorylist.scrollToItem(this.findMemoryWindowLine(addr));
|
||||
this.hibits = addr & 0xffff0000;
|
||||
this.memorylist.scrollToItem(this.findMemoryWindowLine(addr & 0xffff));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -101,7 +103,7 @@ export class MemoryView implements ProjectView {
|
|||
for (var i=0; i<n1; i++) s += ' ';
|
||||
if (n1 > 8) s += ' ';
|
||||
for (var i=n1; i<n2; i++) {
|
||||
var read = this.readAddress(offset+i);
|
||||
var read = this.readAddress((offset+i) | this.hibits);
|
||||
if (i==8) s += ' ';
|
||||
s += ' ' + (typeof read == 'number' ? hex(read,2) : '??');
|
||||
}
|
||||
|
@ -130,7 +132,7 @@ export class MemoryView implements ProjectView {
|
|||
var sym;
|
||||
for (const _nextofs of Object.keys(addr2sym)) {
|
||||
var nextofs = parseInt(_nextofs); // convert from string (stupid JS)
|
||||
var nextsym = addr2sym[nextofs];
|
||||
var nextsym = addr2sym[nextofs | this.hibits];
|
||||
if (sym) {
|
||||
// ignore certain symbols
|
||||
if (ignoreSymbol(sym)) {
|
||||
|
|
|
@ -1,6 +1,27 @@
|
|||
/*
|
||||
* Copyright (c) 2024 Steven E. Hugg
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
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 +39,41 @@ 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 = 0x0;
|
||||
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 +91,15 @@ export class ARM32Machine extends BasicScanlineMachine implements Debuggable, Ha
|
|||
this.serial = serial;
|
||||
}
|
||||
|
||||
loadROM(rom: Uint8Array) {
|
||||
super.loadROM(rom);
|
||||
}
|
||||
|
||||
reset() {
|
||||
this.ram.fill(0);
|
||||
if (this.rom) {
|
||||
this.ram.set(this.rom, this.rombase);
|
||||
}
|
||||
super.reset();
|
||||
this.serialOut = [];
|
||||
this.serialIn = [];
|
||||
|
@ -74,19 +108,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,10 +125,42 @@ 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:
|
||||
return this.inputs[0];
|
||||
case 0x20:
|
||||
return this.getRasterY() & 0xff;
|
||||
case 0x21:
|
||||
return this.getRasterY() >> 8;
|
||||
case 0x24:
|
||||
return this.getRasterX();
|
||||
case 0x25:
|
||||
return this.getRasterX() >> 8;
|
||||
case 0x40:
|
||||
return (this.serial.byteAvailable() ? 0x80 : 0) | (this.serial.clearToSend() ? 0x40 : 0);
|
||||
case 0x44:
|
||||
|
@ -116,16 +176,14 @@ 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;
|
||||
break;
|
||||
case 0x48:
|
||||
if (this.serialOut.length < MAX_SERIAL_CHARS) {
|
||||
this.serialOut.push({op:'write', value:v, nbits:8});
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
startScanline() {
|
||||
|
@ -136,7 +194,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];
|
||||
|
@ -147,18 +206,34 @@ export class ARM32Machine extends BasicScanlineMachine implements Debuggable, Ha
|
|||
}
|
||||
|
||||
getDebugCategories() {
|
||||
return ['CPU', 'Stack'];
|
||||
return ['CPU', 'Stack', 'FPU'];
|
||||
}
|
||||
|
||||
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" : " -";
|
||||
|
@ -172,6 +247,19 @@ export class ARM32Machine extends BasicScanlineMachine implements Debuggable, Ha
|
|||
s += 'SPSR ' + hex(c.spsr,8) + '\n';
|
||||
s += 'cycl ' + c.cycles + '\n';
|
||||
return s;
|
||||
case 'FPU':
|
||||
var s = '';
|
||||
var c = state.c as ARMCoreState;
|
||||
for (var i=0; i<16; i++) {
|
||||
//let j = i+16;
|
||||
s += lpad('s'+i, 5) + ' ' + hex(c.ifprs[i],8) + ' ' + c.sfprs[i].toPrecision(6);
|
||||
if (i & 1) {
|
||||
s += lpad('d'+(i>>1), 5) + ' ' + c.dfprs[i>>1].toPrecision(12);
|
||||
}
|
||||
s += '\n';
|
||||
//s += lpad('s'+j, 5) + ' ' + lpad(c.sfprs[j]+'',8) + '\n';
|
||||
}
|
||||
return s;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,40 +1,49 @@
|
|||
/*
|
||||
* Copyright (c) 2024 Steven E. Hugg
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
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 +62,16 @@ 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'},
|
||||
] } };
|
||||
getPlatformName() { return "ARM7"; }
|
||||
getDebugTree() {
|
||||
return {
|
||||
...this.machine.cpu.getDebugTree(),
|
||||
dwarf: this.debugSymbols.debuginfo
|
||||
}
|
||||
}
|
||||
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;
|
||||
|
|
|
@ -0,0 +1,63 @@
|
|||
/*
|
||||
* Copyright (c) 2024 Steven E. Hugg
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
import assert from "assert";
|
||||
import { DWARFParser, ELFParser } from "../common/binutils";
|
||||
|
||||
describe('test ELFParser', () => {
|
||||
|
||||
const fs = require('fs');
|
||||
const data = fs.readFileSync('./test/exes/arm32.elf');
|
||||
const elfParser = new ELFParser(new Uint8Array(data));
|
||||
|
||||
it('should parse sections and symbols', () => {
|
||||
/*
|
||||
elfParser.sectionHeaders.forEach((section, index) => {
|
||||
console.log('section', index, section.name, section.type, section.vaddr.toString(16), section.size.toString(16));
|
||||
});
|
||||
elfParser.getSymbols().forEach((symbol, index) => {
|
||||
console.log('symbol', index, symbol.info, symbol.other, symbol.name, symbol.value.toString(16));
|
||||
});
|
||||
*/
|
||||
assert.strictEqual(21, elfParser.sectionHeaders.length);
|
||||
assert.strictEqual(29, elfParser.getSymbols().length);
|
||||
assert.ok(elfParser.sectionHeaders.find((section) => section.name === '.text') != null);
|
||||
assert.ok(elfParser.getSymbols().find((symbol) => symbol.name === 'main') != null);
|
||||
});
|
||||
|
||||
it('should parse DWARF info', () => {
|
||||
const dwarf = new DWARFParser(elfParser);
|
||||
assert.strictEqual(2, dwarf.units.length);
|
||||
const cu = dwarf.units[0];
|
||||
// TODO: check info content
|
||||
const li = dwarf.lineInfos[0];
|
||||
assert.strictEqual('crt0.c', li.files[1].name);
|
||||
/*
|
||||
assert.ok(info != null);
|
||||
assert.ok(info!.lineNumberProgram != null);
|
||||
assert.ok(info!.lineNumberProgram!.length > 0);
|
||||
assert.ok(info!.lineNumberProgram![0].file != null);
|
||||
assert.ok(info!.lineNumberProgram![0].file!.name != null);
|
||||
assert.ok(info!.lineNumberProgram![0].file!.name!.length > 0);
|
||||
*/
|
||||
});
|
||||
});
|
Binary file not shown.
|
@ -0,0 +1,27 @@
|
|||
|
||||
extern void _end;
|
||||
|
||||
void malloc_addblock(void* addr, int size);
|
||||
|
||||
int entry();
|
||||
|
||||
__attribute__((weak, naked, noinline, noreturn)) void _start() {
|
||||
// set bss segment symbols
|
||||
asm(".global __bss_start__, __bss_end__");
|
||||
asm("__bss_start__ = _edata");
|
||||
asm("__bss_end__ = _end");
|
||||
// set stack pointer
|
||||
asm("mov sp, #0x100000");
|
||||
// allocate heap block
|
||||
malloc_addblock(&_end, (void*)0xf0000 - &_end);
|
||||
// run main()
|
||||
entry();
|
||||
// wait for next video frame
|
||||
while (*(volatile int*)0x4000020 != 0) { }
|
||||
// halt cpu
|
||||
asm(".long 0xe7f000f0"); // udf #0
|
||||
}
|
||||
|
||||
void _Exit(int ec) {
|
||||
asm(".long 0xe7f000f0"); // udf #0
|
||||
}
|
Binary file not shown.
|
@ -1,3 +1,24 @@
|
|||
/*
|
||||
* Copyright (c) 2024 Steven E. Hugg
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
export var PLATFORM_PARAMS = {
|
||||
'vcs': {
|
||||
|
@ -334,6 +355,13 @@ export var PLATFORM_PARAMS = {
|
|||
extra_link_files: ['crt0.o', 'exidy.cfg'],
|
||||
//extra_compile_files: ['exidy.h'],
|
||||
},
|
||||
'arm32': {
|
||||
arch: 'arm32',
|
||||
define: ['__ARM__', 'DISABLE_UNIMPLEMENTED_LIBC_APIS', 'PRINTF_ALIAS_STANDARD_FUNCTION_NAMES_SOFT'],
|
||||
extra_compile_args: ['-I./arch/arm/include', '-I./openlibm/include', '-I./openlibm/src', '-I./printf/src'],
|
||||
extra_link_files: ['crt0.c', 'libc.a'],
|
||||
extra_link_args: ['crt0.c', '-lc'],
|
||||
},
|
||||
};
|
||||
|
||||
PLATFORM_PARAMS['sms-sms-libcv'] = PLATFORM_PARAMS['sms-sg1000-libcv'];
|
||||
|
|
|
@ -1,8 +1,32 @@
|
|||
/*
|
||||
* Copyright (c) 2024 Steven E. Hugg
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
import { DWARFParser, ELFParser } from "../../common/binutils";
|
||||
import { hex } from "../../common/util";
|
||||
import { WASIFilesystem } from "../../common/wasi/wasishim";
|
||||
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, processEmbedDirective } from "../builder";
|
||||
import { makeErrorMatcher, re_crlf } from "../listingutils";
|
||||
import { loadWASIFilesystemZip } from "../wasiutils";
|
||||
import { loadNative, moduleInstFn, execMain, emglobal, EmscriptenModule } from "../wasmutils";
|
||||
|
||||
export function assembleARMIPS(step: BuildStep): WorkerResult {
|
||||
|
@ -239,3 +263,191 @@ export function assembleVASMARM(step: BuildStep): BuildStepResult {
|
|||
}
|
||||
}
|
||||
|
||||
function tccErrorMatcher(errors: WorkerError[], mainpath: string) {
|
||||
return makeErrorMatcher(errors, /([^:]+|tcc):(\d+|\s*error): (.+)/, 2, 3, mainpath, 1);;
|
||||
}
|
||||
|
||||
let armtcc_fs: WASIFilesystem | null = null;
|
||||
|
||||
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 (!armtcc_fs) {
|
||||
armtcc_fs = await loadWASIFilesystemZip("arm32-fs.zip");
|
||||
}
|
||||
|
||||
if (staleFiles(step, [objpath])) {
|
||||
const armtcc: EmscriptenModule = await emglobal.armtcc({
|
||||
instantiateWasm: moduleInstFn('arm-tcc'),
|
||||
noInitialRun: true,
|
||||
print: error_fn,
|
||||
printErr: error_fn,
|
||||
});
|
||||
|
||||
var args = ['-c', '-I.', '-I./include',
|
||||
//'-std=c11',
|
||||
'-funsigned-char',
|
||||
//'-Wwrite-strings',
|
||||
'-gdwarf-2',
|
||||
'-o', objpath];
|
||||
if (params.define) {
|
||||
params.define.forEach((x) => args.push('-D' + x));
|
||||
}
|
||||
if (params.extra_compile_args) {
|
||||
args = args.concat(params.extra_compile_args);
|
||||
}
|
||||
args.push(step.path);
|
||||
|
||||
const FS = armtcc.FS;
|
||||
// TODO: only should do once?
|
||||
armtcc_fs.getDirectories().forEach((dir) => {
|
||||
if (dir.name != '/') FS.mkdir(dir.name);
|
||||
});
|
||||
armtcc_fs.getFiles().forEach((file) => {
|
||||
FS.writeFile(file.name, file.getBytes(), { encoding: 'binary' });
|
||||
});
|
||||
populateExtraFiles(step, FS, params.extra_compile_files);
|
||||
|
||||
populateFiles(step, FS, {
|
||||
mainFilePath: step.path,
|
||||
processFn: (path, code) => {
|
||||
if (typeof code === 'string') {
|
||||
code = processEmbedDirective(code);
|
||||
}
|
||||
return code;
|
||||
}
|
||||
});
|
||||
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.elf";
|
||||
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=elf32-arm',
|
||||
//'-Wl,-section-alignment=0x100000',
|
||||
'-gdwarf-2',
|
||||
'-o', objpath];
|
||||
if (params.define) {
|
||||
params.define.forEach((x) => args.push('-D' + x));
|
||||
}
|
||||
args = args.concat(step.files);
|
||||
if (params.extra_link_args) {
|
||||
args = args.concat(params.extra_link_args);
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
// parse ELF and create ROM
|
||||
const elfparser = new ELFParser(objout);
|
||||
let maxaddr = 0;
|
||||
elfparser.sectionHeaders.forEach((section, index) => {
|
||||
maxaddr = Math.max(maxaddr, section.vmaddr + section.size);
|
||||
});
|
||||
let rom = new Uint8Array(maxaddr);
|
||||
elfparser.sectionHeaders.forEach((section, index) => {
|
||||
if (section.flags & 0x2) {
|
||||
let data = objout.slice(section.offset, section.offset + section.size);
|
||||
//console.log(section.name, section.vmaddr.toString(16), data);
|
||||
rom.set(data, section.vmaddr);
|
||||
}
|
||||
});
|
||||
// set vectors, entry point etc
|
||||
const obj32 = new Uint32Array(rom.buffer);
|
||||
const start = elfparser.entry;
|
||||
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
|
||||
|
||||
let symbolmap = {};
|
||||
elfparser.getSymbols().forEach((symbol, index) => {
|
||||
symbolmap[symbol.name] = symbol.value;
|
||||
});
|
||||
let segments = [];
|
||||
elfparser.sectionHeaders.forEach((section, index) => {
|
||||
if ((section.flags & 0x2) && section.size) {
|
||||
segments.push({
|
||||
name: section.name,
|
||||
start: section.vmaddr,
|
||||
size: section.size,
|
||||
type: section.type,
|
||||
});
|
||||
}
|
||||
});
|
||||
const listings: CodeListingMap = {};
|
||||
const dwarf = new DWARFParser(elfparser);
|
||||
dwarf.lineInfos.forEach((lineInfo) => {
|
||||
lineInfo.files.forEach((file) => {
|
||||
if (!file || !file.lines) return;
|
||||
file.lines.forEach((line) => {
|
||||
const filename = line.file;
|
||||
const offset = line.address;
|
||||
const path = getPrefix(filename) + '.lst';
|
||||
const linenum = line.line;
|
||||
let lst = listings[path];
|
||||
if (lst == null) { lst = listings[path] = { lines: [] }; }
|
||||
lst.lines.push({
|
||||
path,
|
||||
line: linenum,
|
||||
offset
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
//console.log(listings);
|
||||
|
||||
return {
|
||||
output: rom, //.slice(0x34),
|
||||
listings: listings,
|
||||
errors: errors,
|
||||
symbolmap: symbolmap,
|
||||
segments: segments,
|
||||
debuginfo: dwarf
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
|
|
File diff suppressed because one or more lines are too long
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 = {
|
||||
|
|
Binary file not shown.
Loading…
Reference in New Issue