diff --git a/src/Packet.zig b/src/Packet.zig index 9c196fa..e0dbd86 100644 --- a/src/Packet.zig +++ b/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,7 +46,7 @@ const String = union(enum) { } }; -pub fn parse(self: *Self, allocator: Allocator, emu: Emulator) !String { +pub fn parse(self: *Self, allocator: Allocator, state: *State, emu: Emulator) !String { switch (self.contents[0]) { // Required '?' => { @@ -87,9 +88,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 }); } @@ -98,15 +96,63 @@ pub fn parse(self: *Self, allocator: Allocator, emu: Emulator) !String { return .{ .alloc = ret }; }, 'M' => @panic("TODO: Memory Write"), - 'c' => @panic("TODO: Continue"), + 'c' => { + while (true) { + emu.step(); + + const r = emu.registers(); + const is_thumb = emu.cpsr() >> 5 & 1 == 1; + const r15 = r[15] -| if (is_thumb) @as(u32, 4) else 8; + + if (state.hw_bkpt.isHit(r15)) { + return .{ .static = "T05 hwbreak;" }; + } + } + }, '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(); + return .{ .static = "S05" }; // Signal.Trap + }, - const ret = try std.fmt.allocPrint(allocator, "S{x:0>2}", .{@enumToInt(Signal.Trap)}); - return .{ .alloc = ret }; + // Breakpoints + 'z' => switch (self.contents[1]) { + '0' => @panic("TODO: Remove Software Breakpoint"), + '1' => { + var tokens = std.mem.tokenize(u8, self.contents[2..], ","); + const addr_str = tokens.next() orelse return .{ .static = "E9999" }; + // const kind_str = tokens.next() orelse return .{ .static = "E9999" }; + + const addr = try std.fmt.parseInt(u32, addr_str, 16); + // const kind = try std.fmt.parseInt(u32, kind_str, 16); + + state.hw_bkpt.remove(addr); + return .{ .static = "OK" }; + }, + '2' => @panic("TODO: Remove Write Watchpoint"), + '3' => @panic("TODO: Remove Read Watchpoint"), + '4' => @panic("TODO: Remove Access Watchpoint"), + else => return .{ .static = "" }, + }, + 'Z' => switch (self.contents[1]) { + '0' => @panic("TODO: Insert Software Breakpoint"), + '1' => { + var tokens = std.mem.tokenize(u8, self.contents[2..], ","); + const addr_str = tokens.next() orelse return .{ .static = "E9999" }; + const kind_str = tokens.next() orelse return .{ .static = "E9999" }; + + const addr = try std.fmt.parseInt(u32, addr_str, 16); + const kind = try std.fmt.parseInt(u32, kind_str, 16); + + try state.hw_bkpt.add(addr, kind); + return .{ .static = "OK" }; + }, + '2' => @panic("TODO: Insert Write Watchpoint"), + '3' => @panic("TODO: Insert Read Watchpoint"), + '4' => @panic("TODO: Insert Access Watchpoint"), + else => return .{ .static = "" }, }, // Optional diff --git a/src/Server.zig b/src/Server.zig index 20df87f..1be0f65 100644 --- a/src/Server.zig +++ b/src/Server.zig @@ -1,6 +1,7 @@ const std = @import("std"); const network = @import("network"); const Packet = @import("Packet.zig"); +const State = @import("State.zig"); const Socket = network.Socket; const Allocator = std.mem.Allocator; @@ -59,6 +60,7 @@ pkt_cache: ?[]const u8 = null, client: Socket, _socket: Socket, +state: State = .{}, emu: Emulator, pub fn init(emulator: Emulator) !Self { @@ -129,7 +131,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.state, self.emu) catch return .nack; defer string.deinit(allocator); const reply = string.inner(); diff --git a/src/State.zig b/src/State.zig new file mode 100644 index 0000000..a372020 --- /dev/null +++ b/src/State.zig @@ -0,0 +1,68 @@ +const std = @import("std"); + +hw_bkpt: HwBkpt = .{}, + +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| { + if (bkpt_opt == null) continue; + const bkpt = bkpt_opt.?; + + 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.* != null) { + const bkpt = bkpt_opt.*.?; + if (bkpt.addr == addr) return; // makes this fn indempotent + + continue; + } + + 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| { + if (bkpt_opt.* == null) continue; + const bkpt = bkpt_opt.*.?; // FIXME: bkpt_opt.?.addr works though? + + log.debug("Removed Breakpoint at 0x{X:0>8}", .{addr}); + if (bkpt.addr == addr) bkpt_opt.* = null; + } + } +}; + +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, + }; + } + }; +};