diff --git a/build.zig b/build.zig index 7523958..e2db1e7 100644 --- a/build.zig +++ b/build.zig @@ -15,11 +15,9 @@ pub fn build(b: *std.Build) void { // set a preferred release mode, allowing the user to decide how to optimize. const optimize = b.standardOptimizeOption(.{}); - const zig_network = b.dependency("zig-network", .{}); // https://github.com/MasterQ32/zig-network - _ = b.addModule("gdbstub", .{ .source_file = .{ .path = "src/lib.zig" }, - .dependencies = &.{.{ .name = "network", .module = zig_network.module("network") }}, + .dependencies = &.{}, }); // Creates a step for unit testing. This only builds the test executable diff --git a/build.zig.zon b/build.zig.zon index 7a28d4e..32dc07f 100644 --- a/build.zig.zon +++ b/build.zig.zon @@ -1,10 +1,5 @@ .{ .name = "zba-gdbstub", .version = "0.1.0", - .dependencies = .{ - .@"zig-network" = .{ - .url = "https://github.com/MasterQ32/zig-network/archive/07ef40cacd7b00bc0f26e254d97d4d4c9071195a.tar.gz", - .hash = "12204d3d4c1184d379a78399e5ddd8a46fe19ca6c991d5c9b909c1980d14acb1dede", - }, - }, + .dependencies = .{}, } diff --git a/src/Server.zig b/src/Server.zig index 2901869..5fc470f 100644 --- a/src/Server.zig +++ b/src/Server.zig @@ -1,12 +1,12 @@ const std = @import("std"); -const network = @import("network"); 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 Atomic = std.atomic.Atomic; +const Allocator = std.mem.Allocator; +const Server = std.net.StreamServer; +const Connection = Server.Connection; + const Self = @This(); const log = std.log.scoped(.Server); const port: u16 = 2424; @@ -57,32 +57,20 @@ pub const memory_map: []const u8 = // FIXME: Shouldn't this be a Packet Struct? pkt_cache: ?[]const u8 = null, -client: Socket, -_socket: Socket, +server: Server, emu: Emulator, pub fn init(emulator: Emulator) !Self { - try network.init(); + var server = std.net.StreamServer.init(.{}); + try server.listen(std.net.Address.initIp4([_]u8{0} ** 4, port)); - var socket = try Socket.create(.ipv4, .tcp); - try socket.bindToPort(port); - try socket.listen(); - - var client = try socket.accept(); // TODO: This blocks, is this OK? - - const endpoint = try client.getLocalEndPoint(); - log.info("client connected from {}", .{endpoint}); - - return .{ .emu = emulator, ._socket = socket, .client = client }; + return .{ .emu = emulator, .server = server }; } pub fn deinit(self: *Self, allocator: Allocator) void { self.reset(allocator); - - self.client.close(); - self._socket.close(); - network.deinit(); + self.server.deinit(); self.* = undefined; } @@ -98,13 +86,17 @@ const Action = union(enum) { pub fn run(self: *Self, allocator: Allocator, quit: *Atomic(bool)) !void { var buf: [Packet.max_len]u8 = undefined; + var client = try self.server.accept(); + log.info("client connected from {}", .{client.address}); + while (true) { - const len = try self.client.receive(&buf); + const len = try client.stream.readAll(&buf); if (len == 0) break; if (quit.load(.Monotonic)) break; const action = try self.parse(allocator, buf[0..len]); - try self.send(allocator, action); + + try self.send(allocator, client, action); } // Just in case its the gdbstub that exited first, @@ -113,7 +105,7 @@ pub fn run(self: *Self, allocator: Allocator, quit: *Atomic(bool)) !void { } fn parse(self: *Self, allocator: Allocator, input: []const u8) !Action { - // log.debug("-> {s}", .{input}); + log.debug("-> {s}", .{input}); return switch (input[0]) { '+' => blk: { @@ -146,11 +138,11 @@ fn handlePacket(self: *Self, allocator: Allocator, input: []const u8) !Action { return .{ .send = response }; } -fn send(self: *Self, allocator: Allocator, action: Action) !void { +fn send(self: *Self, allocator: Allocator, client: Server.Connection, action: Action) !void { switch (action) { .send => |pkt| { - _ = try self.client.send(pkt); - // log.debug("<- {s}", .{pkt}); + _ = try client.stream.writeAll(pkt); + log.debug("<- {s}", .{pkt}); self.reset(allocator); self.pkt_cache = pkt; @@ -159,19 +151,19 @@ fn send(self: *Self, allocator: Allocator, action: Action) !void { log.warn("received nack, resending: \"{?s}\"", .{self.pkt_cache}); if (self.pkt_cache) |pkt| { - _ = try self.client.send(pkt); - // log.debug("<- {s}", .{pkt}); + _ = try client.stream.writeAll(pkt); + log.debug("<- {s}", .{pkt}); } }, .ack => { - _ = try self.client.send("+"); - // log.debug("<- +", .{}); + _ = try client.stream.writeAll("+"); + log.debug("<- +", .{}); self.reset(allocator); }, .nack => { - _ = try self.client.send("-"); - // log.debug("<- -", .{}); + _ = try client.stream.writeAll("-"); + log.debug("<- -", .{}); self.reset(allocator); }, diff --git a/src/lib.zig b/src/lib.zig index 1b9f2c3..061cc39 100644 --- a/src/lib.zig +++ b/src/lib.zig @@ -142,3 +142,7 @@ pub const Emulator = struct { } } }; + +test { + _ = @import("test.zig"); +} diff --git a/src/main.zig b/src/main.zig deleted file mode 100644 index 5087e3a..0000000 --- a/src/main.zig +++ /dev/null @@ -1,18 +0,0 @@ -const std = @import("std"); -const Server = @import("gdbstub").Server; - -pub fn main() !void { - const log = std.log.scoped(.Main); - - var gpa = std.heap.GeneralPurposeAllocator(.{}){}; - defer std.debug.assert(!gpa.deinit()); - - const allocator = gpa.allocator(); - - var server = try Server.init(); - defer server.deinit(allocator); - - try server.run(allocator); - - log.info("Client disconnected", .{}); -} diff --git a/src/test.zig b/src/test.zig new file mode 100644 index 0000000..db43492 --- /dev/null +++ b/src/test.zig @@ -0,0 +1,108 @@ +const std = @import("std"); +const builtin = @import("builtin"); + +const Emulator = @import("lib.zig").Emulator; +const Server = @import("Server.zig"); + +const Allocator = std.mem.Allocator; + +const BarebonesEmulator = struct { + r: [16]u32 = [_]u32{0} ** 16, + + pub fn interface(self: *@This(), allocator: Allocator) Emulator { + return Emulator.init(allocator, self); + } + + pub fn read(_: *const @This(), _: u32) u8 { + return 0; + } + + pub fn write(_: *@This(), _: u32, _: u8) void {} + + pub fn registers(self: *@This()) *[16]u32 { + return &self.r; + } + + pub fn cpsr(_: *const @This()) u32 { + return 0; + } + + pub fn step(_: *@This()) void { + // execute 1 instruction + } +}; + +test Server { + // https://github.com/ziglang/zig/blob/225fe6ddbfae016395762850e0cd5c51f9e7751c/lib/std/net/test.zig#L146C1-L156 + if (builtin.single_threaded) return error.SkipZigTest; + if (builtin.os.tag == .wasi) return error.SkipZigTest; + + if (builtin.os.tag == .windows) + _ = try std.os.windows.WSAStartup(2, 2); + + defer if (builtin.os.tag == .windows) std.os.windows.WSACleanup() catch unreachable; + + const allocator = std.testing.allocator; + + var impl = BarebonesEmulator{}; + var iface = impl.interface(allocator); + defer iface.deinit(); + + const clientFn = struct { + fn inner(address: std.net.Address) !void { + const socket = try std.net.tcpConnectToAddress(address); + defer socket.close(); + + _ = try socket.writer().writeAll("+"); + } + }.inner; + + var server = try Server.init(iface); + defer server.deinit(allocator); + + const t = try std.Thread.spawn(.{}, clientFn, .{server.server.listen_address}); + defer t.join(); + + var should_quit = std.atomic.Atomic(bool).init(false); + + try server.run(std.testing.allocator, &should_quit); +} + +test Emulator { + const ExampleImpl = struct { + r: [16]u32 = [_]u32{0} ** 16, + + pub fn interface(self: *@This(), allocator: std.mem.Allocator) Emulator { + return Emulator.init(allocator, self); + } + + pub fn read(_: *const @This(), _: u32) u8 { + return 0; + } + + pub fn write(_: *@This(), _: u32, _: u8) void {} + + pub fn registers(self: *@This()) *[16]u32 { + return &self.r; + } + + pub fn cpsr(_: *const @This()) u32 { + return 0; + } + + pub fn step(_: *@This()) void { + // execute 1 instruction + } + }; + + var impl = ExampleImpl{}; + var emu = Emulator.init(std.testing.allocator, &impl); + + _ = emu.read(0x0000_0000); + emu.write(0x0000_0000, 0x00); + + _ = emu.registers(); + _ = emu.cpsr(); + + _ = emu.step(); +}