diff --git a/src/asm_parser.zig b/src/asm_parser.zig index 1914dd6..04e7be9 100644 --- a/src/asm_parser.zig +++ b/src/asm_parser.zig @@ -5,10 +5,10 @@ const expectEqualSlices = std.testing.expectEqualSlices; /// Represents an operand in an eBPF instruction pub const Operand = union(enum) { - Register: u8, + Register: i64, Integer: i64, Memory: struct { - base: u8, + base: i64, offset: i64, }, Nil, @@ -69,9 +69,9 @@ fn parseInteger(input: []const u8) !i64 { } /// Parses a register from the input -fn parseRegister(input: []const u8) !u8 { +fn parseRegister(input: []const u8) !i64 { if (input.len < 2 or input[0] != 'r') return error.InvalidRegister; - return std.fmt.parseInt(u8, input[1..], 0); + return std.fmt.parseInt(i64, input[1..], 0); } /// Parses an operand from the input @@ -95,7 +95,7 @@ fn parseOperand(input: []const u8) !Operand { /// Parses a single instruction from the input fn parseInstruction(input: []const u8) !Instruction { - var tokens = std.mem.split(u8, input, " "); + var tokens = std.mem.splitSequence(u8, input, " "); const name_token = tokens.next() orelse return error.InvalidToken; const name = try parseIdent(name_token); var operands: [3]Operand = .{ Operand.Nil, Operand.Nil, Operand.Nil }; @@ -104,7 +104,7 @@ fn parseInstruction(input: []const u8) !Instruction { // Get the remaining part of the input after the instruction name const remaining_input = std.mem.trim(u8, input[name.len..], " "); if (remaining_input.len > 0) { - var operand_tokens = std.mem.split(u8, remaining_input, ","); + var operand_tokens = std.mem.splitSequence(u8, remaining_input, ","); while (operand_tokens.next()) |token| { if (i >= operands.len) break; operands[i] = try parseOperand(std.mem.trim(u8, token, " ")); @@ -120,7 +120,7 @@ pub fn parse(input: []const u8) ![]Instruction { var instructions = std.ArrayList(Instruction).init(std.heap.page_allocator); defer instructions.deinit(); - var lines = std.mem.split(u8, input, "\n"); + var lines = std.mem.splitSequence(u8, input, "\n"); while (lines.next()) |line| { const trimmed_line = std.mem.trim(u8, line, " \t"); if (trimmed_line.len == 0) continue; diff --git a/src/assembler.zig b/src/assembler.zig index 2c37f2c..1767baf 100644 --- a/src/assembler.zig +++ b/src/assembler.zig @@ -95,7 +95,7 @@ fn makeInstructionMap() !InstructionMap { // JumpConditional for (jumpConditions) |jmp| { try result.put(jmp.name, InstructionMapEntry{ .instType = .JumpConditional, .opcode = ebpf.BPF_JMP | jmp.condition }); - try result.put(try std.fmt.allocPrint(std.heap.page_allocator, "{s}32", .{jmp.name}), InstructionMapEntry{ .instType = .JumpConditional, .opcode = ebpf.BPF_JMP32 | jmp.condition }); + try result.put(try std.fmt.allocPrint(std.heap.page_allocator, "{s}32", .{jmp.name}), InstructionMapEntry{ .instType = .JumpConditional, .opcode = ebpf.BPF_JMP | jmp.condition }); } // Endian @@ -108,7 +108,7 @@ fn makeInstructionMap() !InstructionMap { } /// Creates an eBPF instruction from its components -fn insn(opc: u8, dst: u8, src: u8, off: i16, imm: i32) ebpf.Instruction { +fn ix(opc: u8, dst: u8, src: u8, off: i16, imm: i32) ebpf.Instruction { return ebpf.Instruction{ .op = opc, .dst = dst, @@ -128,10 +128,10 @@ fn encode(instType: InstructionType, opc: u8, instruction: Instruction) !ebpf.In switch (operands[1]) { .Register => |src_reg| { if (src_reg > 10) return AssemblerError.InvalidRegister; - return insn(opc | ebpf.BPF_X, reg, src_reg, 0, 0); + return ix(opc | ebpf.BPF_X, @intCast(reg), @intCast(src_reg), 0, 0); }, .Integer => |imm| { - return insn(opc | ebpf.BPF_K, reg, 0, 0, @intCast(imm)); + return ix(opc | ebpf.BPF_K, @intCast(reg), 0, 0, @intCast(imm)); }, else => return AssemblerError.InvalidOperands, } @@ -141,13 +141,13 @@ fn encode(instType: InstructionType, opc: u8, instruction: Instruction) !ebpf.In .AluUnary => switch (operands[0]) { .Register => |reg| { if (reg > 10) return AssemblerError.InvalidRegister; - return insn(opc, reg, 0, 0, 0); + return ix(opc, @intCast(reg), 0, 0, 0); }, else => return AssemblerError.InvalidOperands, }, .LoadAbs => switch (operands[0]) { .Integer => |imm| { - return insn(opc, 0, 0, 0, @intCast(imm)); + return ix(opc, 0, 0, 0, @intCast(imm)); }, else => return AssemblerError.InvalidOperands, }, @@ -156,7 +156,7 @@ fn encode(instType: InstructionType, opc: u8, instruction: Instruction) !ebpf.In if (reg > 10) return AssemblerError.InvalidRegister; switch (operands[1]) { .Integer => |off| { - return insn(opc, 0, reg, 0, @intCast(off)); + return ix(opc, 0, @intCast(reg), 0, @intCast(off)); }, else => return AssemblerError.InvalidOperands, } @@ -168,7 +168,7 @@ fn encode(instType: InstructionType, opc: u8, instruction: Instruction) !ebpf.In if (reg > 10) return AssemblerError.InvalidRegister; switch (operands[1]) { .Integer => |imm| { - return insn(opc, reg, 0, 0, @intCast(imm)); + return ix(opc, @intCast(reg), 0, 0, @intCast(imm)); }, else => return AssemblerError.InvalidOperands, } @@ -181,7 +181,7 @@ fn encode(instType: InstructionType, opc: u8, instruction: Instruction) !ebpf.In switch (operands[1]) { .Memory => |mem| { if (mem.base > 10) return AssemblerError.InvalidRegister; - return insn(opc, dst, mem.base, @intCast(mem.offset), 0); + return ix(opc, @intCast(dst), @intCast(mem.base), @intCast(mem.offset), 0); }, else => return AssemblerError.InvalidOperands, } @@ -193,7 +193,7 @@ fn encode(instType: InstructionType, opc: u8, instruction: Instruction) !ebpf.In if (mem.base > 10) return AssemblerError.InvalidRegister; switch (operands[1]) { .Integer => |imm| { - return insn(opc, mem.base, 0, @intCast(mem.offset), @intCast(imm)); + return ix(opc, @intCast(mem.base), 0, @intCast(mem.offset), @intCast(imm)); }, else => return AssemblerError.InvalidOperands, } @@ -206,7 +206,7 @@ fn encode(instType: InstructionType, opc: u8, instruction: Instruction) !ebpf.In switch (operands[1]) { .Register => |src| { if (src > 10) return AssemblerError.InvalidRegister; - return insn(opc, mem.base, src, @intCast(mem.offset), 0); + return ix(opc, @intCast(mem.base), @intCast(src), @intCast(mem.offset), 0); }, else => return AssemblerError.InvalidOperands, } @@ -215,7 +215,7 @@ fn encode(instType: InstructionType, opc: u8, instruction: Instruction) !ebpf.In }, .JumpUnconditional => switch (operands[0]) { .Integer => |off| { - return insn(opc, 0, 0, @intCast(off), 0); + return ix(opc, 0, 0, @intCast(off), 0); }, else => return AssemblerError.InvalidOperands, }, @@ -227,7 +227,7 @@ fn encode(instType: InstructionType, opc: u8, instruction: Instruction) !ebpf.In if (src_reg > 10) return AssemblerError.InvalidRegister; switch (operands[2]) { .Integer => |off| { - return insn(opc | ebpf.BPF_X, reg, src_reg, @intCast(off), 0); + return ix(opc | ebpf.BPF_X, @intCast(reg), @intCast(src_reg), @intCast(off), 0); }, else => return AssemblerError.InvalidOperands, } @@ -235,7 +235,7 @@ fn encode(instType: InstructionType, opc: u8, instruction: Instruction) !ebpf.In .Integer => |imm| { switch (operands[2]) { .Integer => |off| { - return insn(opc | ebpf.BPF_K, reg, 0, @intCast(off), @intCast(imm)); + return ix(opc | ebpf.BPF_K, @intCast(reg), 0, @intCast(off), @intCast(imm)); }, else => return AssemblerError.InvalidOperands, } @@ -247,7 +247,7 @@ fn encode(instType: InstructionType, opc: u8, instruction: Instruction) !ebpf.In }, .Call => switch (operands[0]) { .Integer => |imm| { - return insn(opc, 0, 0, 0, @intCast(imm)); + return ix(opc, 0, 0, 0, @intCast(imm)); }, else => return AssemblerError.InvalidOperands, }, @@ -279,11 +279,11 @@ fn encode(instType: InstructionType, opc: u8, instruction: Instruction) !ebpf.In }, else => return AssemblerError.InvalidOperands, }; - return insn(opc, reg, 0, 0, size); + return ix(opc, @intCast(reg), 0, 0, size); }, else => return AssemblerError.InvalidOperands, }, - .NoOperand => return insn(opc, 0, 0, 0, 0), + .NoOperand => return ix(opc, 0, 0, 0, 0), } } @@ -300,7 +300,7 @@ fn assembleInternal(parsed: []Instruction) ![]ebpf.Instruction { // Special case for lddw if (entry.instType == .LoadImm and instruction.operands[1] != Operand.Nil) { if (instruction.operands[1] == Operand.Integer) { - try insns.append(insn(0, 0, 0, 0, @intCast(instruction.operands[1].Integer >> 32))); + try insns.append(ix(0, 0, 0, 0, @intCast(instruction.operands[1].Integer >> 32))); } } } @@ -315,11 +315,21 @@ pub fn assemble(src: []const u8) ![]const u8 { var result = std.ArrayList(u8).init(std.heap.page_allocator); for (insns) |instruction| { - const instr_array = instruction.to_array(); - try result.appendSlice(&instr_array); + try result.appendSlice(&instruction.to_array()); } - return result.toOwnedSlice(); + var formatted_result = std.ArrayList(u8).init(std.heap.page_allocator); + for (result.items, 0..) |byte, index| { + if (index % 8 == 0 and index != 0) { + try formatted_result.appendSlice("\n"); + } + try std.fmt.format(formatted_result.writer(), "0x{x:0>2}", .{byte}); + if (index % 8 != 7 and index != result.items.len - 1) { + try formatted_result.appendSlice(", "); + } + } + + return formatted_result.items; } /// Possible errors during assembly diff --git a/tests/alu.zig b/tests/alu.zig index f963f7b..4ed990b 100644 --- a/tests/alu.zig +++ b/tests/alu.zig @@ -3,7 +3,7 @@ const testing = std.testing; const interpreter = @import("zig-ebpf").interpreter; const expect = std.testing.expect; const ebpf = @import("zig-ebpf").ebpf; - +const asem = @import("zig-ebpf").assembler; test "simple_alu64_add" { var buffer: [512]u8 = undefined; var fba = std.heap.FixedBufferAllocator.init(&buffer); @@ -38,3 +38,45 @@ test "simple_jsle" { // std.log.warn("jsgt_imm: {d}", .{result}); try expect(result == 0x1); } + + +test "simple_jeq64_reg" { + var buffer: [512]u8 = undefined; + var fba = std.heap.FixedBufferAllocator.init(&buffer); + const allocator = fba.allocator(); + + var syscalls_map = std.AutoHashMap(usize, ebpf.Syscall).init(std.testing.allocator); + defer syscalls_map.deinit(); + const input = + \\mov r9, 1 + \\lsh r9, 32 + \\mov64 r0, 0 + \\mov64 r1, 0xa + \\mov64 r2, 0xb + \\jeq r1, r2, +5 + \\mov64 r0, 1 + \\mov64 r1, 0xb + \\or r1, r9 + \\jeq r1, r2, +1 + \\mov64 r0, 2 + \\exit + ; + + var prog: []u8 = undefined; + var buff: [563]u8 = undefined; + prog = buff[0..563]; + + const temp = (try asem.assemble(input)); + std.log.warn("jsgt_imm: {any}", .{temp.len}); + // std.mem.copyForwards(u8, prog, temp); + @memcpy(prog, temp); + // var prog = [_]u8{ 183, 0, 0, 0, 0, 0, 0, 0, 183, 1, 0, 0, 254, 255, 255, 255, 101, 1, 4, 0, 255, 255, 255, 255, 183, 0, 0, 0, 1, 0, 0, 0, 183, 1, 0, 0, 0, 0, 0, 0, 101, 1, 1, 0, 255, 255, 255, 255, 183, 0, 0, 0, 2, 0, 0, 0, 149, 0, 0, 0, 0, 0, 0, 0 }; + // var prog = [_]u8{ 183, 9, 0, 0, 1, 0, 0, 0, 103, 9, 0, 0, 32, 0, 0, 0, 183, 0, 0, 0, 0, 0, 0, 0, 183, 1, 0, 0, 10, 0, 0, 0, 183, 2, 0, 0, 11, 0, 0, 0, 29, 33, 5, 0, 0, 0, 0, 0, 183, 0, 0, 0, 1, 0, 0, 0, 183, 1, 0, 0, 11, 0, 0, 0, 79, 145, 0, 0, 0, 0, 0, 0, 29, 33, 1, 0, 0, 0, 0, 0, 183, 0, 0, 0, 2, 0, 0, 0, 149, 0, 0, 0, 0, 0, 0, 0 }; + const mem = [_]u8{ 0xaa, 0xbb, 0x11, 0x22, 0xcc, 0xdd }; + const mbuff = [_]u8{0} ** 32; + + + const result = try interpreter.execute_program(allocator, prog[0..], &mem, &mbuff, &syscalls_map); + try expect(result == 0x2); +} + diff --git a/tests/assembler.zig b/tests/assembler.zig index 61e601d..9e59f80 100644 --- a/tests/assembler.zig +++ b/tests/assembler.zig @@ -2,14 +2,27 @@ const std = @import("std"); const testing = std.testing; const assembler = @import("zig-ebpf").assembler; -/// Helper function to compare byte slices and provide detailed error messages -fn expectEqualBytes(expected: []const u8, actual: []const u8) !void { - try testing.expectEqual(expected.len, actual.len); - for (expected, 0..) |exp_byte, i| { - if (exp_byte != actual[i]) { - std.debug.print("Mismatch at index {}: expected 0x{X:0>2}, found 0x{X:0>2}\n", .{ i, exp_byte, actual[i] }); - return error.TestExpectedEqual; +// Helper function to compare binary data and provide detailed error messages +fn expectEqual(expected: []const u8, actual: []const u8) !void { + var expected_hex = std.ArrayList(u8).init(std.testing.allocator); + defer expected_hex.deinit(); + + for (expected, 0..) |byte, index| { + if (index % 8 == 0 and index != 0) { + try expected_hex.appendSlice("\n"); } + try std.fmt.format(expected_hex.writer(), "0x{x:0>2}", .{byte}); + if (index % 8 != 7 and index != expected.len - 1) { + try expected_hex.appendSlice(", "); + } + } + + if (!std.mem.eql(u8, expected_hex.items, actual)) { + std.debug.print("Mismatch:\nExpected: {s}\nActual: {s}\n", .{ + expected_hex.items, + actual, + }); + return error.TestExpectedEqual; } } @@ -18,7 +31,7 @@ test "assembler - add64" { const expected = &[_]u8{ 0x07, 0x01, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00 }; const result = try assembler.assemble(src); defer std.heap.page_allocator.free(result); - try expectEqualBytes(expected, result); + try expectEqual(expected, result); } test "assembler - mov64" { @@ -26,7 +39,7 @@ test "assembler - mov64" { const expected = &[_]u8{ 0xb7, 0x02, 0x00, 0x00, 0x32, 0x00, 0x00, 0x00 }; const result = try assembler.assemble(src); defer std.heap.page_allocator.free(result); - try expectEqualBytes(expected, result); + try expectEqual(expected, result); } test "assembler - mov64 reg" { @@ -34,7 +47,7 @@ test "assembler - mov64 reg" { const expected = &[_]u8{ 0xbf, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; const result = try assembler.assemble(src); defer std.heap.page_allocator.free(result); - try expectEqualBytes(expected, result); + try expectEqual(expected, result); } test "assembler - be16" { @@ -42,7 +55,7 @@ test "assembler - be16" { const expected = &[_]u8{ 0xdc, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00 }; const result = try assembler.assemble(src); defer std.heap.page_allocator.free(result); - try expectEqualBytes(expected, result); + try expectEqual(expected, result); } test "assembler - neg64" { @@ -50,7 +63,7 @@ test "assembler - neg64" { const expected = &[_]u8{ 0x87, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; const result = try assembler.assemble(src); defer std.heap.page_allocator.free(result); - try expectEqualBytes(expected, result); + try expectEqual(expected, result); } test "assembler - exit" { @@ -58,7 +71,7 @@ test "assembler - exit" { const expected = &[_]u8{ 0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; const result = try assembler.assemble(src); defer std.heap.page_allocator.free(result); - try expectEqualBytes(expected, result); + try expectEqual(expected, result); } test "assembler - full program" { @@ -81,7 +94,7 @@ test "assembler - full program" { }; const result = try assembler.assemble(src); defer std.heap.page_allocator.free(result); - try expectEqualBytes(expected, result); + try expectEqual(expected, result); } test "assembler - invalid instruction" { @@ -95,7 +108,7 @@ test "assembler - ja (jump always)" { const expected = &[_]u8{ 0x05, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00 }; const result = try assembler.assemble(src); defer std.heap.page_allocator.free(result); - try expectEqualBytes(expected, result); + try expectEqual(expected, result); } test "assembler - jeq (jump if equal)" { @@ -103,7 +116,7 @@ test "assembler - jeq (jump if equal)" { const expected = &[_]u8{ 0x15, 0x01, 0x03, 0x00, 0x10, 0x00, 0x00, 0x00 }; const result = try assembler.assemble(src); defer std.heap.page_allocator.free(result); - try expectEqualBytes(expected, result); + try expectEqual(expected, result); } test "assembler - call" { @@ -111,7 +124,7 @@ test "assembler - call" { const expected = &[_]u8{ 0x85, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00 }; const result = try assembler.assemble(src); defer std.heap.page_allocator.free(result); - try expectEqualBytes(expected, result); + try expectEqual(expected, result); } test "assembler - ldxw (load word)" { @@ -119,7 +132,7 @@ test "assembler - ldxw (load word)" { const expected = &[_]u8{ 0x61, 0x10, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00 }; const result = try assembler.assemble(src); defer std.heap.page_allocator.free(result); - try expectEqualBytes(expected, result); + try expectEqual(expected, result); } test "assembler - stxb (store byte)" { @@ -127,7 +140,7 @@ test "assembler - stxb (store byte)" { const expected = &[_]u8{ 0x73, 0x12, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00 }; const result = try assembler.assemble(src); defer std.heap.page_allocator.free(result); - try expectEqualBytes(expected, result); + try expectEqual(expected, result); } test "assembler - invalid register" { @@ -172,7 +185,7 @@ test "assembler - complex program" { }; const result = try assembler.assemble(src); defer std.heap.page_allocator.free(result); - try expectEqualBytes(expected, result); + try expectEqual(expected, result); } test "assembler - sub64" { @@ -180,7 +193,7 @@ test "assembler - sub64" { const expected = &[_]u8{ 0x17, 0x01, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00 }; const result = try assembler.assemble(src); defer std.heap.page_allocator.free(result); - try expectEqualBytes(expected, result); + try expectEqual(expected, result); } test "assembler - mul64" { @@ -188,7 +201,7 @@ test "assembler - mul64" { const expected = &[_]u8{ 0x2f, 0x32, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; const result = try assembler.assemble(src); defer std.heap.page_allocator.free(result); - try expectEqualBytes(expected, result); + try expectEqual(expected, result); } test "assembler - div64" { @@ -196,7 +209,7 @@ test "assembler - div64" { const expected = &[_]u8{ 0x37, 0x04, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00 }; const result = try assembler.assemble(src); defer std.heap.page_allocator.free(result); - try expectEqualBytes(expected, result); + try expectEqual(expected, result); } test "assembler - or64" { @@ -204,7 +217,7 @@ test "assembler - or64" { const expected = &[_]u8{ 0x4f, 0x65, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; const result = try assembler.assemble(src); defer std.heap.page_allocator.free(result); - try expectEqualBytes(expected, result); + try expectEqual(expected, result); } test "assembler - and64" { @@ -212,7 +225,7 @@ test "assembler - and64" { const expected = &[_]u8{ 0x57, 0x07, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00 }; const result = try assembler.assemble(src); defer std.heap.page_allocator.free(result); - try expectEqualBytes(expected, result); + try expectEqual(expected, result); } test "assembler - lsh64" { @@ -220,7 +233,7 @@ test "assembler - lsh64" { const expected = &[_]u8{ 0x67, 0x08, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00 }; const result = try assembler.assemble(src); defer std.heap.page_allocator.free(result); - try expectEqualBytes(expected, result); + try expectEqual(expected, result); } test "assembler - rsh64" { @@ -228,7 +241,7 @@ test "assembler - rsh64" { const expected = &[_]u8{ 0x7f, 0xa9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; const result = try assembler.assemble(src); defer std.heap.page_allocator.free(result); - try expectEqualBytes(expected, result); + try expectEqual(expected, result); } test "assembler - mod64" { @@ -236,7 +249,7 @@ test "assembler - mod64" { const expected = &[_]u8{ 0x97, 0x01, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00 }; const result = try assembler.assemble(src); defer std.heap.page_allocator.free(result); - try expectEqualBytes(expected, result); + try expectEqual(expected, result); } test "assembler - xor64" { @@ -244,7 +257,7 @@ test "assembler - xor64" { const expected = &[_]u8{ 0xaf, 0x32, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; const result = try assembler.assemble(src); defer std.heap.page_allocator.free(result); - try expectEqualBytes(expected, result); + try expectEqual(expected, result); } test "assembler - arsh64" { @@ -252,7 +265,7 @@ test "assembler - arsh64" { const expected = &[_]u8{ 0xc7, 0x04, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00 }; const result = try assembler.assemble(src); defer std.heap.page_allocator.free(result); - try expectEqualBytes(expected, result); + try expectEqual(expected, result); } test "assembler - ldabsw" { @@ -260,7 +273,7 @@ test "assembler - ldabsw" { const expected = &[_]u8{ 0x20, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00 }; const result = try assembler.assemble(src); defer std.heap.page_allocator.free(result); - try expectEqualBytes(expected, result); + try expectEqual(expected, result); } test "assembler - ldindw" { @@ -268,7 +281,7 @@ test "assembler - ldindw" { const expected = &[_]u8{ 0x40, 0x10, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00 }; const result = try assembler.assemble(src); defer std.heap.page_allocator.free(result); - try expectEqualBytes(expected, result); + try expectEqual(expected, result); } test "assembler - ldxdw" { @@ -276,7 +289,7 @@ test "assembler - ldxdw" { const expected = &[_]u8{ 0x79, 0x32, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00 }; const result = try assembler.assemble(src); defer std.heap.page_allocator.free(result); - try expectEqualBytes(expected, result); + try expectEqual(expected, result); } test "assembler - jeq" { @@ -284,7 +297,7 @@ test "assembler - jeq" { const expected = &[_]u8{ 0x15, 0x01, 0x05, 0x00, 0x10, 0x00, 0x00, 0x00 }; const result = try assembler.assemble(src); defer std.heap.page_allocator.free(result); - try expectEqualBytes(expected, result); + try expectEqual(expected, result); } test "assembler - jgt" { @@ -292,7 +305,7 @@ test "assembler - jgt" { const expected = &[_]u8{ 0x2d, 0x32, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00 }; const result = try assembler.assemble(src); defer std.heap.page_allocator.free(result); - try expectEqualBytes(expected, result); + try expectEqual(expected, result); } test "assembler - jge" { @@ -300,7 +313,7 @@ test "assembler - jge" { const expected = &[_]u8{ 0x35, 0x04, 0x0f, 0x00, 0x20, 0x00, 0x00, 0x00 }; const result = try assembler.assemble(src); defer std.heap.page_allocator.free(result); - try expectEqualBytes(expected, result); + try expectEqual(expected, result); } test "assembler - jset" { @@ -308,7 +321,7 @@ test "assembler - jset" { const expected = &[_]u8{ 0x45, 0x05, 0x14, 0x00, 0x01, 0x00, 0x00, 0x00 }; const result = try assembler.assemble(src); defer std.heap.page_allocator.free(result); - try expectEqualBytes(expected, result); + try expectEqual(expected, result); } test "assembler - jsgt" { @@ -316,7 +329,7 @@ test "assembler - jsgt" { const expected = &[_]u8{ 0x6d, 0x76, 0x19, 0x00, 0x00, 0x00, 0x00, 0x00 }; const result = try assembler.assemble(src); defer std.heap.page_allocator.free(result); - try expectEqualBytes(expected, result); + try expectEqual(expected, result); } test "assembler - jsge" { @@ -324,5 +337,113 @@ test "assembler - jsge" { const expected = &[_]u8{ 0x75, 0x08, 0x1e, 0x00, 0x30, 0x00, 0x00, 0x00 }; const result = try assembler.assemble(src); defer std.heap.page_allocator.free(result); - try expectEqualBytes(expected, result); + try expectEqual(expected, result); +} + +test "assembler - jeq32" { + const src = + \\mov r9, 1 + \\lsh r9, 32 + \\mov32 r0, 0 + \\mov32 r1, 0xa + \\mov32 r2, 0xb + \\jeq32 r1, r2, +5 + \\mov32 r0, 1 + \\mov32 r1, 0xb + \\or r1, r9 + \\jeq32 r1, r2, +1 + \\mov32 r0, 2 + \\exit + ; + + const expected = &[_]u8{ + 0xb7, 0x09, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, // mov r9, 1 + 0x67, 0x09, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, // lsh r9, 32 + 0xb4, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // mov32 r0, 0 + 0xb4, 0x01, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, // mov32 r1, 0xa + 0xb4, 0x02, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, // mov32 r2, 0xb + 0x1d, 0x21, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, // jeq32 r1, r2, +5 + 0xb4, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, // mov32 r0, 1 + 0xb4, 0x01, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, // mov32 r1, 0xb + 0x4f, 0x91, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // or r1, r9 + 0x1d, 0x21, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, // jeq32 r1, r2, +1 + 0xb4, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, // mov32 r0, 2 + 0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // exit + }; + const result = try assembler.assemble(src); + defer std.heap.page_allocator.free(result); + try expectEqual(expected, result); +} + +test "assembler - ldxb (load byte)" { + const src = "ldxb r0, [r1+0x5]"; + const expected = &[_]u8{ 0x71, 0x10, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00 }; + const result = try assembler.assemble(src); + defer std.heap.page_allocator.free(result); + try expectEqual(expected, result); +} + +test "assembler - jne (jump if not equal)" { + const src = "jne r1, 0x10, +3"; + const expected = &[_]u8{ 0x55, 0x01, 0x03, 0x00, 0x10, 0x00, 0x00, 0x00 }; + const result = try assembler.assemble(src); + defer std.heap.page_allocator.free(result); + try expectEqual(expected, result); +} + +test "assembler - jslt (jump if signed less than)" { + const src = "jslt r2, r3, +5"; + const expected = &[_]u8{ 0xcd, 0x32, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00 }; + const result = try assembler.assemble(src); + defer std.heap.page_allocator.free(result); + try expectEqual(expected, result); +} + +test "assembler - ldabsb (load absolute byte)" { + const src = "ldabsb 0x10"; + const expected = &[_]u8{ 0x30, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00 }; + const result = try assembler.assemble(src); + defer std.heap.page_allocator.free(result); + try expectEqual(expected, result); +} + +test "assembler - ldindb (load indirect byte)" { + const src = "ldindb r1, 0x20"; + const expected = &[_]u8{ 0x50, 0x10, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00 }; + const result = try assembler.assemble(src); + defer std.heap.page_allocator.free(result); + try expectEqual(expected, result); +} + +test "assembler - complex program with various instructions" { + const src = + \\mov r1, 0x1 + \\lsh r1, 32 + \\or r1, 0x2 + \\stxdw [r10-16], r1 + \\ldxdw r2, [r10-16] + \\mov r3, 0x3 + \\mov r3, 0x4 + \\mov r3, 0x5 + \\add r3, r2 + \\mov r0, r3 + \\exit + ; + + const expected = &[_]u8{ + 0xb7, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, // mov r1, 0x1 + 0x67, 0x01, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, // lsh r1, 32 + 0x47, 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, // or r1, 0x2 + 0x7b, 0x1a, 0xf0, 0xff, 0x00, 0x00, 0x00, 0x00, // stxdw [r10-16], r1 + 0x79, 0xa2, 0xf0, 0xff, 0x00, 0x00, 0x00, 0x00, // ldxdw r2, [r10-16] + 0xb7, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, // mov r3, 0x3 + 0xb7, 0x03, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, // mov r3, 0x4 + 0xb7, 0x03, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, // mov r3, 0x5 + 0x0f, 0x23, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // add r3, r2 + 0xbf, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // mov r0, r3 + 0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // exit + }; + const result = try assembler.assemble(src); + defer std.heap.page_allocator.free(result); + try expectEqual(expected, result); }