chore: change directory structure
This commit is contained in:
104
src/core/cpu/arm/block_data_transfer.zig
Normal file
104
src/core/cpu/arm/block_data_transfer.zig
Normal file
@@ -0,0 +1,104 @@
|
||||
const Bus = @import("../../Bus.zig");
|
||||
const Arm7tdmi = @import("../../cpu.zig").Arm7tdmi;
|
||||
const InstrFn = @import("../../cpu.zig").ArmInstrFn;
|
||||
|
||||
pub fn blockDataTransfer(comptime P: bool, comptime U: bool, comptime S: bool, comptime W: bool, comptime L: bool) InstrFn {
|
||||
return struct {
|
||||
fn inner(cpu: *Arm7tdmi, bus: *Bus, opcode: u32) void {
|
||||
const rn = @truncate(u4, opcode >> 16 & 0xF);
|
||||
const rlist = opcode & 0xFFFF;
|
||||
const r15 = rlist >> 15 & 1 == 1;
|
||||
|
||||
var count: u32 = 0;
|
||||
var i: u5 = 0;
|
||||
var first: u4 = 0;
|
||||
var write_to_base = true;
|
||||
|
||||
while (i < 16) : (i += 1) {
|
||||
const r = @truncate(u4, 15 - i);
|
||||
if (rlist >> r & 1 == 1) {
|
||||
first = r;
|
||||
count += 1;
|
||||
}
|
||||
}
|
||||
|
||||
var start = cpu.r[rn];
|
||||
if (U) {
|
||||
start += if (P) 4 else 0;
|
||||
} else {
|
||||
start = start - (4 * count) + if (!P) 4 else 0;
|
||||
}
|
||||
|
||||
var end = cpu.r[rn];
|
||||
if (U) {
|
||||
end = end + (4 * count) - if (!P) 4 else 0;
|
||||
} else {
|
||||
end -= if (P) 4 else 0;
|
||||
}
|
||||
|
||||
var new_base = cpu.r[rn];
|
||||
if (U) {
|
||||
new_base += 4 * count;
|
||||
} else {
|
||||
new_base -= 4 * count;
|
||||
}
|
||||
|
||||
var address = start;
|
||||
|
||||
if (rlist == 0) {
|
||||
var und_addr = cpu.r[rn];
|
||||
if (U) {
|
||||
und_addr += if (P) 4 else 0;
|
||||
} else {
|
||||
und_addr -= 0x40 - if (!P) 4 else 0;
|
||||
}
|
||||
|
||||
if (L) {
|
||||
cpu.r[15] = bus.read(u32, und_addr);
|
||||
} else {
|
||||
bus.write(u32, und_addr, cpu.r[15] + 8);
|
||||
}
|
||||
|
||||
cpu.r[rn] = if (U) cpu.r[rn] + 0x40 else cpu.r[rn] - 0x40;
|
||||
return;
|
||||
}
|
||||
|
||||
i = first;
|
||||
while (i < 16) : (i += 1) {
|
||||
if (rlist >> i & 1 == 1) {
|
||||
transfer(cpu, bus, r15, i, address);
|
||||
address += 4;
|
||||
|
||||
if (W and !L and write_to_base) {
|
||||
cpu.r[rn] = new_base;
|
||||
write_to_base = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (W and L and rlist >> rn & 1 == 0) cpu.r[rn] = new_base;
|
||||
}
|
||||
|
||||
fn transfer(cpu: *Arm7tdmi, bus: *Bus, r15_present: bool, i: u5, address: u32) void {
|
||||
if (L) {
|
||||
if (S and !r15_present) {
|
||||
// Always Transfer User mode Registers
|
||||
cpu.setUserModeRegister(i, bus.read(u32, address));
|
||||
} else {
|
||||
const value = bus.read(u32, address);
|
||||
cpu.r[i] = if (i == 0xF) value & 0xFFFF_FFFC else value;
|
||||
if (S and i == 0xF) cpu.setCpsr(cpu.spsr.raw);
|
||||
}
|
||||
} else {
|
||||
if (S) {
|
||||
// Always Transfer User mode Registers
|
||||
// This happens regardless if r15 is in the list
|
||||
const value = cpu.getUserModeRegister(i);
|
||||
bus.write(u32, address, value + if (i == 0xF) 8 else @as(u32, 0)); // PC is already 4 ahead to make 12
|
||||
} else {
|
||||
bus.write(u32, address, cpu.r[i] + if (i == 0xF) 8 else @as(u32, 0));
|
||||
}
|
||||
}
|
||||
}
|
||||
}.inner;
|
||||
}
|
22
src/core/cpu/arm/branch.zig
Normal file
22
src/core/cpu/arm/branch.zig
Normal file
@@ -0,0 +1,22 @@
|
||||
const std = @import("std");
|
||||
|
||||
const Bus = @import("../../Bus.zig");
|
||||
const Arm7tdmi = @import("../../cpu.zig").Arm7tdmi;
|
||||
const InstrFn = @import("../../cpu.zig").ArmInstrFn;
|
||||
|
||||
const sext = @import("../../util.zig").sext;
|
||||
|
||||
pub fn branch(comptime L: bool) InstrFn {
|
||||
return struct {
|
||||
fn inner(cpu: *Arm7tdmi, _: *Bus, opcode: u32) void {
|
||||
if (L) cpu.r[14] = cpu.r[15];
|
||||
cpu.r[15] = cpu.fakePC() +% (sext(u32, u24, opcode) << 2);
|
||||
}
|
||||
}.inner;
|
||||
}
|
||||
|
||||
pub fn branchAndExchange(cpu: *Arm7tdmi, _: *Bus, opcode: u32) void {
|
||||
const rn = opcode & 0xF;
|
||||
cpu.cpsr.t.write(cpu.r[rn] & 1 == 1);
|
||||
cpu.r[15] = cpu.r[rn] & 0xFFFF_FFFE;
|
||||
}
|
284
src/core/cpu/arm/data_processing.zig
Normal file
284
src/core/cpu/arm/data_processing.zig
Normal file
@@ -0,0 +1,284 @@
|
||||
const Bus = @import("../../Bus.zig");
|
||||
const Arm7tdmi = @import("../../cpu.zig").Arm7tdmi;
|
||||
const InstrFn = @import("../../cpu.zig").ArmInstrFn;
|
||||
|
||||
const rotateRight = @import("../barrel_shifter.zig").rotateRight;
|
||||
const execute = @import("../barrel_shifter.zig").execute;
|
||||
|
||||
pub fn dataProcessing(comptime I: bool, comptime S: bool, comptime instrKind: u4) InstrFn {
|
||||
return struct {
|
||||
fn inner(cpu: *Arm7tdmi, _: *Bus, opcode: u32) void {
|
||||
const rd = @truncate(u4, opcode >> 12 & 0xF);
|
||||
const rn = opcode >> 16 & 0xF;
|
||||
const old_carry = @boolToInt(cpu.cpsr.c.read());
|
||||
|
||||
// If certain conditions are met, PC is 12 ahead instead of 8
|
||||
if (!I and opcode >> 4 & 1 == 1) cpu.r[15] += 4;
|
||||
|
||||
const op1 = if (rn == 0xF) cpu.fakePC() else cpu.r[rn];
|
||||
|
||||
var op2: u32 = undefined;
|
||||
if (I) {
|
||||
const amount = @truncate(u8, (opcode >> 8 & 0xF) << 1);
|
||||
op2 = rotateRight(S, &cpu.cpsr, opcode & 0xFF, amount);
|
||||
} else {
|
||||
op2 = execute(S, cpu, opcode);
|
||||
}
|
||||
|
||||
// Undo special condition from above
|
||||
if (!I and opcode >> 4 & 1 == 1) cpu.r[15] -= 4;
|
||||
|
||||
switch (instrKind) {
|
||||
0x0 => {
|
||||
// AND
|
||||
const result = op1 & op2;
|
||||
cpu.r[rd] = result;
|
||||
setArmLogicOpFlags(S, cpu, rd, result);
|
||||
},
|
||||
0x1 => {
|
||||
// EOR
|
||||
const result = op1 ^ op2;
|
||||
cpu.r[rd] = result;
|
||||
setArmLogicOpFlags(S, cpu, rd, result);
|
||||
},
|
||||
0x2 => {
|
||||
// SUB
|
||||
cpu.r[rd] = armSub(S, cpu, rd, op1, op2);
|
||||
},
|
||||
0x3 => {
|
||||
// RSB
|
||||
cpu.r[rd] = armSub(S, cpu, rd, op2, op1);
|
||||
},
|
||||
0x4 => {
|
||||
// ADD
|
||||
cpu.r[rd] = armAdd(S, cpu, rd, op1, op2);
|
||||
},
|
||||
0x5 => {
|
||||
// ADC
|
||||
cpu.r[rd] = armAdc(S, cpu, rd, op1, op2, old_carry);
|
||||
},
|
||||
0x6 => {
|
||||
// SBC
|
||||
cpu.r[rd] = armSbc(S, cpu, rd, op1, op2, old_carry);
|
||||
},
|
||||
0x7 => {
|
||||
// RSC
|
||||
cpu.r[rd] = armSbc(S, cpu, rd, op2, op1, old_carry);
|
||||
},
|
||||
0x8 => {
|
||||
// TST
|
||||
if (rd == 0xF) {
|
||||
undefinedTestBehaviour(cpu);
|
||||
return;
|
||||
}
|
||||
|
||||
const result = op1 & op2;
|
||||
setTestOpFlags(S, cpu, opcode, result);
|
||||
},
|
||||
0x9 => {
|
||||
// TEQ
|
||||
if (rd == 0xF) {
|
||||
undefinedTestBehaviour(cpu);
|
||||
return;
|
||||
}
|
||||
|
||||
const result = op1 ^ op2;
|
||||
setTestOpFlags(S, cpu, opcode, result);
|
||||
},
|
||||
0xA => {
|
||||
// CMP
|
||||
if (rd == 0xF) {
|
||||
undefinedTestBehaviour(cpu);
|
||||
return;
|
||||
}
|
||||
|
||||
cmp(cpu, op1, op2);
|
||||
},
|
||||
0xB => {
|
||||
// CMN
|
||||
if (rd == 0xF) {
|
||||
undefinedTestBehaviour(cpu);
|
||||
return;
|
||||
}
|
||||
|
||||
cmn(cpu, op1, op2);
|
||||
},
|
||||
0xC => {
|
||||
// ORR
|
||||
const result = op1 | op2;
|
||||
cpu.r[rd] = result;
|
||||
setArmLogicOpFlags(S, cpu, rd, result);
|
||||
},
|
||||
0xD => {
|
||||
// MOV
|
||||
cpu.r[rd] = op2;
|
||||
setArmLogicOpFlags(S, cpu, rd, op2);
|
||||
},
|
||||
0xE => {
|
||||
// BIC
|
||||
const result = op1 & ~op2;
|
||||
cpu.r[rd] = result;
|
||||
setArmLogicOpFlags(S, cpu, rd, result);
|
||||
},
|
||||
0xF => {
|
||||
// MVN
|
||||
const result = ~op2;
|
||||
cpu.r[rd] = result;
|
||||
setArmLogicOpFlags(S, cpu, rd, result);
|
||||
},
|
||||
}
|
||||
}
|
||||
}.inner;
|
||||
}
|
||||
|
||||
fn armSbc(comptime S: bool, cpu: *Arm7tdmi, rd: u4, left: u32, right: u32, old_carry: u1) u32 {
|
||||
var result: u32 = undefined;
|
||||
if (S and rd == 0xF) {
|
||||
result = sbc(false, cpu, left, right, old_carry);
|
||||
cpu.setCpsr(cpu.spsr.raw);
|
||||
} else {
|
||||
result = sbc(S, cpu, left, right, old_carry);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
pub fn sbc(comptime S: bool, cpu: *Arm7tdmi, left: u32, right: u32, old_carry: u1) u32 {
|
||||
// TODO: Make your own version (thanks peach.bot)
|
||||
const subtrahend = @as(u64, right) -% old_carry +% 1;
|
||||
const result = @truncate(u32, left -% subtrahend);
|
||||
|
||||
if (S) {
|
||||
cpu.cpsr.n.write(result >> 31 & 1 == 1);
|
||||
cpu.cpsr.z.write(result == 0);
|
||||
cpu.cpsr.c.write(subtrahend <= left);
|
||||
cpu.cpsr.v.write(((left ^ result) & (~right ^ result)) >> 31 & 1 == 1);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
fn armSub(comptime S: bool, cpu: *Arm7tdmi, rd: u4, left: u32, right: u32) u32 {
|
||||
var result: u32 = undefined;
|
||||
if (S and rd == 0xF) {
|
||||
result = sub(false, cpu, left, right);
|
||||
cpu.setCpsr(cpu.spsr.raw);
|
||||
} else {
|
||||
result = sub(S, cpu, left, right);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
pub fn sub(comptime S: bool, cpu: *Arm7tdmi, left: u32, right: u32) u32 {
|
||||
const result = left -% right;
|
||||
|
||||
if (S) {
|
||||
cpu.cpsr.n.write(result >> 31 & 1 == 1);
|
||||
cpu.cpsr.z.write(result == 0);
|
||||
cpu.cpsr.c.write(right <= left);
|
||||
cpu.cpsr.v.write(((left ^ result) & (~right ^ result)) >> 31 & 1 == 1);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
fn armAdd(comptime S: bool, cpu: *Arm7tdmi, rd: u4, left: u32, right: u32) u32 {
|
||||
var result: u32 = undefined;
|
||||
if (S and rd == 0xF) {
|
||||
result = add(false, cpu, left, right);
|
||||
cpu.setCpsr(cpu.spsr.raw);
|
||||
} else {
|
||||
result = add(S, cpu, left, right);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
pub fn add(comptime S: bool, cpu: *Arm7tdmi, left: u32, right: u32) u32 {
|
||||
var result: u32 = undefined;
|
||||
const didOverflow = @addWithOverflow(u32, left, right, &result);
|
||||
|
||||
if (S) {
|
||||
cpu.cpsr.n.write(result >> 31 & 1 == 1);
|
||||
cpu.cpsr.z.write(result == 0);
|
||||
cpu.cpsr.c.write(didOverflow);
|
||||
cpu.cpsr.v.write(((left ^ result) & (right ^ result)) >> 31 & 1 == 1);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
fn armAdc(comptime S: bool, cpu: *Arm7tdmi, rd: u4, left: u32, right: u32, old_carry: u1) u32 {
|
||||
var result: u32 = undefined;
|
||||
if (S and rd == 0xF) {
|
||||
result = adc(false, cpu, left, right, old_carry);
|
||||
cpu.setCpsr(cpu.spsr.raw);
|
||||
} else {
|
||||
result = adc(S, cpu, left, right, old_carry);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
pub fn adc(comptime S: bool, cpu: *Arm7tdmi, left: u32, right: u32, old_carry: u1) u32 {
|
||||
var result: u32 = undefined;
|
||||
const did = @addWithOverflow(u32, left, right, &result);
|
||||
const overflow = @addWithOverflow(u32, result, old_carry, &result);
|
||||
|
||||
if (S) {
|
||||
cpu.cpsr.n.write(result >> 31 & 1 == 1);
|
||||
cpu.cpsr.z.write(result == 0);
|
||||
cpu.cpsr.c.write(did or overflow);
|
||||
cpu.cpsr.v.write(((left ^ result) & (right ^ result)) >> 31 & 1 == 1);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
pub fn cmp(cpu: *Arm7tdmi, left: u32, right: u32) void {
|
||||
const result = left -% right;
|
||||
|
||||
cpu.cpsr.n.write(result >> 31 & 1 == 1);
|
||||
cpu.cpsr.z.write(result == 0);
|
||||
cpu.cpsr.c.write(right <= left);
|
||||
cpu.cpsr.v.write(((left ^ result) & (~right ^ result)) >> 31 & 1 == 1);
|
||||
}
|
||||
|
||||
pub fn cmn(cpu: *Arm7tdmi, left: u32, right: u32) void {
|
||||
var result: u32 = undefined;
|
||||
const didOverflow = @addWithOverflow(u32, left, right, &result);
|
||||
|
||||
cpu.cpsr.n.write(result >> 31 & 1 == 1);
|
||||
cpu.cpsr.z.write(result == 0);
|
||||
cpu.cpsr.c.write(didOverflow);
|
||||
cpu.cpsr.v.write(((left ^ result) & (right ^ result)) >> 31 & 1 == 1);
|
||||
}
|
||||
|
||||
fn setArmLogicOpFlags(comptime S: bool, cpu: *Arm7tdmi, rd: u4, result: u32) void {
|
||||
if (S and rd == 0xF) {
|
||||
cpu.setCpsr(cpu.spsr.raw);
|
||||
} else {
|
||||
setLogicOpFlags(S, cpu, result);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn setLogicOpFlags(comptime S: bool, cpu: *Arm7tdmi, result: u32) void {
|
||||
if (S) {
|
||||
cpu.cpsr.n.write(result >> 31 & 1 == 1);
|
||||
cpu.cpsr.z.write(result == 0);
|
||||
// C set by Barrel Shifter, V is unaffected
|
||||
}
|
||||
}
|
||||
|
||||
fn setTestOpFlags(comptime S: bool, cpu: *Arm7tdmi, opcode: u32, result: u32) void {
|
||||
cpu.cpsr.n.write(result >> 31 & 1 == 1);
|
||||
cpu.cpsr.z.write(result == 0);
|
||||
// Barrel Shifter should always calc CPSR C in TST
|
||||
if (!S) _ = execute(true, cpu, opcode);
|
||||
}
|
||||
|
||||
fn undefinedTestBehaviour(cpu: *Arm7tdmi) void {
|
||||
@setCold(true);
|
||||
cpu.setCpsr(cpu.spsr.raw);
|
||||
}
|
70
src/core/cpu/arm/half_signed_data_transfer.zig
Normal file
70
src/core/cpu/arm/half_signed_data_transfer.zig
Normal file
@@ -0,0 +1,70 @@
|
||||
const std = @import("std");
|
||||
|
||||
const Bus = @import("../../Bus.zig");
|
||||
const Arm7tdmi = @import("../../cpu.zig").Arm7tdmi;
|
||||
const InstrFn = @import("../../cpu.zig").ArmInstrFn;
|
||||
|
||||
const sext = @import("../../util.zig").sext;
|
||||
const rotr = @import("../../util.zig").rotr;
|
||||
|
||||
pub fn halfAndSignedDataTransfer(comptime P: bool, comptime U: bool, comptime I: bool, comptime W: bool, comptime L: bool) InstrFn {
|
||||
return struct {
|
||||
fn inner(cpu: *Arm7tdmi, bus: *Bus, opcode: u32) void {
|
||||
const rn = opcode >> 16 & 0xF;
|
||||
const rd = opcode >> 12 & 0xF;
|
||||
const rm = opcode & 0xF;
|
||||
const imm_offset_high = opcode >> 8 & 0xF;
|
||||
|
||||
var base: u32 = undefined;
|
||||
if (rn == 0xF) {
|
||||
base = cpu.fakePC();
|
||||
if (!L) base += 4;
|
||||
} else {
|
||||
base = cpu.r[rn];
|
||||
}
|
||||
|
||||
var offset: u32 = undefined;
|
||||
if (I) {
|
||||
offset = imm_offset_high << 4 | rm;
|
||||
} else {
|
||||
offset = cpu.r[rm];
|
||||
}
|
||||
|
||||
const modified_base = if (U) base +% offset else base -% offset;
|
||||
var address = if (P) modified_base else base;
|
||||
|
||||
var result: u32 = undefined;
|
||||
if (L) {
|
||||
switch (@truncate(u2, opcode >> 5)) {
|
||||
0b01 => {
|
||||
// LDRH
|
||||
const value = bus.read(u16, address);
|
||||
result = rotr(u32, value, 8 * (address & 1));
|
||||
},
|
||||
0b10 => {
|
||||
// LDRSB
|
||||
result = sext(u32, u8, bus.read(u8, address));
|
||||
},
|
||||
0b11 => {
|
||||
// LDRSH
|
||||
result = if (address & 1 == 1) blk: {
|
||||
break :blk sext(u32, u8, bus.read(u8, address));
|
||||
} else blk: {
|
||||
break :blk sext(u32, u16, bus.read(u16, address));
|
||||
};
|
||||
},
|
||||
0b00 => unreachable, // SWP
|
||||
}
|
||||
} else {
|
||||
if (opcode >> 5 & 0x01 == 0x01) {
|
||||
// STRH
|
||||
bus.write(u16, address, @truncate(u16, cpu.r[rd]));
|
||||
} else unreachable; // SWP
|
||||
}
|
||||
|
||||
address = modified_base;
|
||||
if (W and P or !P) cpu.r[rn] = address;
|
||||
if (L) cpu.r[rd] = result; // // This emulates the LDR rd == rn behaviour
|
||||
}
|
||||
}.inner;
|
||||
}
|
57
src/core/cpu/arm/multiply.zig
Normal file
57
src/core/cpu/arm/multiply.zig
Normal file
@@ -0,0 +1,57 @@
|
||||
const Bus = @import("../../Bus.zig");
|
||||
const Arm7tdmi = @import("../../cpu.zig").Arm7tdmi;
|
||||
const InstrFn = @import("../../cpu.zig").ArmInstrFn;
|
||||
|
||||
pub fn multiply(comptime A: bool, comptime S: bool) InstrFn {
|
||||
return struct {
|
||||
fn inner(cpu: *Arm7tdmi, _: *Bus, opcode: u32) void {
|
||||
const rd = opcode >> 16 & 0xF;
|
||||
const rn = opcode >> 12 & 0xF;
|
||||
const rs = opcode >> 8 & 0xF;
|
||||
const rm = opcode & 0xF;
|
||||
|
||||
const temp: u64 = @as(u64, cpu.r[rm]) * @as(u64, cpu.r[rs]) + if (A) cpu.r[rn] else 0;
|
||||
const result = @truncate(u32, temp);
|
||||
cpu.r[rd] = result;
|
||||
|
||||
if (S) {
|
||||
cpu.cpsr.n.write(result >> 31 & 1 == 1);
|
||||
cpu.cpsr.z.write(result == 0);
|
||||
// V is unaffected, C is *actually* undefined in ARMv4
|
||||
}
|
||||
}
|
||||
}.inner;
|
||||
}
|
||||
|
||||
pub fn multiplyLong(comptime U: bool, comptime A: bool, comptime S: bool) InstrFn {
|
||||
return struct {
|
||||
fn inner(cpu: *Arm7tdmi, _: *Bus, opcode: u32) void {
|
||||
const rd_hi = opcode >> 16 & 0xF;
|
||||
const rd_lo = opcode >> 12 & 0xF;
|
||||
const rs = opcode >> 8 & 0xF;
|
||||
const rm = opcode & 0xF;
|
||||
|
||||
if (U) {
|
||||
// Signed (WHY IS IT U THEN?)
|
||||
var result: i64 = @as(i64, @bitCast(i32, cpu.r[rm])) * @as(i64, @bitCast(i32, cpu.r[rs]));
|
||||
if (A) result +%= @bitCast(i64, @as(u64, cpu.r[rd_hi]) << 32 | @as(u64, cpu.r[rd_lo]));
|
||||
|
||||
cpu.r[rd_hi] = @bitCast(u32, @truncate(i32, result >> 32));
|
||||
cpu.r[rd_lo] = @bitCast(u32, @truncate(i32, result));
|
||||
} else {
|
||||
// Unsigned
|
||||
var result: u64 = @as(u64, cpu.r[rm]) * @as(u64, cpu.r[rs]);
|
||||
if (A) result +%= @as(u64, cpu.r[rd_hi]) << 32 | @as(u64, cpu.r[rd_lo]);
|
||||
|
||||
cpu.r[rd_hi] = @truncate(u32, result >> 32);
|
||||
cpu.r[rd_lo] = @truncate(u32, result);
|
||||
}
|
||||
|
||||
if (S) {
|
||||
cpu.cpsr.z.write(cpu.r[rd_hi] == 0 and cpu.r[rd_lo] == 0);
|
||||
cpu.cpsr.n.write(cpu.r[rd_hi] >> 31 & 1 == 1);
|
||||
// C and V are set to meaningless values
|
||||
}
|
||||
}
|
||||
}.inner;
|
||||
}
|
59
src/core/cpu/arm/psr_transfer.zig
Normal file
59
src/core/cpu/arm/psr_transfer.zig
Normal file
@@ -0,0 +1,59 @@
|
||||
const std = @import("std");
|
||||
|
||||
const Bus = @import("../../Bus.zig");
|
||||
const Arm7tdmi = @import("../../cpu.zig").Arm7tdmi;
|
||||
const InstrFn = @import("../../cpu.zig").ArmInstrFn;
|
||||
const PSR = @import("../../cpu.zig").PSR;
|
||||
|
||||
const log = std.log.scoped(.PsrTransfer);
|
||||
|
||||
const rotr = @import("../../util.zig").rotr;
|
||||
|
||||
pub fn psrTransfer(comptime I: bool, comptime R: bool, comptime kind: u2) InstrFn {
|
||||
return struct {
|
||||
fn inner(cpu: *Arm7tdmi, _: *Bus, opcode: u32) void {
|
||||
switch (kind) {
|
||||
0b00 => {
|
||||
// MRS
|
||||
const rd = opcode >> 12 & 0xF;
|
||||
|
||||
if (R and !cpu.hasSPSR()) log.err("Tried to read SPSR from User/System Mode", .{});
|
||||
cpu.r[rd] = if (R) cpu.spsr.raw else cpu.cpsr.raw;
|
||||
},
|
||||
0b10 => {
|
||||
// MSR
|
||||
const field_mask = @truncate(u4, opcode >> 16 & 0xF);
|
||||
const rm_idx = opcode & 0xF;
|
||||
const right = if (I) rotr(u32, opcode & 0xFF, (opcode >> 8 & 0xF) * 2) else cpu.r[rm_idx];
|
||||
|
||||
if (R and !cpu.hasSPSR()) log.err("Tried to write to SPSR in User/System Mode", .{});
|
||||
|
||||
if (R) {
|
||||
// arm.gba seems to expect the SPSR to do somethign in SYS mode,
|
||||
// so we just assume that despite writing to the SPSR in USR or SYS mode
|
||||
// being UNPREDICTABLE, it just magically has a working SPSR somehow
|
||||
cpu.spsr.raw = fieldMask(&cpu.spsr, field_mask, right);
|
||||
} else {
|
||||
if (cpu.isPrivileged()) cpu.setCpsr(fieldMask(&cpu.cpsr, field_mask, right));
|
||||
}
|
||||
},
|
||||
else => cpu.panic("[CPU/PSR Transfer] Bits 21:220 of {X:0>8} are undefined", .{opcode}),
|
||||
}
|
||||
}
|
||||
}.inner;
|
||||
}
|
||||
|
||||
fn fieldMask(psr: *const PSR, field_mask: u4, right: u32) u32 {
|
||||
// This bitwise ORs bits 3 and 0 of the field mask into a u2
|
||||
// We do this because we only care about bits 7:0 and 31:28 of the CPSR
|
||||
const bits = @truncate(u2, (field_mask >> 2 & 0x2) | (field_mask & 1));
|
||||
|
||||
const mask: u32 = switch (bits) {
|
||||
0b00 => 0x0000_0000,
|
||||
0b01 => 0x0000_00FF,
|
||||
0b10 => 0xF000_0000,
|
||||
0b11 => 0xF000_00FF,
|
||||
};
|
||||
|
||||
return (psr.raw & ~mask) | (right & mask);
|
||||
}
|
31
src/core/cpu/arm/single_data_swap.zig
Normal file
31
src/core/cpu/arm/single_data_swap.zig
Normal file
@@ -0,0 +1,31 @@
|
||||
const std = @import("std");
|
||||
|
||||
const Bus = @import("../../Bus.zig");
|
||||
const Arm7tdmi = @import("../../cpu.zig").Arm7tdmi;
|
||||
const InstrFn = @import("../../cpu.zig").ArmInstrFn;
|
||||
|
||||
const rotr = @import("../../util.zig").rotr;
|
||||
|
||||
pub fn singleDataSwap(comptime B: bool) InstrFn {
|
||||
return struct {
|
||||
fn inner(cpu: *Arm7tdmi, bus: *Bus, opcode: u32) void {
|
||||
const rn = opcode >> 16 & 0xF;
|
||||
const rd = opcode >> 12 & 0xF;
|
||||
const rm = opcode & 0xF;
|
||||
|
||||
const address = cpu.r[rn];
|
||||
|
||||
if (B) {
|
||||
// SWPB
|
||||
const value = bus.read(u8, address);
|
||||
bus.write(u8, address, @truncate(u8, cpu.r[rm]));
|
||||
cpu.r[rd] = value;
|
||||
} else {
|
||||
// SWP
|
||||
const value = rotr(u32, bus.read(u32, address), 8 * (address & 0x3));
|
||||
bus.write(u32, address, cpu.r[rm]);
|
||||
cpu.r[rd] = value;
|
||||
}
|
||||
}
|
||||
}.inner;
|
||||
}
|
57
src/core/cpu/arm/single_data_transfer.zig
Normal file
57
src/core/cpu/arm/single_data_transfer.zig
Normal file
@@ -0,0 +1,57 @@
|
||||
const std = @import("std");
|
||||
const util = @import("../../util.zig");
|
||||
|
||||
const shifter = @import("../barrel_shifter.zig");
|
||||
const Bus = @import("../../Bus.zig");
|
||||
const Arm7tdmi = @import("../../cpu.zig").Arm7tdmi;
|
||||
const InstrFn = @import("../../cpu.zig").ArmInstrFn;
|
||||
|
||||
const rotr = @import("../../util.zig").rotr;
|
||||
|
||||
pub fn singleDataTransfer(comptime I: bool, comptime P: bool, comptime U: bool, comptime B: bool, comptime W: bool, comptime L: bool) InstrFn {
|
||||
return struct {
|
||||
fn inner(cpu: *Arm7tdmi, bus: *Bus, opcode: u32) void {
|
||||
const rn = opcode >> 16 & 0xF;
|
||||
const rd = opcode >> 12 & 0xF;
|
||||
|
||||
var base: u32 = undefined;
|
||||
if (rn == 0xF) {
|
||||
base = cpu.fakePC();
|
||||
if (!L) base += 4; // Offset of 12
|
||||
} else {
|
||||
base = cpu.r[rn];
|
||||
}
|
||||
|
||||
const offset = if (I) shifter.immShift(false, cpu, opcode) else opcode & 0xFFF;
|
||||
|
||||
const modified_base = if (U) base +% offset else base -% offset;
|
||||
var address = if (P) modified_base else base;
|
||||
|
||||
var result: u32 = undefined;
|
||||
if (L) {
|
||||
if (B) {
|
||||
// LDRB
|
||||
result = bus.read(u8, address);
|
||||
} else {
|
||||
// LDR
|
||||
const value = bus.read(u32, address);
|
||||
result = rotr(u32, value, 8 * (address & 0x3));
|
||||
}
|
||||
} else {
|
||||
if (B) {
|
||||
// STRB
|
||||
const value = if (rd == 0xF) cpu.r[rd] + 8 else cpu.r[rd];
|
||||
bus.write(u8, address, @truncate(u8, value));
|
||||
} else {
|
||||
// STR
|
||||
const value = if (rd == 0xF) cpu.r[rd] + 8 else cpu.r[rd];
|
||||
bus.write(u32, address, value);
|
||||
}
|
||||
}
|
||||
|
||||
address = modified_base;
|
||||
if (W and P or !P) cpu.r[rn] = address;
|
||||
if (L) cpu.r[rd] = result; // This emulates the LDR rd == rn behaviour
|
||||
}
|
||||
}.inner;
|
||||
}
|
22
src/core/cpu/arm/software_interrupt.zig
Normal file
22
src/core/cpu/arm/software_interrupt.zig
Normal file
@@ -0,0 +1,22 @@
|
||||
const Bus = @import("../../Bus.zig");
|
||||
const Arm7tdmi = @import("../../cpu.zig").Arm7tdmi;
|
||||
const InstrFn = @import("../../cpu.zig").ArmInstrFn;
|
||||
|
||||
pub fn armSoftwareInterrupt() InstrFn {
|
||||
return struct {
|
||||
fn inner(cpu: *Arm7tdmi, _: *Bus, _: u32) void {
|
||||
// Copy Values from Current Mode
|
||||
const r15 = cpu.r[15];
|
||||
const cpsr = cpu.cpsr.raw;
|
||||
|
||||
// Switch Mode
|
||||
cpu.changeMode(.Supervisor);
|
||||
cpu.cpsr.t.write(false); // Force ARM Mode
|
||||
cpu.cpsr.i.write(true); // Disable normal interrupts
|
||||
|
||||
cpu.r[14] = r15; // Resume Execution
|
||||
cpu.spsr.raw = cpsr; // Previous mode CPSR
|
||||
cpu.r[15] = 0x0000_0008;
|
||||
}
|
||||
}.inner;
|
||||
}
|
Reference in New Issue
Block a user