1
0
mirror of https://github.com/sehugg/8bitworkshop.git synced 2024-11-28 08:50:22 +00:00

arm: fixed fpu, raster frame sync

This commit is contained in:
Steven Hugg 2024-01-01 17:35:13 -05:00
parent 579e58e966
commit d5b5734ef9
5 changed files with 198 additions and 40 deletions

View File

@ -1771,46 +1771,37 @@ ARMCoreArm.prototype.constructUMULLS = function(rd, rn, rs, rm, condOp) {
}; };
}; };
ARMCoreArm.prototype.constructVFP3Register = function(condOp, opcode, nOperandReg, destReg, number, opcode2, mOperandReg) { ARMCoreArm.prototype.constructVFP3Register = function(condOp, opcode, nOperandReg, destReg, sz, opcode2, mOperandReg) {
var cpu : ARMCoreType = this.cpu; var cpu : ARMCoreType = this.cpu;
var gprs = cpu.gprs; var fpregs = sz ? cpu.dfprs : cpu.sfprs;
var sregs = cpu.sfprs; //console.log("VFP3Register: " + hex(opcode) + " " + hex(nOperandReg) + " " + hex(destReg) + " " + hex(number) + " " + hex(opcode2) + " " + hex(mOperandReg));
var dregs = cpu.dfprs;
var iregs = cpu.ifprs;
return function() { return function() {
cpu.mmu.waitPrefetch32(cpu.gprs[ARMRegs.PC]); cpu.mmu.waitPrefetch32(cpu.gprs[ARMRegs.PC]);
if (condOp && !condOp()) { if (condOp && !condOp()) {
return; return;
} }
switch (opcode) { switch (opcode) {
case 1: // VMOV
switch (opcode2) {
case 0:
gprs[destReg] = iregs[nOperandReg];
return;
}
break;
case 2: // VMUL case 2: // VMUL
switch (opcode2) { switch (opcode2) {
case 0: case 0:
sregs[destReg] = sregs[nOperandReg] * sregs[mOperandReg]; fpregs[destReg] = fpregs[nOperandReg] * fpregs[mOperandReg];
return; return;
} }
break; break;
case 3: // VADD/VSUB case 3: // VADD/VSUB
switch (opcode2) { switch (opcode2) {
case 0: case 0:
sregs[destReg] = sregs[nOperandReg] + sregs[mOperandReg]; fpregs[destReg] = fpregs[nOperandReg] + fpregs[mOperandReg];
return; return;
case 2: case 2:
sregs[destReg] = sregs[nOperandReg] - sregs[mOperandReg]; fpregs[destReg] = fpregs[nOperandReg] - fpregs[mOperandReg];
return; return;
} }
break; break;
case 8: // VDIV case 8: // VDIV
switch (opcode2) { switch (opcode2) {
case 0: case 0:
sregs[destReg] = sregs[nOperandReg] / sregs[mOperandReg]; fpregs[destReg] = fpregs[nOperandReg] / fpregs[mOperandReg];
return; return;
} }
break; break;
@ -1892,6 +1883,26 @@ ARMCoreArm.prototype.constructVCVT = function(condOp, D, opc2, Vd, sz, op, M, Vm
}; };
} }
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) { ARMCoreArm.prototype.constructVLDR = function(condOp, destReg, address, single_reg) {
var cpu : ARMCoreType = this.cpu; var cpu : ARMCoreType = this.cpu;
var iregs = cpu.ifprs; var iregs = cpu.ifprs;
@ -1904,8 +1915,8 @@ ARMCoreArm.prototype.constructVLDR = function(condOp, destReg, address, single_r
if (single_reg) { if (single_reg) {
iregs[destReg] = cpu.mmu.load32(addr); iregs[destReg] = cpu.mmu.load32(addr);
} else { } else {
iregs[destReg*2] = cpu.mmu.load32(addr); iregs[destReg] = cpu.mmu.load32(addr);
iregs[destReg*2+1] = cpu.mmu.load32(addr+4); iregs[destReg+1] = cpu.mmu.load32(addr+4);
} }
cpu.mmu.wait32(addr); cpu.mmu.wait32(addr);
cpu.mmu.wait32(cpu.gprs[ARMRegs.PC]); cpu.mmu.wait32(cpu.gprs[ARMRegs.PC]);
@ -1966,6 +1977,58 @@ ARMCoreArm.prototype.constructVPOP = function(condOp, d, regs, single_regs) {
}; };
} }
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.constructVMOV = function(condOp, to_arm_reg, n, t) {
var cpu : ARMCoreType = this.cpu;
var srcregs = to_arm_reg ? cpu.ifprs : cpu.gprs;
var destregs = to_arm_reg ? cpu.gprs : cpu.ifprs;
//console.log('VMOV: ' + hex(to_arm_reg) + ' ' + hex(n) + ' ' + hex(t));
return function() {
cpu.mmu.waitPrefetch32(cpu.gprs[ARMRegs.PC]);
if (condOp && !condOp()) {
return;
}
destregs[t] = srcregs[n];
}
}
/////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////
function ARMCoreThumb(cpu) { function ARMCoreThumb(cpu) {
@ -3926,9 +3989,9 @@ ARMCore.prototype.compileArm = function(instruction) {
address = this.armCompiler.constructAddressingMode4(immediate, rn); address = this.armCompiler.constructAddressingMode4(immediate, rn);
} }
if (load) { if (load) {
op = this.armCompiler.constructVLDR(condOp, crd, address, true); op = this.armCompiler.constructVLDR(condOp, d, address, true);
} else { } else {
op = this.armCompiler.constructVSTR(condOp, crd, address, true); op = this.armCompiler.constructVSTR(condOp, d, address, true);
} }
} else if ((instruction & 0x0f200f00) == 0x0d000b00) { } else if ((instruction & 0x0f200f00) == 0x0d000b00) {
immediate *= 4; immediate *= 4;
@ -3943,9 +4006,9 @@ ARMCore.prototype.compileArm = function(instruction) {
address = this.armCompiler.constructAddressingMode4(immediate, rn); address = this.armCompiler.constructAddressingMode4(immediate, rn);
} }
if (load) { if (load) {
op = this.armCompiler.constructVLDR(condOp, crd, address, false); op = this.armCompiler.constructVLDR(condOp, d, address, false);
} else { } else {
op = this.armCompiler.constructVSTR(condOp, crd, address, false); op = this.armCompiler.constructVSTR(condOp, d, address, false);
} }
} }
break; break;
@ -3971,19 +4034,99 @@ ARMCore.prototype.compileArm = function(instruction) {
op = this.armCompiler.constructVCVT(condOp, D, opc2, Vd, sz, to_fixed, M, Vm); op = this.armCompiler.constructVCVT(condOp, D, opc2, Vd, sz, to_fixed, M, Vm);
op.writesPC = false; op.writesPC = false;
} }
// floating point vector instructions // VCVT f64/f32
else { else if ((instruction & 0x0FBF0ED0) == 0x0EB70AC0) {
const cond = (instruction >> 28) & 0xf; const cond = (instruction >> 28) & 0xf;
const opcode = (instruction & 0x0F00000) >> 20; const D = (instruction >> 22) & 0x1;
const CRn = (instruction & 0x000F0000) >> 16; const Vd = (instruction >> 12) & 0xf;
const CRd = (instruction & 0x0000F000) >> 12; const sz = (instruction >> 8) & 0x1;
const CPn = (instruction & 0x00000F00) >> 8; const M = (instruction >> 5) & 0x1;
const opcode2 = (instruction & 0b11100000) >> 5; const Vm = instruction & 0xf;
const CRm = instruction & 0x0000000F; const double_to_single = sz != 0;
var condOp = this.conds[cond]; const d = sz ? (D?16:0)|Vd : (Vd<<1)|(D?1:0);
op = this.armCompiler.constructVFP3Register(condOp, opcode, CRn, CRd, CPn, opcode2, CRm); 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; 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;
}
// vmrs apsr_nzcv, fpscr (ignore, we always call this after CMP)
else if (instruction == 0xeef1fa10) {
op = this.armCompiler.constructNOP();
}
// VMOV
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];
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; break;
default: default:
throw new EmuHalt('Bad opcode: 0x' + instruction.toString(16)); throw new EmuHalt('Bad opcode: 0x' + instruction.toString(16));

View File

@ -914,7 +914,7 @@ function showDebugInfo(state?) {
if (allcats && !debugCategory) if (allcats && !debugCategory)
debugCategory = allcats[0]; debugCategory = allcats[0];
var s = state && platform.getDebugInfo(debugCategory, state); var s = state && platform.getDebugInfo(debugCategory, state);
if (s) { if (typeof s === 'string') {
var hs = lastDebugInfo ? highlightDifferences(lastDebugInfo, s) : s; var hs = lastDebugInfo ? highlightDifferences(lastDebugInfo, s) : s;
meminfo.show(); meminfo.show();
meminfomsg.html(hs); meminfomsg.html(hs);

View File

@ -152,6 +152,14 @@ export class ARM32Machine extends BasicScanlineMachine
switch (a) { switch (a) {
case 0x0: case 0x0:
return this.inputs[0]; 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: case 0x40:
return (this.serial.byteAvailable() ? 0x80 : 0) | (this.serial.clearToSend() ? 0x40 : 0); return (this.serial.byteAvailable() ? 0x80 : 0) | (this.serial.clearToSend() ? 0x40 : 0);
case 0x44: case 0x44:
@ -169,15 +177,12 @@ export class ARM32Machine extends BasicScanlineMachine
writeIO(a : number, v : number) : void { writeIO(a : number, v : number) : void {
this.ioregs[a] = v; this.ioregs[a] = v;
switch (a) { switch (a) {
case 0x0:
//this.brightness = v & 0xff;
break;
case 0x48: case 0x48:
if (this.serialOut.length < MAX_SERIAL_CHARS) { if (this.serialOut.length < MAX_SERIAL_CHARS) {
this.serialOut.push({op:'write', value:v, nbits:8}); this.serialOut.push({op:'write', value:v, nbits:8});
} }
break; break;
} }
} }
startScanline() { startScanline() {
@ -246,7 +251,11 @@ export class ARM32Machine extends BasicScanlineMachine
var c = state.c as ARMCoreState; var c = state.c as ARMCoreState;
for (var i=0; i<16; i++) { for (var i=0; i<16; i++) {
//let j = i+16; //let j = i+16;
s += lpad('s'+i, 5) + ' ' + hex(c.ifprs[i],8) + ' ' + c.sfprs[i] + '\n'; 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'; //s += lpad('s'+j, 5) + ' ' + lpad(c.sfprs[j]+'',8) + '\n';
} }
return s; return s;

View File

@ -2,11 +2,17 @@
int entry(); int entry();
__attribute__((weak, naked, noinline, noreturn)) void _start() { __attribute__((weak, naked, noinline, noreturn)) void _start() {
// set bss segment symbols
asm(".global __bss_start__, __bss_end__"); asm(".global __bss_start__, __bss_end__");
asm("__bss_start__ = _edata"); asm("__bss_start__ = _edata");
asm("__bss_end__ = _end"); asm("__bss_end__ = _end");
asm("mov sp, #0x100000"); // stack pointer // set stack pointer
asm("mov sp, #0x100000");
// run main()
entry(); entry();
// wait for next video frame
while (*(volatile int*)0x4000020 != 0) { }
// halt cpu
asm(".long 0xe7f000f0"); // udf #0 asm(".long 0xe7f000f0"); // udf #0
} }

View File

@ -292,7 +292,7 @@ export async function compileARMTCC(step: BuildStep): Promise<BuildStepResult> {
var args = ['-c', '-I.', '-I./include', var args = ['-c', '-I.', '-I./include',
//'-std=c11', //'-std=c11',
'-funsigned-char', '-funsigned-char',
'-Wwrite-strings', //'-Wwrite-strings',
'-gdwarf', '-gdwarf',
'-o', objpath]; '-o', objpath];
if (params.define) { if (params.define) {