chore: group THUMB and select ARM instructions together (same file)
This commit is contained in:
		
							
								
								
									
										45
									
								
								src/cpu.zig
									
									
									
									
									
								
							
							
						
						
									
										45
									
								
								src/cpu.zig
									
									
									
									
									
								
							| @@ -17,29 +17,34 @@ const blockDataTransfer = @import("cpu/arm/block_data_transfer.zig").blockDataTr | ||||
| const branch = @import("cpu/arm/branch.zig").branch; | ||||
| const branchAndExchange = @import("cpu/arm/branch.zig").branchAndExchange; | ||||
| const softwareInterrupt = @import("cpu/arm/software_interrupt.zig").softwareInterrupt; | ||||
| const multiply = @import("cpu/arm/multiply.zig").multiply; | ||||
| const multiplyLong = @import("cpu/arm/multiply_long.zig").multiplyLong; | ||||
| const singleDataSwap = @import("cpu/arm/single_data_swap.zig").singleDataSwap; | ||||
|  | ||||
| const multiply = @import("cpu/arm/multiply.zig").multiply; | ||||
| const multiplyLong = @import("cpu/arm/multiply.zig").multiplyLong; | ||||
|  | ||||
| // THUMB Instruction Groups | ||||
| const format1 = @import("cpu/thumb/format1.zig").format1; | ||||
| const format2 = @import("cpu/thumb/format2.zig").format2; | ||||
| const format3 = @import("cpu/thumb/format3.zig").format3; | ||||
| const format4 = @import("cpu/thumb/format4.zig").format4; | ||||
| const format5 = @import("cpu/thumb/format5.zig").format5; | ||||
| const format6 = @import("cpu/thumb/format6.zig").format6; | ||||
| const format78 = @import("cpu/thumb/format78.zig").format78; | ||||
| const format9 = @import("cpu/thumb/format9.zig").format9; | ||||
| const format10 = @import("cpu/thumb/format10.zig").format10; | ||||
| const format11 = @import("cpu/thumb/format11.zig").format11; | ||||
| const format12 = @import("cpu/thumb/format12.zig").format12; | ||||
| const format13 = @import("cpu/thumb/format13.zig").format13; | ||||
| const format14 = @import("cpu/thumb/format14.zig").format14; | ||||
| const format15 = @import("cpu/thumb/format15.zig").format15; | ||||
| const format16 = @import("cpu/thumb/format16.zig").format16; | ||||
| const format17 = @import("cpu/thumb/format17.zig").format17; | ||||
| const format18 = @import("cpu/thumb/format18.zig").format18; | ||||
| const format19 = @import("cpu/thumb/format19.zig").format19; | ||||
| const format1 = @import("cpu/thumb/data_processing.zig").format1; | ||||
| const format2 = @import("cpu/thumb/data_processing.zig").format2; | ||||
| const format3 = @import("cpu/thumb/data_processing.zig").format3; | ||||
| const format12 = @import("cpu/thumb/data_processing.zig").format12; | ||||
| const format13 = @import("cpu/thumb/data_processing.zig").format13; | ||||
|  | ||||
| const format4 = @import("cpu/thumb/alu.zig").format4; | ||||
| const format5 = @import("cpu/thumb/processing_branch.zig").format5; | ||||
|  | ||||
| const format6 = @import("cpu/thumb/data_transfer.zig").format6; | ||||
| const format78 = @import("cpu/thumb/data_transfer.zig").format78; | ||||
| const format9 = @import("cpu/thumb/data_transfer.zig").format9; | ||||
| const format10 = @import("cpu/thumb/data_transfer.zig").format10; | ||||
| const format11 = @import("cpu/thumb/data_transfer.zig").format11; | ||||
| const format14 = @import("cpu/thumb/block_data_transfer.zig").format14; | ||||
| const format15 = @import("cpu/thumb/block_data_transfer.zig").format15; | ||||
|  | ||||
| const format16 = @import("cpu/thumb/branch.zig").format16; | ||||
| const format18 = @import("cpu/thumb/branch.zig").format18; | ||||
| const format19 = @import("cpu/thumb/branch.zig").format19; | ||||
|  | ||||
| const format17 = @import("cpu/thumb/software_interrupt.zig").format17; | ||||
|  | ||||
| pub const ArmInstrFn = fn (*Arm7tdmi, *Bus, u32) void; | ||||
| pub const ThumbInstrFn = fn (*Arm7tdmi, *Bus, u16) void; | ||||
|   | ||||
| @@ -24,3 +24,36 @@ pub fn multiply(comptime A: bool, comptime S: bool) InstrFn { | ||||
|         } | ||||
|     }.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; | ||||
| } | ||||
|   | ||||
| @@ -1,38 +0,0 @@ | ||||
| const std = @import("std"); | ||||
|  | ||||
| const Bus = @import("../../Bus.zig"); | ||||
| const Arm7tdmi = @import("../../cpu.zig").Arm7tdmi; | ||||
| const InstrFn = @import("../../cpu.zig").ArmInstrFn; | ||||
|  | ||||
| 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; | ||||
| } | ||||
| @@ -1,5 +1,3 @@ | ||||
| const std = @import("std"); | ||||
| 
 | ||||
| const Bus = @import("../../Bus.zig"); | ||||
| const Arm7tdmi = @import("../../cpu.zig").Arm7tdmi; | ||||
| const InstrFn = @import("../../cpu.zig").ThumbInstrFn; | ||||
| @@ -48,3 +46,27 @@ pub fn format14(comptime L: bool, comptime R: bool) InstrFn { | ||||
|         } | ||||
|     }.inner; | ||||
| } | ||||
| 
 | ||||
| pub fn format15(comptime L: bool, comptime rb: u3) InstrFn { | ||||
|     return struct { | ||||
|         fn inner(cpu: *Arm7tdmi, bus: *Bus, opcode: u16) void { | ||||
|             const base = cpu.r[rb]; | ||||
| 
 | ||||
|             var address: u32 = base; | ||||
| 
 | ||||
|             var i: usize = 0; | ||||
|             while (i < 8) : (i += 1) { | ||||
|                 if ((opcode >> @truncate(u3, i)) & 1 == 1) { | ||||
|                     if (L) { | ||||
|                         cpu.r[i] = bus.read32(address); | ||||
|                     } else { | ||||
|                         bus.write32(address, cpu.r[i]); | ||||
|                     } | ||||
|                     address += 4; | ||||
|                 } | ||||
|             } | ||||
| 
 | ||||
|             cpu.r[rb] = address; | ||||
|         } | ||||
|     }.inner; | ||||
| } | ||||
							
								
								
									
										54
									
								
								src/cpu/thumb/branch.zig
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										54
									
								
								src/cpu/thumb/branch.zig
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,54 @@ | ||||
| const Bus = @import("../../Bus.zig"); | ||||
| const Arm7tdmi = @import("../../cpu.zig").Arm7tdmi; | ||||
| const InstrFn = @import("../../cpu.zig").ThumbInstrFn; | ||||
|  | ||||
| const checkCond = @import("../../cpu.zig").checkCond; | ||||
| const u32SignExtend = @import("../../util.zig").u32SignExtend; | ||||
|  | ||||
| pub fn format16(comptime cond: u4) InstrFn { | ||||
|     return struct { | ||||
|         fn inner(cpu: *Arm7tdmi, _: *Bus, opcode: u16) void { | ||||
|             // B | ||||
|             const offset = u32SignExtend(8, opcode & 0xFF) << 1; | ||||
|  | ||||
|             const should_execute = switch (cond) { | ||||
|                 0xE, 0xF => cpu.panic("[CPU/THUMB] Undefined conditional branch with condition {}", .{cond}), | ||||
|                 else => checkCond(cpu.cpsr, cond), | ||||
|             }; | ||||
|  | ||||
|             if (should_execute) { | ||||
|                 cpu.r[15] = (cpu.r[15] + 2) +% offset; | ||||
|             } | ||||
|         } | ||||
|     }.inner; | ||||
| } | ||||
|  | ||||
| pub fn format18() InstrFn { | ||||
|     return struct { | ||||
|         // B but conditional | ||||
|         fn inner(cpu: *Arm7tdmi, _: *Bus, opcode: u16) void { | ||||
|             const offset = u32SignExtend(11, opcode & 0x7FF) << 1; | ||||
|             cpu.r[15] = (cpu.r[15] + 2) +% offset; | ||||
|         } | ||||
|     }.inner; | ||||
| } | ||||
|  | ||||
| pub fn format19(comptime is_low: bool) InstrFn { | ||||
|     return struct { | ||||
|         fn inner(cpu: *Arm7tdmi, _: *Bus, opcode: u16) void { | ||||
|             // BL | ||||
|             const offset = opcode & 0x7FF; | ||||
|  | ||||
|             if (is_low) { | ||||
|                 // Instruction 2 | ||||
|                 const old_pc = cpu.r[15]; | ||||
|  | ||||
|                 cpu.r[15] = cpu.r[14] + (offset << 1); | ||||
|                 cpu.r[14] = old_pc | 1; | ||||
|             } else { | ||||
|                 // Instruction 1 | ||||
|                 cpu.r[14] = (cpu.r[15] + 2) +% (u32SignExtend(11, @as(u32, offset)) << 12); | ||||
|             } | ||||
|         } | ||||
|     }.inner; | ||||
| } | ||||
							
								
								
									
										118
									
								
								src/cpu/thumb/data_processing.zig
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										118
									
								
								src/cpu/thumb/data_processing.zig
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,118 @@ | ||||
| const Bus = @import("../../Bus.zig"); | ||||
| const Arm7tdmi = @import("../../cpu.zig").Arm7tdmi; | ||||
| const InstrFn = @import("../../cpu.zig").ThumbInstrFn; | ||||
| const shifter = @import("../barrel_shifter.zig"); | ||||
|  | ||||
| const add = @import("../arm/data_processing.zig").add; | ||||
| const sub = @import("../arm/data_processing.zig").sub; | ||||
| const cmp = @import("../arm/data_processing.zig").cmp; | ||||
| const setLogicOpFlags = @import("../arm/data_processing.zig").setLogicOpFlags; | ||||
|  | ||||
| pub fn format1(comptime op: u2, comptime offset: u5) InstrFn { | ||||
|     return struct { | ||||
|         fn inner(cpu: *Arm7tdmi, _: *Bus, opcode: u16) void { | ||||
|             const rs = opcode >> 3 & 0x7; | ||||
|             const rd = opcode & 0x7; | ||||
|  | ||||
|             const result = switch (op) { | ||||
|                 0b00 => blk: { | ||||
|                     // LSL | ||||
|                     if (offset == 0) { | ||||
|                         break :blk cpu.r[rs]; | ||||
|                     } else { | ||||
|                         break :blk shifter.logicalLeft(true, &cpu.cpsr, cpu.r[rs], offset); | ||||
|                     } | ||||
|                 }, | ||||
|                 0b01 => blk: { | ||||
|                     // LSR | ||||
|                     if (offset == 0) { | ||||
|                         cpu.cpsr.c.write(cpu.r[rs] >> 31 & 1 == 1); | ||||
|                         break :blk @as(u32, 0); | ||||
|                     } else { | ||||
|                         break :blk shifter.logicalRight(true, &cpu.cpsr, cpu.r[rs], offset); | ||||
|                     } | ||||
|                 }, | ||||
|                 0b10 => blk: { | ||||
|                     // ASR | ||||
|                     if (offset == 0) { | ||||
|                         cpu.cpsr.c.write(cpu.r[rs] >> 31 & 1 == 1); | ||||
|                         break :blk @bitCast(u32, @bitCast(i32, cpu.r[rs]) >> 31); | ||||
|                     } else { | ||||
|                         break :blk shifter.arithmeticRight(true, &cpu.cpsr, cpu.r[rs], offset); | ||||
|                     } | ||||
|                 }, | ||||
|                 else => cpu.panic("[CPU|THUMB|Fmt1] {} is an invalid op", .{op}), | ||||
|             }; | ||||
|  | ||||
|             // Equivalent to an ARM MOVS | ||||
|             cpu.r[rd] = result; | ||||
|             setLogicOpFlags(true, cpu, result); | ||||
|         } | ||||
|     }.inner; | ||||
| } | ||||
|  | ||||
| pub fn format2(comptime I: bool, is_sub: bool, rn: u3) InstrFn { | ||||
|     return struct { | ||||
|         fn inner(cpu: *Arm7tdmi, _: *Bus, opcode: u16) void { | ||||
|             const rs = opcode >> 3 & 0x7; | ||||
|             const rd = @truncate(u3, opcode); | ||||
|  | ||||
|             if (is_sub) { | ||||
|                 // SUB | ||||
|                 cpu.r[rd] = if (I) blk: { | ||||
|                     break :blk sub(true, cpu, cpu.r[rs], @as(u32, rn)); | ||||
|                 } else blk: { | ||||
|                     break :blk sub(true, cpu, cpu.r[rs], cpu.r[rn]); | ||||
|                 }; | ||||
|             } else { | ||||
|                 // ADD | ||||
|                 cpu.r[rd] = if (I) blk: { | ||||
|                     break :blk add(true, cpu, cpu.r[rs], @as(u32, rn)); | ||||
|                 } else blk: { | ||||
|                     break :blk add(true, cpu, cpu.r[rs], cpu.r[rn]); | ||||
|                 }; | ||||
|             } | ||||
|         } | ||||
|     }.inner; | ||||
| } | ||||
|  | ||||
| pub fn format3(comptime op: u2, comptime rd: u3) InstrFn { | ||||
|     return struct { | ||||
|         fn inner(cpu: *Arm7tdmi, _: *Bus, opcode: u16) void { | ||||
|             const offset = @truncate(u8, opcode); | ||||
|  | ||||
|             switch (op) { | ||||
|                 0b00 => { | ||||
|                     // MOV | ||||
|                     cpu.r[rd] = offset; | ||||
|                     setLogicOpFlags(true, cpu, offset); | ||||
|                 }, | ||||
|                 0b01 => cmp(cpu, cpu.r[rd], offset), // CMP | ||||
|                 0b10 => cpu.r[rd] = add(true, cpu, cpu.r[rd], offset), // ADD | ||||
|                 0b11 => cpu.r[rd] = sub(true, cpu, cpu.r[rd], offset), // SUB | ||||
|             } | ||||
|         } | ||||
|     }.inner; | ||||
| } | ||||
|  | ||||
| pub fn format12(comptime isSP: bool, comptime rd: u3) InstrFn { | ||||
|     return struct { | ||||
|         fn inner(cpu: *Arm7tdmi, _: *Bus, opcode: u16) void { | ||||
|             // ADD | ||||
|             const left = if (isSP) cpu.r[13] else (cpu.r[15] + 2) & 0xFFFF_FFFD; | ||||
|             const right = (opcode & 0xFF) << 2; | ||||
|             const result = left + right; // TODO: What about overflows? | ||||
|             cpu.r[rd] = result; | ||||
|         } | ||||
|     }.inner; | ||||
| } | ||||
|  | ||||
| pub fn format13(comptime S: bool) InstrFn { | ||||
|     return struct { | ||||
|         fn inner(cpu: *Arm7tdmi, _: *Bus, opcode: u16) void { | ||||
|             // ADD | ||||
|             const offset = (opcode & 0x7F) << 2; | ||||
|             cpu.r[13] = if (S) cpu.r[13] - offset else cpu.r[13] + offset; | ||||
|         } | ||||
|     }.inner; | ||||
| } | ||||
							
								
								
									
										143
									
								
								src/cpu/thumb/data_transfer.zig
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										143
									
								
								src/cpu/thumb/data_transfer.zig
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,143 @@ | ||||
| const std = @import("std"); | ||||
|  | ||||
| const Bus = @import("../../Bus.zig"); | ||||
| const Arm7tdmi = @import("../../cpu.zig").Arm7tdmi; | ||||
| const InstrFn = @import("../../cpu.zig").ThumbInstrFn; | ||||
|  | ||||
| pub fn format6(comptime rd: u3) InstrFn { | ||||
|     return struct { | ||||
|         fn inner(cpu: *Arm7tdmi, bus: *Bus, opcode: u16) void { | ||||
|             // LDR | ||||
|             const offset = (opcode & 0xFF) << 2; | ||||
|  | ||||
|             // FIXME: Should this overflow? | ||||
|             cpu.r[rd] = bus.read32((cpu.r[15] + 2 & 0xFFFF_FFFD) + offset); | ||||
|         } | ||||
|     }.inner; | ||||
| } | ||||
|  | ||||
| const u32SignExtend = @import("../../util.zig").u32SignExtend; | ||||
|  | ||||
| pub fn format78(comptime op: u2, comptime T: bool) InstrFn { | ||||
|     return struct { | ||||
|         fn inner(cpu: *Arm7tdmi, bus: *Bus, opcode: u16) void { | ||||
|             const ro = opcode >> 6 & 0x7; | ||||
|             const rb = opcode >> 3 & 0x7; | ||||
|             const rd = opcode & 0x7; | ||||
|  | ||||
|             const address = cpu.r[rb] + cpu.r[ro]; | ||||
|  | ||||
|             if (T) { | ||||
|                 switch (op) { | ||||
|                     0b00 => { | ||||
|                         // STRH | ||||
|                         bus.write16(address & 0xFFFF_FFFE, @truncate(u16, cpu.r[rd])); | ||||
|                     }, | ||||
|                     0b01 => { | ||||
|                         // LDRH | ||||
|                         const value = bus.read16(address & 0xFFFF_FFFE); | ||||
|                         cpu.r[rd] = std.math.rotr(u32, @as(u32, value), 8 * (address & 1)); | ||||
|                     }, | ||||
|                     0b10 => { | ||||
|                         // LDSB | ||||
|                         cpu.r[rd] = u32SignExtend(8, @as(u32, bus.read8(address))); | ||||
|                     }, | ||||
|                     0b11 => { | ||||
|                         // LDSH | ||||
|                         cpu.r[rd] = u32SignExtend(16, @as(u32, bus.read16(address & 0xFFFF_FFFE))); | ||||
|                     }, | ||||
|                 } | ||||
|             } else { | ||||
|                 switch (op) { | ||||
|                     0b00 => { | ||||
|                         // STR | ||||
|                         bus.write32(address & 0xFFFF_FFFC, cpu.r[rd]); | ||||
|                     }, | ||||
|                     0b01 => { | ||||
|                         // STRB | ||||
|                         bus.write8(address, @truncate(u8, cpu.r[rd])); | ||||
|                     }, | ||||
|                     0b10 => { | ||||
|                         // LDR | ||||
|                         const value = bus.read32(address & 0xFFFF_FFFC); | ||||
|                         cpu.r[rd] = std.math.rotr(u32, value, 8 * (address & 0x3)); | ||||
|                     }, | ||||
|                     0b11 => { | ||||
|                         // LDRB | ||||
|                         cpu.r[rd] = bus.read8(address); | ||||
|                     }, | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|     }.inner; | ||||
| } | ||||
|  | ||||
| pub fn format9(comptime B: bool, comptime L: bool, comptime offset: u5) InstrFn { | ||||
|     return struct { | ||||
|         fn inner(cpu: *Arm7tdmi, bus: *Bus, opcode: u16) void { | ||||
|             const rb = opcode >> 3 & 0x7; | ||||
|             const rd = opcode & 0x7; | ||||
|  | ||||
|             if (L) { | ||||
|                 if (B) { | ||||
|                     // LDRB | ||||
|                     const address = cpu.r[rb] + offset; | ||||
|                     cpu.r[rd] = bus.read8(address); | ||||
|                 } else { | ||||
|                     // LDR | ||||
|                     const address = cpu.r[rb] + (@as(u32, offset) << 2); | ||||
|                     const value = bus.read32(address & 0xFFFF_FFFC); | ||||
|                     cpu.r[rd] = std.math.rotr(u32, value, 8 * (address & 0x3)); | ||||
|                 } | ||||
|             } else { | ||||
|                 if (B) { | ||||
|                     // STRB | ||||
|                     const address = cpu.r[rb] + offset; | ||||
|                     bus.write8(address, @truncate(u8, cpu.r[rd])); | ||||
|                 } else { | ||||
|                     // STR | ||||
|                     const address = cpu.r[rb] + (@as(u32, offset) << 2); | ||||
|                     bus.write32(address & 0xFFFF_FFFC, cpu.r[rd]); | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|     }.inner; | ||||
| } | ||||
|  | ||||
| pub fn format10(comptime L: bool, comptime offset: u5) InstrFn { | ||||
|     return struct { | ||||
|         fn inner(cpu: *Arm7tdmi, bus: *Bus, opcode: u16) void { | ||||
|             const rb = opcode >> 3 & 0x7; | ||||
|             const rd = opcode & 0x7; | ||||
|  | ||||
|             const address = cpu.r[rb] + (offset << 1); | ||||
|  | ||||
|             if (L) { | ||||
|                 // LDRH | ||||
|                 const value = bus.read16(address & 0xFFFF_FFFE); | ||||
|                 cpu.r[rd] = std.math.rotr(u32, @as(u32, value), 8 * (address & 1)); | ||||
|             } else { | ||||
|                 // STRH | ||||
|                 bus.write16(address & 0xFFFF_FFFE, @truncate(u16, cpu.r[rd])); | ||||
|             } | ||||
|         } | ||||
|     }.inner; | ||||
| } | ||||
|  | ||||
| pub fn format11(comptime L: bool, comptime rd: u3) InstrFn { | ||||
|     return struct { | ||||
|         fn inner(cpu: *Arm7tdmi, bus: *Bus, opcode: u16) void { | ||||
|             const offset = (opcode & 0xFF) << 2; | ||||
|             const address = cpu.r[13] + offset; | ||||
|  | ||||
|             if (L) { | ||||
|                 // LDR | ||||
|                 const value = bus.read32(address & 0xFFFF_FFFC); | ||||
|                 cpu.r[rd] = std.math.rotr(u32, value, 8 * (address & 0x3)); | ||||
|             } else { | ||||
|                 // STR | ||||
|                 bus.write32(address & 0xFFFF_FFFC, cpu.r[rd]); | ||||
|             } | ||||
|         } | ||||
|     }.inner; | ||||
| } | ||||
| @@ -1,51 +0,0 @@ | ||||
| const std = @import("std"); | ||||
|  | ||||
| const Bus = @import("../../Bus.zig"); | ||||
| const Arm7tdmi = @import("../../cpu.zig").Arm7tdmi; | ||||
| const InstrFn = @import("../../cpu.zig").ThumbInstrFn; | ||||
| const shifter = @import("../barrel_shifter.zig"); | ||||
|  | ||||
| const setLogicOpFlags = @import("../arm/data_processing.zig").setLogicOpFlags; | ||||
|  | ||||
| pub fn format1(comptime op: u2, comptime offset: u5) InstrFn { | ||||
|     return struct { | ||||
|         fn inner(cpu: *Arm7tdmi, _: *Bus, opcode: u16) void { | ||||
|             const rs = opcode >> 3 & 0x7; | ||||
|             const rd = opcode & 0x7; | ||||
|  | ||||
|             const result = switch (op) { | ||||
|                 0b00 => blk: { | ||||
|                     // LSL | ||||
|                     if (offset == 0) { | ||||
|                         break :blk cpu.r[rs]; | ||||
|                     } else { | ||||
|                         break :blk shifter.logicalLeft(true, &cpu.cpsr, cpu.r[rs], offset); | ||||
|                     } | ||||
|                 }, | ||||
|                 0b01 => blk: { | ||||
|                     // LSR | ||||
|                     if (offset == 0) { | ||||
|                         cpu.cpsr.c.write(cpu.r[rs] >> 31 & 1 == 1); | ||||
|                         break :blk @as(u32, 0); | ||||
|                     } else { | ||||
|                         break :blk shifter.logicalRight(true, &cpu.cpsr, cpu.r[rs], offset); | ||||
|                     } | ||||
|                 }, | ||||
|                 0b10 => blk: { | ||||
|                     // ASR | ||||
|                     if (offset == 0) { | ||||
|                         cpu.cpsr.c.write(cpu.r[rs] >> 31 & 1 == 1); | ||||
|                         break :blk @bitCast(u32, @bitCast(i32, cpu.r[rs]) >> 31); | ||||
|                     } else { | ||||
|                         break :blk shifter.arithmeticRight(true, &cpu.cpsr, cpu.r[rs], offset); | ||||
|                     } | ||||
|                 }, | ||||
|                 else => cpu.panic("[CPU|THUMB|Fmt1] {} is an invalid op", .{op}), | ||||
|             }; | ||||
|  | ||||
|             // Equivalent to an ARM MOVS | ||||
|             cpu.r[rd] = result; | ||||
|             setLogicOpFlags(true, cpu, result); | ||||
|         } | ||||
|     }.inner; | ||||
| } | ||||
| @@ -1,25 +0,0 @@ | ||||
| const std = @import("std"); | ||||
|  | ||||
| const Bus = @import("../../Bus.zig"); | ||||
| const Arm7tdmi = @import("../../cpu.zig").Arm7tdmi; | ||||
| const InstrFn = @import("../../cpu.zig").ThumbInstrFn; | ||||
|  | ||||
| pub fn format10(comptime L: bool, comptime offset: u5) InstrFn { | ||||
|     return struct { | ||||
|         fn inner(cpu: *Arm7tdmi, bus: *Bus, opcode: u16) void { | ||||
|             const rb = opcode >> 3 & 0x7; | ||||
|             const rd = opcode & 0x7; | ||||
|  | ||||
|             const address = cpu.r[rb] + (offset << 1); | ||||
|  | ||||
|             if (L) { | ||||
|                 // LDRH | ||||
|                 const value = bus.read16(address & 0xFFFF_FFFE); | ||||
|                 cpu.r[rd] = std.math.rotr(u32, @as(u32, value), 8 * (address & 1)); | ||||
|             } else { | ||||
|                 // STRH | ||||
|                 bus.write16(address & 0xFFFF_FFFE, @truncate(u16, cpu.r[rd])); | ||||
|             } | ||||
|         } | ||||
|     }.inner; | ||||
| } | ||||
| @@ -1,21 +0,0 @@ | ||||
| const std = @import("std"); | ||||
|  | ||||
| const Bus = @import("../../Bus.zig"); | ||||
| const Arm7tdmi = @import("../../cpu.zig").Arm7tdmi; | ||||
| const InstrFn = @import("../../cpu.zig").ThumbInstrFn; | ||||
|  | ||||
| pub fn format11(comptime L: bool, comptime rd: u3) InstrFn { | ||||
|     return struct { | ||||
|         fn inner(cpu: *Arm7tdmi, bus: *Bus, opcode: u16) void { | ||||
|             const offset = (opcode & 0xFF) << 2; | ||||
|             const address = cpu.r[13] + offset; | ||||
|  | ||||
|             if (L) { | ||||
|                 const value = bus.read32(address & 0xFFFF_FFFC); | ||||
|                 cpu.r[rd] = std.math.rotr(u32, value, 8 * (address & 0x3)); | ||||
|             } else { | ||||
|                 bus.write32(address & 0xFFFF_FFFC, cpu.r[rd]); | ||||
|             } | ||||
|         } | ||||
|     }.inner; | ||||
| } | ||||
| @@ -1,17 +0,0 @@ | ||||
| const std = @import("std"); | ||||
|  | ||||
| const Bus = @import("../../Bus.zig"); | ||||
| const Arm7tdmi = @import("../../cpu.zig").Arm7tdmi; | ||||
| const InstrFn = @import("../../cpu.zig").ThumbInstrFn; | ||||
|  | ||||
| pub fn format12(comptime isSP: bool, comptime rd: u3) InstrFn { | ||||
|     return struct { | ||||
|         fn inner(cpu: *Arm7tdmi, _: *Bus, opcode: u16) void { | ||||
|             // ADD | ||||
|             const left = if (isSP) cpu.r[13] else (cpu.r[15] + 2) & 0xFFFF_FFFD; | ||||
|             const right = (opcode & 0xFF) << 2; | ||||
|             const result = left + right; // TODO: What about overflows? | ||||
|             cpu.r[rd] = result; | ||||
|         } | ||||
|     }.inner; | ||||
| } | ||||
| @@ -1,14 +0,0 @@ | ||||
| const std = @import("std"); | ||||
|  | ||||
| const Bus = @import("../../Bus.zig"); | ||||
| const Arm7tdmi = @import("../../cpu.zig").Arm7tdmi; | ||||
| const InstrFn = @import("../../cpu.zig").ThumbInstrFn; | ||||
|  | ||||
| pub fn format13(comptime S: bool) InstrFn { | ||||
|     return struct { | ||||
|         fn inner(cpu: *Arm7tdmi, _: *Bus, opcode: u16) void { | ||||
|             const offset = (opcode & 0x7F) << 2; | ||||
|             cpu.r[13] = if (S) cpu.r[13] - offset else cpu.r[13] + offset; | ||||
|         } | ||||
|     }.inner; | ||||
| } | ||||
| @@ -1,29 +0,0 @@ | ||||
| const std = @import("std"); | ||||
|  | ||||
| const Bus = @import("../../Bus.zig"); | ||||
| const Arm7tdmi = @import("../../cpu.zig").Arm7tdmi; | ||||
| const InstrFn = @import("../../cpu.zig").ThumbInstrFn; | ||||
|  | ||||
| pub fn format15(comptime L: bool, comptime rb: u3) InstrFn { | ||||
|     return struct { | ||||
|         fn inner(cpu: *Arm7tdmi, bus: *Bus, opcode: u16) void { | ||||
|             const base = cpu.r[rb]; | ||||
|  | ||||
|             var address: u32 = base; | ||||
|  | ||||
|             var i: usize = 0; | ||||
|             while (i < 8) : (i += 1) { | ||||
|                 if ((opcode >> @truncate(u3, i)) & 1 == 1) { | ||||
|                     if (L) { | ||||
|                         cpu.r[i] = bus.read32(address); | ||||
|                     } else { | ||||
|                         bus.write32(address, cpu.r[i]); | ||||
|                     } | ||||
|                     address += 4; | ||||
|                 } | ||||
|             } | ||||
|  | ||||
|             cpu.r[rb] = address; | ||||
|         } | ||||
|     }.inner; | ||||
| } | ||||
| @@ -1,26 +0,0 @@ | ||||
| const std = @import("std"); | ||||
|  | ||||
| const Bus = @import("../../Bus.zig"); | ||||
| const Arm7tdmi = @import("../../cpu.zig").Arm7tdmi; | ||||
| const InstrFn = @import("../../cpu.zig").ThumbInstrFn; | ||||
|  | ||||
| const checkCond = @import("../../cpu.zig").checkCond; | ||||
| const u32SignExtend = @import("../../util.zig").u32SignExtend; | ||||
|  | ||||
| pub fn format16(comptime cond: u4) InstrFn { | ||||
|     return struct { | ||||
|         fn inner(cpu: *Arm7tdmi, _: *Bus, opcode: u16) void { | ||||
|             // B | ||||
|             const offset = u32SignExtend(8, opcode & 0xFF) << 1; | ||||
|  | ||||
|             const should_execute = switch (cond) { | ||||
|                 0xE, 0xF => cpu.panic("[CPU/THUMB] Undefined conditional branch with condition {}", .{cond}), | ||||
|                 else => checkCond(cpu.cpsr, cond), | ||||
|             }; | ||||
|  | ||||
|             if (should_execute) { | ||||
|                 cpu.r[15] = (cpu.r[15] + 2) +% offset; | ||||
|             } | ||||
|         } | ||||
|     }.inner; | ||||
| } | ||||
| @@ -1,15 +0,0 @@ | ||||
| const std = @import("std"); | ||||
|  | ||||
| const Bus = @import("../../Bus.zig"); | ||||
| const Arm7tdmi = @import("../../cpu.zig").Arm7tdmi; | ||||
| const InstrFn = @import("../../cpu.zig").ThumbInstrFn; | ||||
| const u32SignExtend = @import("../../util.zig").u32SignExtend; | ||||
|  | ||||
| pub fn format18() InstrFn { | ||||
|     return struct { | ||||
|         fn inner(cpu: *Arm7tdmi, _: *Bus, opcode: u16) void { | ||||
|             const offset = u32SignExtend(11, opcode & 0x7FF) << 1; | ||||
|             cpu.r[15] = (cpu.r[15] + 2) +% offset; | ||||
|         } | ||||
|     }.inner; | ||||
| } | ||||
| @@ -1,26 +0,0 @@ | ||||
| const std = @import("std"); | ||||
|  | ||||
| const Bus = @import("../../Bus.zig"); | ||||
| const Arm7tdmi = @import("../../cpu.zig").Arm7tdmi; | ||||
| const InstrFn = @import("../../cpu.zig").ThumbInstrFn; | ||||
| const u32SignExtend = @import("../../util.zig").u32SignExtend; | ||||
|  | ||||
| pub fn format19(comptime is_low: bool) InstrFn { | ||||
|     return struct { | ||||
|         fn inner(cpu: *Arm7tdmi, _: *Bus, opcode: u16) void { | ||||
|             // BL | ||||
|             const offset = opcode & 0x7FF; | ||||
|  | ||||
|             if (is_low) { | ||||
|                 // Instruction 2 | ||||
|                 const old_pc = cpu.r[15]; | ||||
|  | ||||
|                 cpu.r[15] = cpu.r[14] + (offset << 1); | ||||
|                 cpu.r[14] = old_pc | 1; | ||||
|             } else { | ||||
|                 // Instruction 1 | ||||
|                 cpu.r[14] = (cpu.r[15] + 2) +% (u32SignExtend(11, @as(u32, offset)) << 12); | ||||
|             } | ||||
|         } | ||||
|     }.inner; | ||||
| } | ||||
| @@ -1,33 +0,0 @@ | ||||
| const std = @import("std"); | ||||
|  | ||||
| const Bus = @import("../../Bus.zig"); | ||||
| const Arm7tdmi = @import("../../cpu.zig").Arm7tdmi; | ||||
| const InstrFn = @import("../../cpu.zig").ThumbInstrFn; | ||||
|  | ||||
| const add = @import("../arm/data_processing.zig").add; | ||||
| const sub = @import("../arm/data_processing.zig").sub; | ||||
|  | ||||
| pub fn format2(comptime I: bool, is_sub: bool, rn: u3) InstrFn { | ||||
|     return struct { | ||||
|         fn inner(cpu: *Arm7tdmi, _: *Bus, opcode: u16) void { | ||||
|             const rs = opcode >> 3 & 0x7; | ||||
|             const rd = @truncate(u3, opcode); | ||||
|  | ||||
|             if (is_sub) { | ||||
|                 // SUB | ||||
|                 cpu.r[rd] = if (I) blk: { | ||||
|                     break :blk sub(true, cpu, cpu.r[rs], @as(u32, rn)); | ||||
|                 } else blk: { | ||||
|                     break :blk sub(true, cpu, cpu.r[rs], cpu.r[rn]); | ||||
|                 }; | ||||
|             } else { | ||||
|                 // ADD | ||||
|                 cpu.r[rd] = if (I) blk: { | ||||
|                     break :blk add(true, cpu, cpu.r[rs], @as(u32, rn)); | ||||
|                 } else blk: { | ||||
|                     break :blk add(true, cpu, cpu.r[rs], cpu.r[rn]); | ||||
|                 }; | ||||
|             } | ||||
|         } | ||||
|     }.inner; | ||||
| } | ||||
| @@ -1,29 +0,0 @@ | ||||
| const std = @import("std"); | ||||
|  | ||||
| const Bus = @import("../../Bus.zig"); | ||||
| const Arm7tdmi = @import("../../cpu.zig").Arm7tdmi; | ||||
| const InstrFn = @import("../../cpu.zig").ThumbInstrFn; | ||||
|  | ||||
| const add = @import("../arm/data_processing.zig").add; | ||||
| const sub = @import("../arm/data_processing.zig").sub; | ||||
| const cmp = @import("../arm/data_processing.zig").cmp; | ||||
| const setLogicOpFlags = @import("../arm/data_processing.zig").setLogicOpFlags; | ||||
|  | ||||
| pub fn format3(comptime op: u2, comptime rd: u3) InstrFn { | ||||
|     return struct { | ||||
|         fn inner(cpu: *Arm7tdmi, _: *Bus, opcode: u16) void { | ||||
|             const offset = @truncate(u8, opcode); | ||||
|  | ||||
|             switch (op) { | ||||
|                 0b00 => { | ||||
|                     // MOV | ||||
|                     cpu.r[rd] = offset; | ||||
|                     setLogicOpFlags(true, cpu, offset); | ||||
|                 }, | ||||
|                 0b01 => cmp(cpu, cpu.r[rd], offset), // CMP | ||||
|                 0b10 => cpu.r[rd] = add(true, cpu, cpu.r[rd], offset), // ADD | ||||
|                 0b11 => cpu.r[rd] = sub(true, cpu, cpu.r[rd], offset), // SUB | ||||
|             } | ||||
|         } | ||||
|     }.inner; | ||||
| } | ||||
| @@ -1,17 +0,0 @@ | ||||
| const std = @import("std"); | ||||
|  | ||||
| const Bus = @import("../../Bus.zig"); | ||||
| const Arm7tdmi = @import("../../cpu.zig").Arm7tdmi; | ||||
| const InstrFn = @import("../../cpu.zig").ThumbInstrFn; | ||||
|  | ||||
| pub fn format6(comptime rd: u3) InstrFn { | ||||
|     return struct { | ||||
|         fn inner(cpu: *Arm7tdmi, bus: *Bus, opcode: u16) void { | ||||
|             // LDR | ||||
|             const offset = (opcode & 0xFF) << 2; | ||||
|  | ||||
|             // FIXME: Should this overflow? | ||||
|             cpu.r[rd] = bus.read32((cpu.r[15] + 2 & 0xFFFF_FFFD) + offset); | ||||
|         } | ||||
|     }.inner; | ||||
| } | ||||
| @@ -1,60 +0,0 @@ | ||||
| const std = @import("std"); | ||||
|  | ||||
| const Bus = @import("../../Bus.zig"); | ||||
| const Arm7tdmi = @import("../../cpu.zig").Arm7tdmi; | ||||
| const InstrFn = @import("../../cpu.zig").ThumbInstrFn; | ||||
| const u32SignExtend = @import("../../util.zig").u32SignExtend; | ||||
|  | ||||
| pub fn format78(comptime op: u2, comptime T: bool) InstrFn { | ||||
|     return struct { | ||||
|         fn inner(cpu: *Arm7tdmi, bus: *Bus, opcode: u16) void { | ||||
|             const ro = opcode >> 6 & 0x7; | ||||
|             const rb = opcode >> 3 & 0x7; | ||||
|             const rd = opcode & 0x7; | ||||
|  | ||||
|             const address = cpu.r[rb] + cpu.r[ro]; | ||||
|  | ||||
|             if (T) { | ||||
|                 switch (op) { | ||||
|                     0b00 => { | ||||
|                         // STRH | ||||
|                         bus.write16(address & 0xFFFF_FFFE, @truncate(u16, cpu.r[rd])); | ||||
|                     }, | ||||
|                     0b01 => { | ||||
|                         // LDRH | ||||
|                         const value = bus.read16(address & 0xFFFF_FFFE); | ||||
|                         cpu.r[rd] = std.math.rotr(u32, @as(u32, value), 8 * (address & 1)); | ||||
|                     }, | ||||
|                     0b10 => { | ||||
|                         // LDSB | ||||
|                         cpu.r[rd] = u32SignExtend(8, @as(u32, bus.read8(address))); | ||||
|                     }, | ||||
|                     0b11 => { | ||||
|                         // LDSH | ||||
|                         cpu.r[rd] = u32SignExtend(16, @as(u32, bus.read16(address & 0xFFFF_FFFE))); | ||||
|                     }, | ||||
|                 } | ||||
|             } else { | ||||
|                 switch (op) { | ||||
|                     0b00 => { | ||||
|                         // STR | ||||
|                         bus.write32(address & 0xFFFF_FFFC, cpu.r[rd]); | ||||
|                     }, | ||||
|                     0b01 => { | ||||
|                         // STRB | ||||
|                         bus.write8(address, @truncate(u8, cpu.r[rd])); | ||||
|                     }, | ||||
|                     0b10 => { | ||||
|                         // LDR | ||||
|                         const value = bus.read32(address & 0xFFFF_FFFC); | ||||
|                         cpu.r[rd] = std.math.rotr(u32, value, 8 * (address & 0x3)); | ||||
|                     }, | ||||
|                     0b11 => { | ||||
|                         // LDRB | ||||
|                         cpu.r[rd] = bus.read8(address); | ||||
|                     }, | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|     }.inner; | ||||
| } | ||||
| @@ -1,37 +0,0 @@ | ||||
| const std = @import("std"); | ||||
|  | ||||
| const Bus = @import("../../Bus.zig"); | ||||
| const Arm7tdmi = @import("../../cpu.zig").Arm7tdmi; | ||||
| const InstrFn = @import("../../cpu.zig").ThumbInstrFn; | ||||
|  | ||||
| pub fn format9(comptime B: bool, comptime L: bool, comptime offset: u5) InstrFn { | ||||
|     return struct { | ||||
|         fn inner(cpu: *Arm7tdmi, bus: *Bus, opcode: u16) void { | ||||
|             const rb = opcode >> 3 & 0x7; | ||||
|             const rd = opcode & 0x7; | ||||
|  | ||||
|             if (L) { | ||||
|                 if (B) { | ||||
|                     // LDRB | ||||
|                     const address = cpu.r[rb] + offset; | ||||
|                     cpu.r[rd] = bus.read8(address); | ||||
|                 } else { | ||||
|                     // LDR | ||||
|                     const address = cpu.r[rb] + (@as(u32, offset) << 2); | ||||
|                     const value = bus.read32(address & 0xFFFF_FFFC); | ||||
|                     cpu.r[rd] = std.math.rotr(u32, value, 8 * (address & 0x3)); | ||||
|                 } | ||||
|             } else { | ||||
|                 if (B) { | ||||
|                     // STRB | ||||
|                     const address = cpu.r[rb] + offset; | ||||
|                     bus.write8(address, @truncate(u8, cpu.r[rd])); | ||||
|                 } else { | ||||
|                     // STR | ||||
|                     const address = cpu.r[rb] + (@as(u32, offset) << 2); | ||||
|                     bus.write32(address & 0xFFFF_FFFC, cpu.r[rd]); | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|     }.inner; | ||||
| } | ||||
| @@ -1,5 +1,3 @@ | ||||
| const std = @import("std"); | ||||
| 
 | ||||
| const Bus = @import("../../Bus.zig"); | ||||
| const Arm7tdmi = @import("../../cpu.zig").Arm7tdmi; | ||||
| const InstrFn = @import("../../cpu.zig").ThumbInstrFn; | ||||
		Reference in New Issue
	
	Block a user