Compare commits
	
		
			9 Commits
		
	
	
		
			4bca44e5f2
			...
			215e053b9a
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 215e053b9a | |||
| acb59994fc | |||
| 6d6a109a08 | |||
| c1158b547e | |||
| d7b8d7acb1 | |||
| 81ff227ea7 | |||
| 82bad92fcf | |||
| dbf00006e7 | |||
| 59b6b51466 | 
							
								
								
									
										56
									
								
								build.zig
									
									
									
									
									
								
							
							
						
						
									
										56
									
								
								build.zig
									
									
									
									
									
								
							| @@ -1,54 +1,44 @@ | ||||
| const std = @import("std"); | ||||
| const CompileStep = std.Build.CompileStep; | ||||
|  | ||||
| fn path(comptime suffix: []const u8) []const u8 { | ||||
|     if (suffix[0] == '/') @compileError("expected a relative path"); | ||||
|     return comptime (std.fs.path.dirname(@src().file) orelse ".") ++ std.fs.path.sep_str ++ suffix; | ||||
| } | ||||
|  | ||||
| const pkgs = struct { | ||||
|     const Pkg = std.build.Pkg; | ||||
|  | ||||
|     pub const gdbstub: Pkg = .{ | ||||
|         .name = "gdbstub", | ||||
|         .source = .{ .path = path("src/lib.zig") }, | ||||
|         .dependencies = &[_]Pkg{network}, | ||||
|     }; | ||||
|  | ||||
| pub fn getModule(b: *std.Build) *std.build.Module { | ||||
|     // https://github.com/MasterQ32/zig-network | ||||
|     pub const network: Pkg = .{ | ||||
|         .name = "network", | ||||
|         .source = .{ .path = path("lib/zig-network/network.zig") }, | ||||
|     }; | ||||
| }; | ||||
|     const network = b.createModule(.{ .source_file = .{ .path = path("lib/zig-network/network.zig") } }); | ||||
|  | ||||
| pub fn link(exe: *std.build.LibExeObjStep) void { | ||||
|     exe.addPackage(pkgs.gdbstub); | ||||
|     return b.createModule(.{ | ||||
|         .source_file = .{ .path = path("src/lib.zig") }, | ||||
|         .dependencies = &.{.{ .name = "network", .module = network }}, | ||||
|     }); | ||||
| } | ||||
|  | ||||
| pub fn build(b: *std.build.Builder) void { | ||||
| pub fn build(b: *std.Build) void { | ||||
|     const target = b.standardTargetOptions(.{}); | ||||
|     _ = target; | ||||
|     const mode = b.standardReleaseOptions(); | ||||
|     // const optimize = b.standardOptimizeOption(.{}); | ||||
|  | ||||
|     // -- library -- | ||||
|     const lib = b.addStaticLibrary("gdbstub", "src/lib.zig"); | ||||
|     lib.addPackage(pkgs.network); | ||||
|     // -- Library -- | ||||
|  | ||||
|     lib.setBuildMode(mode); | ||||
|     lib.install(); | ||||
|     const lib_test = b.addTest(.{ | ||||
|         .root_source_file = .{ .path = "src/lib.zig" }, | ||||
|         .target = target, | ||||
|     }); | ||||
|  | ||||
|     const lib_tests = b.addTest("src/lib.zig"); | ||||
|     lib_tests.setBuildMode(mode); | ||||
|     const test_step = b.step("test", "Run Library Tests"); | ||||
|     test_step.dependOn(&lib_test.step); | ||||
|  | ||||
|     const test_step = b.step("lib-test", "Run Library Tests"); | ||||
|     test_step.dependOn(&lib_tests.step); | ||||
|     // -- Executable -- | ||||
|  | ||||
|     // // -- Executable -- | ||||
|     // const exe = b.addExecutable("gdbserver", "src/main.zig"); | ||||
|     // const exe = b.addExecutable(.{ | ||||
|     //     .name = "gdbserver", | ||||
|     //     .root_source_file = .{ .path = "src/main.zig" }, | ||||
|     //     .target = target, | ||||
|     //     .optimize = optimize, | ||||
|     // }); | ||||
|     // link(exe); | ||||
|  | ||||
|     // exe.setTarget(target); | ||||
|     // exe.setBuildMode(mode); | ||||
|     // exe.install(); | ||||
|  | ||||
|     // const run_cmd = exe.run(); | ||||
|   | ||||
 Submodule lib/zig-network updated: a8c4502538...45ae8cf0ce
									
								
							
							
								
								
									
										132
									
								
								src/Packet.zig
									
									
									
									
									
								
							
							
						
						
									
										132
									
								
								src/Packet.zig
									
									
									
									
									
								
							| @@ -2,6 +2,7 @@ const std = @import("std"); | ||||
|  | ||||
| const Allocator = std.mem.Allocator; | ||||
| const Emulator = @import("lib.zig").Emulator; | ||||
| const State = @import("State.zig"); | ||||
|  | ||||
| const target = @import("Server.zig").target; | ||||
| const memory_map = @import("Server.zig").memory_map; | ||||
| @@ -45,13 +46,10 @@ const String = union(enum) { | ||||
|     } | ||||
| }; | ||||
|  | ||||
| pub fn parse(self: *Self, allocator: Allocator, emu: Emulator) !String { | ||||
| pub fn parse(self: *Self, allocator: Allocator, emu: *Emulator) !String { | ||||
|     switch (self.contents[0]) { | ||||
|         // Required | ||||
|         '?' => { | ||||
|             const ret = try std.fmt.allocPrint(allocator, "S{x:0>2}", .{@enumToInt(Signal.Int)}); | ||||
|             return .{ .alloc = ret }; | ||||
|         }, | ||||
|         '?' => return .{ .static = "T05" }, // FIXME: which errno? | ||||
|         'g' => { | ||||
|             const r = emu.registers(); | ||||
|             const cpsr = emu.cpsr(); | ||||
| @@ -76,8 +74,8 @@ pub fn parse(self: *Self, allocator: Allocator, emu: Emulator) !String { | ||||
|         'G' => @panic("TODO: Register Write"), | ||||
|         'm' => { | ||||
|             var tokens = std.mem.tokenize(u8, self.contents[1..], ","); | ||||
|             const addr_str = tokens.next() orelse return .{ .static = "E9999" }; // EUNKNOWN | ||||
|             const length_str = tokens.next() orelse return .{ .static = "E9999" }; // EUNKNOWN | ||||
|             const addr_str = tokens.next() orelse return error.InvalidPacket; | ||||
|             const length_str = tokens.next() orelse return error.InvalidPacket; | ||||
|  | ||||
|             const addr = try std.fmt.parseInt(u32, addr_str, 16); | ||||
|             const len = try std.fmt.parseInt(u32, length_str, 16); | ||||
| @@ -87,9 +85,6 @@ pub fn parse(self: *Self, allocator: Allocator, emu: Emulator) !String { | ||||
|             { | ||||
|                 var i: u32 = 0; | ||||
|                 while (i < len) : (i += 1) { | ||||
|                     const value = emu.read(addr + i); | ||||
|                     log.debug("read 0x{X:0>2} from 0x{X:0>8}", .{ value, addr + i }); | ||||
|  | ||||
|                     // writes the formatted integer to the buffer, returns a slice to the buffer but we ignore that | ||||
|                     _ = std.fmt.bufPrintIntToSlice(ret[i * 2 ..][0..2], emu.read(addr + i), 16, .lower, .{ .fill = '0', .width = 2 }); | ||||
|                 } | ||||
| @@ -97,40 +92,124 @@ pub fn parse(self: *Self, allocator: Allocator, emu: Emulator) !String { | ||||
|  | ||||
|             return .{ .alloc = ret }; | ||||
|         }, | ||||
|         'M' => @panic("TODO: Memory Write"), | ||||
|         'c' => @panic("TODO: Continue"), | ||||
|         'M' => { | ||||
|             var tokens = std.mem.tokenize(u8, self.contents[1..], ",:"); | ||||
|  | ||||
|             const addr_str = tokens.next() orelse return error.InvalidPacket; | ||||
|             const length_str = tokens.next() orelse return error.InvalidPacket; | ||||
|             const bytes = tokens.next() orelse return error.InvalidPacket; | ||||
|  | ||||
|             const addr = try std.fmt.parseInt(u32, addr_str, 16); | ||||
|             const len = try std.fmt.parseInt(u32, length_str, 16); | ||||
|  | ||||
|             { | ||||
|                 var i: u32 = 0; | ||||
|                 while (i < len) : (i += 1) { | ||||
|                     const str = bytes[2 * i ..][0..2]; | ||||
|                     const value = try std.fmt.parseInt(u8, str, 16); | ||||
|  | ||||
|                     emu.write(addr + i, value); | ||||
|                 } | ||||
|             } | ||||
|  | ||||
|             return .{ .static = "OK" }; | ||||
|         }, | ||||
|         'c' => { | ||||
|             switch (emu.contd()) { | ||||
|                 .SingleStep => unreachable, | ||||
|                 .Trap => |r| switch (r) { | ||||
|                     .HwBkpt => return .{ .static = "T05 hwbreak:;" }, | ||||
|                     .SwBkpt => return .{ .static = "T05 swbreak:;" }, | ||||
|                 }, | ||||
|             } | ||||
|         }, | ||||
|         's' => { | ||||
|             // var tokens = std.mem.tokenize(u8, self.contents[1..], " "); | ||||
|             // const addr = if (tokens.next()) |s| try std.fmt.parseInt(u32, s, 16) else null; | ||||
|  | ||||
|             emu.step(); | ||||
|  | ||||
|             const ret = try std.fmt.allocPrint(allocator, "S{x:0>2}", .{@enumToInt(Signal.Trap)}); | ||||
|             return .{ .alloc = ret }; | ||||
|             switch (emu.step()) { | ||||
|                 .SingleStep => return .{ .static = "T05" }, | ||||
|                 .Trap => |r| switch (r) { | ||||
|                     .HwBkpt => return .{ .static = "T05 hwbreak:;" }, | ||||
|                     .SwBkpt => return .{ .static = "T05 swbreak:;" }, | ||||
|                 }, | ||||
|             } | ||||
|         }, | ||||
|  | ||||
|         // Optional | ||||
|         // Breakpoints | ||||
|         'z' => { | ||||
|             var tokens = std.mem.tokenize(u8, self.contents[2..], ","); | ||||
|  | ||||
|             const addr_str = tokens.next() orelse return error.InvalidPacket; | ||||
|             const addr = try std.fmt.parseInt(u32, addr_str, 16); | ||||
|  | ||||
|             switch (self.contents[1]) { | ||||
|                 '0' => { | ||||
|                     emu.removeBkpt(.Software, addr); | ||||
|                     return .{ .static = "OK" }; | ||||
|                 }, | ||||
|                 '1' => { | ||||
|                     emu.removeBkpt(.Hardware, addr); | ||||
|                     return .{ .static = "OK" }; | ||||
|                 }, | ||||
|                 '2' => return .{ .static = "" }, // TODO: Remove Write Watchpoint | ||||
|                 '3' => return .{ .static = "" }, // TODO: Remove Read Watchpoint | ||||
|                 '4' => return .{ .static = "" }, // TODO: Remove Access Watchpoint | ||||
|                 else => return .{ .static = "" }, | ||||
|             } | ||||
|         }, | ||||
|         'Z' => { | ||||
|             var tokens = std.mem.tokenize(u8, self.contents[2..], ","); | ||||
|             const addr_str = tokens.next() orelse return error.InvalidPacket; | ||||
|             const kind_str = tokens.next() orelse return error.InvalidPacket; | ||||
|  | ||||
|             const addr = try std.fmt.parseInt(u32, addr_str, 16); | ||||
|             const kind = try std.fmt.parseInt(u32, kind_str, 16); | ||||
|  | ||||
|             switch (self.contents[1]) { | ||||
|                 '0' => { | ||||
|                     try emu.addBkpt(.Software, addr, kind); | ||||
|                     return .{ .static = "OK" }; | ||||
|                 }, | ||||
|                 '1' => { | ||||
|                     emu.addBkpt(.Hardware, addr, kind) catch |e| { | ||||
|                         switch (e) { | ||||
|                             error.OutOfSpace => return .{ .static = "E22" }, // FIXME: which errno? | ||||
|                             else => return e, | ||||
|                         } | ||||
|                     }; | ||||
|  | ||||
|                     return .{ .static = "OK" }; | ||||
|                 }, | ||||
|                 '2' => return .{ .static = "" }, // TODO: Insert Write Watchpoint | ||||
|                 '3' => return .{ .static = "" }, // TODO: Insert Read Watchpoint | ||||
|                 '4' => return .{ .static = "" }, // TODO: Insert Access Watchpoint | ||||
|                 else => return .{ .static = "" }, | ||||
|             } | ||||
|         }, | ||||
|  | ||||
|         // TODO: Figure out the difference between 'M' and 'X' | ||||
|         'D' => { | ||||
|             log.info("Disconnecting...", .{}); | ||||
|             return .{ .static = "OK" }; | ||||
|         }, | ||||
|         'H' => return .{ .static = "" }, | ||||
|         'v' => { | ||||
|             if (substr(self.contents[1..], "MustReplyEmpty")) { | ||||
|                 return .{ .static = "" }; | ||||
|             if (!substr(self.contents[1..], "MustReplyEmpty")) { | ||||
|                 log.warn("Unimplemented: {s}", .{self.contents}); | ||||
|             } | ||||
|  | ||||
|             log.warn("Unimplemented: {s}", .{self.contents}); | ||||
|             return .{ .static = "" }; | ||||
|         }, | ||||
|         'q' => { | ||||
|             if (self.contents[1] == 'C' and self.contents.len == 2) return .{ .static = "QC1" }; | ||||
|  | ||||
|             if (substr(self.contents[1..], "fThreadInfo")) return .{ .static = "m1" }; | ||||
|             if (substr(self.contents[1..], "sThreadInfo")) return .{ .static = "l" }; | ||||
|             if (substr(self.contents[1..], "Attached")) return .{ .static = "1" }; // Tell GDB we're attached to a process | ||||
|  | ||||
|             if (substr(self.contents[1..], "Supported")) { | ||||
|                 const format = "PacketSize={x:};qXfer:features:read+;qXfer:memory-map:read+"; | ||||
|                 const format = "PacketSize={x:};swbreak+;hwbreak+;qXfer:features:read+;qXfer:memory-map:read+"; | ||||
|                 // TODO: Anything else? | ||||
|  | ||||
|                 const ret = try std.fmt.allocPrint(allocator, format, .{Self.max_len}); | ||||
| @@ -142,9 +221,10 @@ pub fn parse(self: *Self, allocator: Allocator, emu: Emulator) !String { | ||||
|                 _ = tokens.next(); // Xfer | ||||
|                 _ = tokens.next(); // features | ||||
|                 _ = tokens.next(); // read | ||||
|                 const annex = tokens.next() orelse return .{ .static = "E9999" }; | ||||
|                 const offset_str = tokens.next() orelse return .{ .static = "E99999" }; | ||||
|                 const length_str = tokens.next() orelse return .{ .static = "E9999" }; | ||||
|  | ||||
|                 const annex = tokens.next() orelse return error.InvalidPacket; | ||||
|                 const offset_str = tokens.next() orelse return error.InvalidPacket; | ||||
|                 const length_str = tokens.next() orelse return error.InvalidPacket; | ||||
|  | ||||
|                 if (std.mem.eql(u8, annex, "target.xml")) { | ||||
|                     const offset = try std.fmt.parseInt(usize, offset_str, 16); | ||||
| @@ -173,8 +253,8 @@ pub fn parse(self: *Self, allocator: Allocator, emu: Emulator) !String { | ||||
|                 _ = tokens.next(); // Xfer | ||||
|                 _ = tokens.next(); // memory-map | ||||
|                 _ = tokens.next(); // read | ||||
|                 const offset_str = tokens.next() orelse return .{ .static = "E9999" }; | ||||
|                 const length_str = tokens.next() orelse return .{ .static = "E9999" }; | ||||
|                 const offset_str = tokens.next() orelse return error.InvalidPacket; | ||||
|                 const length_str = tokens.next() orelse return error.InvalidPacket; | ||||
|  | ||||
|                 const offset = try std.fmt.parseInt(usize, offset_str, 16); | ||||
|                 const length = try std.fmt.parseInt(usize, length_str, 16); | ||||
|   | ||||
| @@ -4,6 +4,7 @@ const Packet = @import("Packet.zig"); | ||||
|  | ||||
| const Socket = network.Socket; | ||||
| const Allocator = std.mem.Allocator; | ||||
| const Atomic = std.atomic.Atomic; | ||||
| const Emulator = @import("lib.zig").Emulator; | ||||
|  | ||||
| const Self = @This(); | ||||
| @@ -94,16 +95,21 @@ const Action = union(enum) { | ||||
|     nack, | ||||
| }; | ||||
|  | ||||
| pub fn run(self: *Self, allocator: Allocator) !void { | ||||
| pub fn run(self: *Self, allocator: Allocator, quit: *Atomic(bool)) !void { | ||||
|     var buf: [Packet.max_len]u8 = undefined; | ||||
|  | ||||
|     while (true) { | ||||
|         const len = try self.client.receive(&buf); | ||||
|         if (len == 0) break; | ||||
|  | ||||
|         if (quit.load(.Monotonic)) break; | ||||
|         const action = try self.parse(allocator, buf[0..len]); | ||||
|         try self.send(allocator, action); | ||||
|     } | ||||
|  | ||||
|     // Just in case its the gdbstub that exited first, | ||||
|     // attempt to signal to the GUI that it should also exit | ||||
|     quit.store(true, .Monotonic); | ||||
| } | ||||
|  | ||||
| fn parse(self: *Self, allocator: Allocator, input: []const u8) !Action { | ||||
| @@ -129,7 +135,7 @@ fn handlePacket(self: *Self, allocator: Allocator, input: []const u8) !Action { | ||||
|     var packet = Packet.from(allocator, input) catch return .nack; | ||||
|     defer packet.deinit(allocator); | ||||
|  | ||||
|     var string = packet.parse(allocator, self.emu) catch return .nack; | ||||
|     var string = packet.parse(allocator, &self.emu) catch return .nack; | ||||
|     defer string.deinit(allocator); | ||||
|  | ||||
|     const reply = string.inner(); | ||||
|   | ||||
							
								
								
									
										122
									
								
								src/State.zig
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										122
									
								
								src/State.zig
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,122 @@ | ||||
| const std = @import("std"); | ||||
|  | ||||
| const Allocator = std.mem.Allocator; | ||||
| const ArrayList = std.ArrayList; | ||||
|  | ||||
| hw_bkpt: HwBkpt = .{}, | ||||
| sw_bkpt: SwBkpt, | ||||
|  | ||||
| pub fn init(allocator: Allocator) @This() { | ||||
|     return .{ .sw_bkpt = SwBkpt.init(allocator) }; | ||||
| } | ||||
|  | ||||
| pub fn deinit(self: *@This()) void { | ||||
|     self.sw_bkpt.deinit(); | ||||
|     self.* = undefined; | ||||
| } | ||||
|  | ||||
| const SwBkpt = struct { | ||||
|     const log = std.log.scoped(.SwBkpt); | ||||
|  | ||||
|     list: std.ArrayList(Bkpt), | ||||
|  | ||||
|     pub fn init(allocator: Allocator) @This() { | ||||
|         return .{ .list = ArrayList(Bkpt).init(allocator) }; | ||||
|     } | ||||
|  | ||||
|     pub fn deinit(self: *@This()) void { | ||||
|         self.list.deinit(); | ||||
|         self.* = undefined; | ||||
|     } | ||||
|  | ||||
|     pub fn isHit(self: *const @This(), addr: u32) bool { | ||||
|         for (self.list.items) |bkpt| { | ||||
|             if (bkpt.addr == addr) return true; | ||||
|         } | ||||
|  | ||||
|         return false; | ||||
|     } | ||||
|  | ||||
|     pub fn add(self: *@This(), addr: u32, kind: u32) !void { | ||||
|         for (self.list.items) |bkpt| { | ||||
|             if (bkpt.addr == addr) return; // indempotent | ||||
|         } | ||||
|  | ||||
|         try self.list.append(.{ .addr = addr, .kind = try Bkpt.Kind.from(u32, kind) }); | ||||
|         log.warn("Added Breakpoint at 0x{X:0>8}", .{addr}); | ||||
|     } | ||||
|  | ||||
|     pub fn remove(self: *@This(), addr: u32) void { | ||||
|         for (self.list.items, 0..) |bkpt, i| { | ||||
|             if (bkpt.addr == addr) { | ||||
|                 _ = self.list.orderedRemove(i); | ||||
|                 log.debug("Removed Breakpoint at 0x{X:0>8}", .{addr}); | ||||
|  | ||||
|                 return; | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| }; | ||||
|  | ||||
| const HwBkpt = struct { | ||||
|     const log = std.log.scoped(.HwBkpt); | ||||
|  | ||||
|     list: [2]?Bkpt = .{ null, null }, | ||||
|  | ||||
|     pub fn isHit(self: *const @This(), addr: u32) bool { | ||||
|         for (self.list) |bkpt_opt| { | ||||
|             const bkpt = bkpt_opt orelse continue; | ||||
|             if (bkpt.addr == addr) return true; | ||||
|         } | ||||
|  | ||||
|         return false; | ||||
|     } | ||||
|  | ||||
|     pub fn add(self: *@This(), addr: u32, kind: u32) !void { | ||||
|         for (&self.list) |*bkpt_opt| { | ||||
|             if (bkpt_opt.*) |bkpt| { | ||||
|                 if (bkpt.addr == addr) return; // idempotent | ||||
|             } else { | ||||
|                 bkpt_opt.* = .{ .addr = addr, .kind = try Bkpt.Kind.from(u32, kind) }; | ||||
|                 log.debug("Added Breakpoint at 0x{X:0>8}", .{addr}); | ||||
|  | ||||
|                 return; | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         return error.OutOfSpace; | ||||
|     } | ||||
|  | ||||
|     pub fn remove(self: *@This(), addr: u32) void { | ||||
|         for (&self.list) |*bkpt_opt| { | ||||
|             const bkpt = bkpt_opt.* orelse continue; | ||||
|  | ||||
|             if (bkpt.addr == addr) { | ||||
|                 bkpt_opt.* = null; | ||||
|                 log.debug("Removed Breakpoint at 0x{X:0>8}", .{addr}); | ||||
|  | ||||
|                 break; | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| }; | ||||
|  | ||||
| const Bkpt = struct { | ||||
|     addr: u32, | ||||
|     kind: Kind, | ||||
|  | ||||
|     const Kind = enum(u3) { | ||||
|         Arm = 2, | ||||
|         Thumb = 4, | ||||
|  | ||||
|         pub fn from(comptime T: type, num: T) !@This() { | ||||
|             comptime std.debug.assert(@typeInfo(T) == .Int); | ||||
|  | ||||
|             return switch (num) { | ||||
|                 2 => .Arm, | ||||
|                 4 => .Thumb, | ||||
|                 else => error.UnknownBkptKind, | ||||
|             }; | ||||
|         } | ||||
|     }; | ||||
| }; | ||||
							
								
								
									
										71
									
								
								src/lib.zig
									
									
									
									
									
								
							
							
						
						
									
										71
									
								
								src/lib.zig
									
									
									
									
									
								
							| @@ -1,10 +1,23 @@ | ||||
| const std = @import("std"); | ||||
| const Allocator = std.mem.Allocator; | ||||
|  | ||||
| /// Re-export of the server interface | ||||
| pub const Server = @import("Server.zig"); | ||||
| const State = @import("State.zig"); | ||||
|  | ||||
| /// Interface for interacting between GDB and a GBA emu | ||||
| pub const Emulator = struct { | ||||
|     const Self = @This(); | ||||
|  | ||||
|     const Signal = union(enum) { | ||||
|         const Kind = enum { HwBkpt, SwBkpt }; | ||||
|  | ||||
|         Trap: Kind, | ||||
|         SingleStep: void, | ||||
|     }; | ||||
|  | ||||
|     state: State, | ||||
|  | ||||
|     ptr: *anyopaque, | ||||
|  | ||||
|     readFn: *const fn (*anyopaque, u32) u8, | ||||
| @@ -15,7 +28,7 @@ pub const Emulator = struct { | ||||
|  | ||||
|     stepFn: *const fn (*anyopaque) void, | ||||
|  | ||||
|     pub fn init(ptr: anytype) Self { | ||||
|     pub fn init(allocator: Allocator, ptr: anytype) Self { | ||||
|         const Ptr = @TypeOf(ptr); | ||||
|         const ptr_info = @typeInfo(Ptr); | ||||
|  | ||||
| @@ -56,7 +69,21 @@ pub const Emulator = struct { | ||||
|             } | ||||
|         }; | ||||
|  | ||||
|         return .{ .ptr = ptr, .readFn = gen.readImpl, .writeFn = gen.writeImpl, .registersFn = gen.registersImpl, .cpsrFn = gen.cpsrImpl, .stepFn = gen.stepImpl }; | ||||
|         return .{ | ||||
|             .ptr = ptr, | ||||
|             .readFn = gen.readImpl, | ||||
|             .writeFn = gen.writeImpl, | ||||
|             .registersFn = gen.registersImpl, | ||||
|             .cpsrFn = gen.cpsrImpl, | ||||
|             .stepFn = gen.stepImpl, | ||||
|  | ||||
|             .state = State.init(allocator), | ||||
|         }; | ||||
|     } | ||||
|  | ||||
|     pub fn deinit(self: *Self) void { | ||||
|         self.state.deinit(); | ||||
|         self.* = undefined; | ||||
|     } | ||||
|  | ||||
|     pub inline fn read(self: Self, addr: u32) u8 { | ||||
| @@ -75,7 +102,45 @@ pub const Emulator = struct { | ||||
|         return self.cpsrFn(self.ptr); | ||||
|     } | ||||
|  | ||||
|     pub inline fn step(self: Self) void { | ||||
|     pub inline fn contd(self: *Self) Signal { | ||||
|         while (true) { | ||||
|             const signal = self.step(); | ||||
|  | ||||
|             switch (signal) { | ||||
|                 .SingleStep => {}, | ||||
|                 .Trap => return signal, | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     pub inline fn step(self: *Self) Signal { | ||||
|         self.stepFn(self.ptr); | ||||
|  | ||||
|         const r = self.registersFn(self.ptr); | ||||
|         const is_thumb = self.cpsrFn(self.ptr) >> 5 & 1 == 1; | ||||
|  | ||||
|         const r15 = r[15] -| if (is_thumb) @as(u32, 4) else 8; | ||||
|  | ||||
|         if (self.state.sw_bkpt.isHit(r15)) return .{ .Trap = .SwBkpt }; | ||||
|         if (self.state.hw_bkpt.isHit(r15)) return .{ .Trap = .HwBkpt }; | ||||
|  | ||||
|         return .SingleStep; | ||||
|     } | ||||
|  | ||||
|     const BkptType = enum { Hardware, Software }; | ||||
|  | ||||
|     // TODO: Consider properly implementing Software interrupts? | ||||
|     pub fn addBkpt(self: *Self, comptime @"type": BkptType, addr: u32, kind: u32) !void { | ||||
|         switch (@"type") { | ||||
|             .Hardware => try self.state.hw_bkpt.add(addr, kind), | ||||
|             .Software => try self.state.sw_bkpt.add(addr, kind), | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     pub fn removeBkpt(self: *Self, comptime @"type": BkptType, addr: u32) void { | ||||
|         switch (@"type") { | ||||
|             .Hardware => self.state.hw_bkpt.remove(addr), | ||||
|             .Software => self.state.sw_bkpt.remove(addr), | ||||
|         } | ||||
|     } | ||||
| }; | ||||
|   | ||||
		Reference in New Issue
	
	Block a user