Skip to content

Instantly share code, notes, and snippets.

@g-cassie
Last active March 7, 2023 15:17
Show Gist options
  • Save g-cassie/9702ec3e88c437677f095de77a7890ad to your computer and use it in GitHub Desktop.
Save g-cassie/9702ec3e88c437677f095de77a7890ad to your computer and use it in GitHub Desktop.
Computer Enhance Part 1 - Lesson 2 in Zig
const std = @import("std");
const Ops = enum { mov, imm, imm_mem, mem_acc, acc_mem };
const Mod = enum { reg, mem0, mem8, mem16 };
pub fn parseMod(byte: u8) Mod {
return switch (byte & 0b11_000_000) {
0b11_000000 => Mod.reg,
0b01_000000 => Mod.mem8,
0b10_000000 => Mod.mem16,
0b00_000000 => Mod.mem0,
else => unreachable,
};
}
pub fn readToBuf(reader_p: anytype, buf: []u8, offset: u8, num_bytes: u8) void {
const amt_read: usize = reader_p.*.read(buf[offset .. num_bytes + offset]) catch 0;
if (amt_read == 0) {
unreachable;
}
for (buf[offset .. num_bytes + offset]) |byte| {
std.debug.print("{b:0>8} ", .{byte});
}
}
pub fn printEffectiveAddress(addr: u8) void {
std.debug.print("{s}", .{switch (addr) {
0b00_000_000 => "bx + si",
0b00_000_001 => "bx + di",
0b00_000_010 => "bp + si",
0b00_000_011 => "bp + di",
0b00_000_100 => "si",
0b00_000_101 => "di",
0b00_000_110 => "bp", // in certain contexts this needs to be handled separately
0b00_000_111 => "bx",
else => unreachable,
}});
}
pub fn printReg(addr: u8, w: bool) void {
std.debug.print("{s}", .{switch (addr) {
0b00_000_000 => if (w) "ax" else "al",
0b00_000_001 => if (w) "cx" else "cl",
0b00_000_010 => if (w) "dx" else "dl",
0b00_000_011 => if (w) "bx" else "bl",
0b00_000_100 => if (w) "sp" else "ah",
0b00_000_101 => if (w) "bp" else "ch",
0b00_000_110 => if (w) "si" else "dh",
0b00_000_111 => if (w) "di" else "bh",
else => unreachable,
}});
}
pub fn main() !void {
var file = try std.fs.cwd().openFile("listing_0040_challenge_movs", .{});
defer file.close();
var reader = file.reader();
var buf: [6]u8 = undefined;
while (true) {
readToBuf(&reader, &buf, 0, 2); // every instruction is at least two bytes
// parse the op code
const op: Ops = switch (buf[0] & 0b1111_0000) {
0b1011_0000 => Ops.imm,
else => switch (buf[0] & 0b111111_00) {
0b100010_00 => Ops.mov,
else => switch (buf[0] & 0b1111111_0) {
0b1100011_0 => Ops.imm_mem,
0b1010000_0 => Ops.mem_acc,
0b1010001_0 => Ops.acc_mem,
else => unreachable,
},
},
};
// parse w and mod and read additional bytes if necessary
var mod: Mod = undefined;
var w: bool = undefined;
switch (op) {
Ops.imm => {
w = buf[0] & 0b0000_1000 > 0;
if (w) {
readToBuf(&reader, &buf, 2, 1);
}
},
Ops.mov => {
mod = parseMod(buf[1]);
const extra_bytes: u8 = switch (mod) {
Mod.mem8 => 1,
Mod.mem16 => 2,
Mod.reg => 0,
Mod.mem0 => if ((buf[1] & 0b00_000_111) == 0b00_000_110) 2 else 0,
};
if (extra_bytes > 0) {
readToBuf(&reader, &buf, 2, extra_bytes);
}
},
Ops.imm_mem => {
mod = parseMod(buf[1]);
w = buf[0] & 0b0000000_1 > 0;
var extra_bytes: u8 = if (w) 2 else 1;
extra_bytes += switch (mod) {
Mod.mem8 => 1,
Mod.mem16 => 2,
Mod.reg, Mod.mem0 => 0,
};
readToBuf(&reader, &buf, 2, extra_bytes);
},
Ops.mem_acc, Ops.acc_mem => {
readToBuf(&reader, &buf, 2, 1);
},
}
std.debug.print("\n", .{}); // terminate the byte line
switch (op) {
Ops.imm => {
var reg: u8 = (buf[0] & 0b00000_111);
printReg(reg, w);
// Two approaches to the same thing
// 1. i16 ptr
// const val_p: *align(1) i16 = @buf[1];
// std.debug.print(", {d}", .{val_p.*});
// 2. @bitCast (slightly better because it errors out if buf is not long enough)
std.debug.print(", {d}", .{@bitCast(i16, buf[1..3].*)});
},
Ops.imm_mem => {
std.debug.print("mov [", .{});
const pos2: u8 = buf[1] & 0b00_000_111;
var idx: u8 = 2;
printEffectiveAddress(pos2);
if (mod == Mod.mem16) {
std.debug.print("+ {d}", .{@bitCast(i16, buf[2..4].*)});
idx += 2;
}
if (w) {
const val_p = @ptrCast(*align(1) i16, @alignCast(1, &buf[idx]));
std.debug.print("], word {d}", .{val_p.*});
} else {
std.debug.print("], byte {d}", .{buf[idx]});
}
},
Ops.mov => {
var d: bool = buf[0] & 0b000000_1_0 > 0;
w = buf[0] & 0b000000_0_1 > 0;
const pos1: u8 = (buf[1] & 0b00_111_000) >> 3;
const pos2: u8 = buf[1] & 0b00_000_111;
std.debug.print("mov ", .{});
if (d) {
printReg(pos1, w);
std.debug.print(", ", .{});
}
if (mod == Mod.reg) {
printReg(pos2, w);
} else {
std.debug.print("[", .{});
if (mod == Mod.mem0 and pos2 == 0b00_00_110) {
std.debug.print("{}", .{@bitCast(i16, buf[2..4].*)});
} else {
printEffectiveAddress(pos2);
}
switch (mod) {
Mod.mem0 => {},
Mod.mem8 => {
const d8: i8 = @bitCast(i8, buf[2]);
if (d8 > 0) {
std.debug.print(" + {d}", .{d8});
} else if (d8 < 0) {
std.debug.print(" - {d}", .{try std.math.absInt(d8)});
}
},
Mod.mem16 => {
const d16 = @bitCast(i16, buf[2..4].*);
if (d16 > 0) {
std.debug.print(" + {d}", .{d16});
} else if (d16 < 0) {
std.debug.print(" - {d}", .{try std.math.absInt(d16)});
}
},
Mod.reg => unreachable,
}
std.debug.print("]", .{});
}
if (!d) {
std.debug.print(", ", .{});
printReg(pos1, w);
}
},
Ops.mem_acc => {
std.debug.print("mov ax, [{d}]", .{@bitCast(i16, buf[1..3].*)});
},
Ops.acc_mem => {
std.debug.print("mov [{d}], ax", .{@bitCast(i16, buf[1..3].*)});
},
}
std.debug.print("\n\n", .{}); // terminate the assembly line
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment