Last active
March 7, 2023 15:17
-
-
Save g-cassie/9702ec3e88c437677f095de77a7890ad to your computer and use it in GitHub Desktop.
Computer Enhance Part 1 - Lesson 2 in Zig
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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