Compare commits

..

No commits in common. "99fd7fabd7a5496f3ee232767f3b623f7675eacc" and "731e5bd45df816b4da4ffe86210e9cfa5926d386" have entirely different histories.

10 changed files with 64 additions and 192 deletions

View File

@ -1,23 +0,0 @@
; ========================================================================
;
; (C) Copyright 2023 by Molly Rocket, Inc., All Rights Reserved.
;
; This software is provided 'as-is', without any express or implied
; warranty. In no event will the authors be held liable for any damages
; arising from the use of this software.
;
; Please see https://computerenhance.com for further information
;
; ========================================================================
; ========================================================================
; LISTING 48
; ========================================================================
bits 16
mov cx, 200
mov bx, cx
add cx, 1000
mov bx, 2000
sub cx, bx

View File

@ -1,12 +0,0 @@
--- test\listing_0048_ip_register execution ---
mov cx, 200 ; cx:0x0->0xc8 ip:0x0->0x3
mov bx, cx ; bx:0x0->0xc8 ip:0x3->0x5
add cx, 1000 ; cx:0xc8->0x4b0 ip:0x5->0x9 flags:->A
mov bx, 2000 ; bx:0xc8->0x7d0 ip:0x9->0xc
sub cx, bx ; cx:0x4b0->0xfce0 ip:0xc->0xe flags:A->CS
Final registers:
bx: 0x07d0 (2000)
cx: 0xfce0 (64736)
ip: 0x000e (14)
flags: CS

View File

@ -1,24 +0,0 @@
; ========================================================================
;
; (C) Copyright 2023 by Molly Rocket, Inc., All Rights Reserved.
;
; This software is provided 'as-is', without any express or implied
; warranty. In no event will the authors be held liable for any damages
; arising from the use of this software.
;
; Please see https://computerenhance.com for further information
;
; ========================================================================
; ========================================================================
; LISTING 49
; ========================================================================
bits 16
mov cx, 3
mov bx, 1000
loop_start:
add bx, 10
sub cx, 1
jnz loop_start

View File

@ -1,17 +0,0 @@
--- test\listing_0049_conditional_jumps execution ---
mov cx, 3 ; cx:0x0->0x3 ip:0x0->0x3
mov bx, 1000 ; bx:0x0->0x3e8 ip:0x3->0x6
add bx, 10 ; bx:0x3e8->0x3f2 ip:0x6->0x9 flags:->A
sub cx, 1 ; cx:0x3->0x2 ip:0x9->0xc flags:A->
jne $-6 ; ip:0xc->0x6
add bx, 10 ; bx:0x3f2->0x3fc ip:0x6->0x9 flags:->P
sub cx, 1 ; cx:0x2->0x1 ip:0x9->0xc flags:P->
jne $-6 ; ip:0xc->0x6
add bx, 10 ; bx:0x3fc->0x406 ip:0x6->0x9 flags:->PA
sub cx, 1 ; cx:0x1->0x0 ip:0x9->0xc flags:PA->PZ
jne $-6 ; ip:0xc->0xe
Final registers:
bx: 0x0406 (1030)
ip: 0x000e (14)
flags: PZ

View File

@ -66,9 +66,7 @@ get_op :: proc(inst: Instruction) -> (Op, bool) {
} }
return op, interseg return op, interseg
} }
parse_operand :: proc(inst: InstructionInfo, opinfo: OperandInfo, data: []u8, processed: ^int, word: bool, has_segreg: Maybe(RegisterId)) -> Operand {
parse_operand :: proc(inst: InstructionInfo, opinfo: OperandInfo, data: []u8, processed: ^int,
total_bytes_processed: int, word: bool, has_segreg: Maybe(RegisterId)) -> Operand {
operand: Operand = None{} operand: Operand = None{}
switch opinfo { switch opinfo {
case .None: case .None:
@ -156,7 +154,7 @@ parse_operand :: proc(inst: InstructionInfo, opinfo: OperandInfo, data: []u8, pr
operand = Repeat(fmt.aprintf("%s%s", rep, w)) operand = Repeat(fmt.aprintf("%s%s", rep, w))
processed^ += 1 processed^ += 1
case .DirectWithinSegment: case .DirectWithinSegment:
value := (int)(get_i16(data[1:])) + total_bytes_processed + 3 value := (int)(get_i16(data[1:])) + CPU.total_bytes_processed + 3
operand = Immediate { value = i16(value), size = .Signed16 } operand = Immediate { value = i16(value), size = .Signed16 }
processed^ += 2 processed^ += 2
case .Intersegment: case .Intersegment:
@ -169,11 +167,10 @@ parse_operand :: proc(inst: InstructionInfo, opinfo: OperandInfo, data: []u8, pr
return operand return operand
} }
decode_data :: proc(inst_list: ^[dynamic]Instruction, data: []u8, bytes_to_read: int) -> int { decode_data :: proc(inst_list: ^[dynamic]Instruction, data: []u8, bytes_to_read: int) {
idx := 0 idx := 0
has_segment: Maybe(RegisterId) has_segment: Maybe(RegisterId)
has_lock: bool has_lock: bool
total_bytes_processed: int
for idx < bytes_to_read { for idx < bytes_to_read {
instruction: Instruction instruction: Instruction
processed := 1 processed := 1
@ -237,8 +234,8 @@ decode_data :: proc(inst_list: ^[dynamic]Instruction, data: []u8, bytes_to_read:
case .Always16: word = true case .Always16: word = true
} }
dst_opr = parse_operand(inst, inst.dst, data[idx:], &processed, total_bytes_processed, word, has_segment) dst_opr = parse_operand(inst, inst.dst, data[idx:], &processed, word, has_segment)
src_opr = parse_operand(inst, inst.src, data[idx:], &processed, total_bytes_processed, word, has_segment) src_opr = parse_operand(inst, inst.src, data[idx:], &processed, word, has_segment)
if flip { if flip {
src_opr, dst_opr = dst_opr, src_opr src_opr, dst_opr = dst_opr, src_opr
@ -255,7 +252,6 @@ decode_data :: proc(inst_list: ^[dynamic]Instruction, data: []u8, bytes_to_read:
instruction.info = inst instruction.info = inst
instruction.has_lock = has_lock instruction.has_lock = has_lock
instruction.has_segment = has_segment instruction.has_segment = has_segment
instruction.segment_offset += total_bytes_processed
instruction.opname = inst.opname instruction.opname = inst.opname
instruction.opname,instruction.indirect_intersegment = get_op(instruction) instruction.opname,instruction.indirect_intersegment = get_op(instruction)
@ -266,7 +262,6 @@ decode_data :: proc(inst_list: ^[dynamic]Instruction, data: []u8, bytes_to_read:
has_lock = false has_lock = false
has_segment = nil has_segment = nil
total_bytes_processed = idx CPU.total_bytes_processed = idx
} }
return total_bytes_processed
} }

View File

@ -6,15 +6,13 @@ import "core:math"
import "core:strings" import "core:strings"
import "core:reflect" import "core:reflect"
get_ip :: proc(cpu: ^Cpu) -> int { return int(cpu.registers[.ip].full) } get_operand_value :: proc(operand: Operand) -> i16 {
get_operand_value :: proc(cpu: ^Cpu, operand: Operand) -> i16 {
#partial switch opr in operand { #partial switch opr in operand {
case Immediate: case Immediate:
// fmt.printfln("0x%4x %d", i16(opr.value), opr.value) // fmt.printfln("0x%4x %d", i16(opr.value), opr.value)
return i16(opr.value) return i16(opr.value)
case RegisterId: case RegisterId:
reg_val := cpu.registers[opr.name] reg_val := CPU.registers[opr.name]
switch opr.access { switch opr.access {
case .Low, .High: case .Low, .High:
return i16(opr.access == .Low ? reg_val.low : reg_val.high) return i16(opr.access == .Low ? reg_val.low : reg_val.high)
@ -25,14 +23,14 @@ get_operand_value :: proc(cpu: ^Cpu, operand: Operand) -> i16 {
return 0 return 0
} }
set_register_value :: proc(cpu: ^Cpu, reg: RegisterId, value: i16) { set_register_value :: proc(reg: RegisterId, value: i16) {
switch reg.access { switch reg.access {
case .Low: case .Low:
cpu.registers[reg.name].low = u8(value) CPU.registers[reg.name].low = u8(value)
case .High: case .High:
cpu.registers[reg.name].high = u8(value) CPU.registers[reg.name].high = u8(value)
case .Full: case .Full:
cpu.registers[reg.name].full = i16(value) CPU.registers[reg.name].full = i16(value)
} }
} }
@ -41,19 +39,19 @@ get_cpu_register_by_name :: proc(cpu: ^Cpu, name: string) -> ^RegisterValue {
return &cpu.registers[reg] return &cpu.registers[reg]
} }
check_zero_flag :: proc(cpu: ^Cpu, value: i16) { check_zero_flag :: proc(value: i16) {
cpu.flags[.ZF] = value == 0 CPU.flags[.ZF] = value == 0
} }
check_sign_flag :: proc(cpu: ^Cpu, value: i16) { check_sign_flag :: proc(value: i16) {
cpu.flags[.SF] = (value >> 15) & 0x1 == 1 CPU.flags[.SF] = (value >> 15) & 0x1 == 1
} }
check_carry_flag :: proc(cpu: ^Cpu, dst: i16, src: i16, is_add: bool) { check_carry_flag :: proc(dst: i16, src: i16, is_add: bool) {
cpu.flags[.CF] = is_add ? u32(dst) + u32(src) > 0xFFFF : src > dst CPU.flags[.CF] = is_add ? u32(dst) + u32(src) > 0xFFFF : src > dst
} }
check_parity_flag :: proc(cpu: ^Cpu, value: i16) { check_parity_flag :: proc(value: i16) {
val := value val := value
bit_count := 0 bit_count := 0
val &= 0x00FF val &= 0x00FF
@ -63,72 +61,49 @@ check_parity_flag :: proc(cpu: ^Cpu, value: i16) {
} }
val >>= 1 val >>= 1
} }
cpu.flags[.PF] = bit_count % 2 == 0 CPU.flags[.PF] = val % 2 == 0
} }
check_auxiliary_carry_flag :: proc(cpu: ^Cpu, dst: i16, src: i16, is_add: bool) { check_auxiliary_carry_flag :: proc(dst: i16, src: i16, is_add: bool) {
lhs, rhs := dst & 0xF, src & 0xF lhs, rhs := dst & 0xF, src & 0xF
cpu.flags[.AF] = is_add ? lhs + rhs > 15 : lhs < rhs CPU.flags[.AF] = is_add ? lhs + rhs > 15 : lhs < rhs
} }
check_flags :: proc(cpu: ^Cpu, value: i16) { check_flags :: proc(value: i16) {
check_zero_flag(cpu, value) check_zero_flag(value)
check_sign_flag(cpu, value) check_sign_flag(value)
check_parity_flag(cpu, value) check_parity_flag(value)
} }
execute_instruction :: proc(cpu: ^Cpu, inst: Instruction) { execute_instruction :: proc(inst: Instruction) {
if reg,ok := inst.dst.(RegisterId); ok { if reg,ok := inst.dst.(RegisterId); ok {
#partial switch inst.opname { #partial switch inst.opname {
case .MOV: case .MOV:
src_val := get_operand_value(cpu, inst.src) src_val := get_operand_value(inst.src)
set_register_value(cpu, reg, src_val) set_register_value(reg, src_val)
case .ADD: case .ADD:
src_val := get_operand_value(cpu, inst.src) src_val := get_operand_value(inst.src)
dst_val := get_operand_value(cpu, inst.dst) dst_val := get_operand_value(inst.dst)
val := i16(cpu.registers[reg.name].full) + src_val val := i16(CPU.registers[reg.name].full) + src_val
set_register_value(cpu, reg, val) set_register_value(reg, val)
check_flags(cpu, val) check_flags(val)
check_auxiliary_carry_flag(cpu, dst_val, src_val, true) check_auxiliary_carry_flag(dst_val, src_val, true)
check_carry_flag(cpu, dst_val, src_val, true) check_carry_flag(dst_val, src_val, true)
case .SUB: case .SUB:
src_val := get_operand_value(cpu, inst.src) src_val := get_operand_value(inst.src)
dst_val := get_operand_value(cpu, inst.dst) dst_val := get_operand_value(inst.dst)
val := i16(cpu.registers[reg.name].full) - src_val val := i16(CPU.registers[reg.name].full) - src_val
set_register_value(cpu, reg, val) set_register_value(reg, val)
check_flags(cpu, val) check_flags(val)
check_auxiliary_carry_flag(cpu, dst_val, src_val, false) check_auxiliary_carry_flag(dst_val, src_val, false)
check_carry_flag(cpu, dst_val, src_val, false) check_carry_flag(dst_val, src_val, false)
case .CMP: case .CMP:
src_val := get_operand_value(cpu, inst.src) src_val := get_operand_value(inst.src)
dst_val := get_operand_value(cpu, inst.dst) dst_val := get_operand_value(inst.dst)
val := i16(cpu.registers[reg.name].full) - src_val val := i16(CPU.registers[reg.name].full) - src_val
check_flags(cpu, val) check_flags(val)
check_auxiliary_carry_flag(cpu, dst_val, src_val, false) check_auxiliary_carry_flag(dst_val, src_val, false)
check_carry_flag(cpu, dst_val, src_val, false) check_carry_flag(dst_val, src_val, false)
}
} else if jmp_offset,ok := inst.src.(Jump); ok {
#partial switch inst.opname {
case .JNZ, .JNE:
if !cpu.flags[.ZF] {
cpu.registers[.ip].full += i16(jmp_offset)
if jmp_offset < 0 {
cpu.registers[.ip].full -= i16(inst.bytes_read)
} }
} }
} }
}
}
execute_instructions :: proc(cpu: ^Cpu, instructions: DecodedInstructions) {
halt := false
fmt.println(instructions.total_bytes_decoded)
for !halt && get_ip(cpu) < instructions.total_bytes_decoded {
inst,ok := instructions.inst_map[get_ip(cpu)]
if !ok {
// Something went wrong with a jump, most likely
}
cpu.registers[.ip].full += i16(inst.bytes_read)
execute_instruction(cpu, inst)
}
}

View File

@ -76,7 +76,6 @@ Op :: enum {
CALL, CALL,
JMP, JMP,
JNZ, JNZ,
JNE,
JNGE, JNGE,
JE, JE,
JZ, JZ,
@ -91,6 +90,7 @@ Op :: enum {
JBE, JBE,
JO, JO,
JS, JS,
JNE,
JNL, JNL,
JGE, JGE,
JNLE, JNLE,

View File

@ -7,6 +7,10 @@ import "core:math"
import "core:strings" import "core:strings"
import "core:reflect" import "core:reflect"
CPU := Cpu {
memory = make([dynamic]u8, 65536),
}
main :: proc() { main :: proc() {
f,err := os.open(os.args[1]) f,err := os.open(os.args[1])
if err != os.ERROR_NONE { if err != os.ERROR_NONE {
@ -32,26 +36,15 @@ main :: proc() {
instruction_list := make([dynamic]string, 0, 512) instruction_list := make([dynamic]string, 0, 512)
instructions_list := make([dynamic]Instruction, 0, 512) instructions_list := make([dynamic]Instruction, 0, 512)
total_bytes := decode_data(&instructions_list, data[:], bytes_read) decode_data(&instructions_list, data[:], bytes_read)
inst_map := make(map[int]Instruction)
for inst in instructions_list { for inst in instructions_list {
inst_map[inst.segment_offset] = inst execute_instruction(inst)
} }
decoded_insts := DecodedInstructions {
inst_list = instructions_list,
inst_map = inst_map,
total_bytes_decoded = total_bytes
}
cpu := Cpu {
memory = make([dynamic]u8, 65536),
}
execute_instructions(&cpu, decoded_insts)
if what_to_print == "registers" || what_to_print == "all" { if what_to_print == "registers" || what_to_print == "all" {
fmt.println("Registers") fmt.println("Registers")
for reg_val,name in cpu.registers { for reg_val,name in CPU.registers {
full := fmt.aprintf("%s: %d ", name, i16(reg_val.full)) full := fmt.aprintf("%s: %d ", name, i16(reg_val.full))
hex := fmt.aprintf("0x%04x ", u16(reg_val.full)) hex := fmt.aprintf("0x%04x ", u16(reg_val.full))
fmt.printf("%s %*[1]s %s %*[4]s %08b %08b", fmt.printf("%s %*[1]s %s %*[4]s %08b %08b",
@ -59,7 +52,7 @@ main :: proc() {
fmt.println() fmt.println()
} }
fmt.println("\nFlags") fmt.println("\nFlags")
for state,flag in cpu.flags { for state,flag in CPU.flags {
fmt.printf("%c:%d ",reflect.enum_string(flag)[0], state ? 1 : 0) fmt.printf("%c:%d ",reflect.enum_string(flag)[0], state ? 1 : 0)
} }
fmt.println() fmt.println()
@ -68,16 +61,16 @@ main :: proc() {
path,ok := strings.replace(os.args[1], ".bin", ".txt", 1) path,ok := strings.replace(os.args[1], ".bin", ".txt", 1)
ref_cpu,_ := extract_reference_cpu_state(path) ref_cpu,_ := extract_reference_cpu_state(path)
for reg_val,name in ref_cpu.registers { for reg_val,name in ref_cpu.registers {
if cpu.registers[name].full != reg_val.full { if CPU.registers[name].full != reg_val.full {
msg := "%s register does not match reference - Expected 0x%04x | Actual 0x%04x" msg := "%s register does not match reference - Expected 0x%04x | Actual 0x%04x"
fmt.printfln(msg, name, reg_val.full, cpu.registers[name].full) fmt.printfln(msg, name, reg_val.full, CPU.registers[name].full)
failed_ref = true failed_ref = true
} }
} }
for f in Flag { for f in Flag {
if ref_cpu.flags[f] != cpu.flags[f] { if ref_cpu.flags[f] != CPU.flags[f] {
msg := "%s flag does not match reference - Expected %d | Actual %d" msg := "%s flag does not match reference - Expected %d | Actual %d"
fmt.printfln(msg, f, ref_cpu.flags[f] ? 1 : 0, cpu.flags[f] ? 1 : 0) fmt.printfln(msg, f, ref_cpu.flags[f] ? 1 : 0, CPU.flags[f] ? 1 : 0)
failed_ref = true failed_ref = true
} }
} }

View File

@ -20,7 +20,6 @@ extract_reference_cpu_state :: proc(filename: string) -> (Cpu, bool) {
lines := strings.split(content, "\n") lines := strings.split(content, "\n")
defer delete(lines) defer delete(lines)
ignore_ip_reg := true
for line in lines { for line in lines {
space_count := 0 space_count := 0
for c,i in line { for c,i in line {
@ -42,9 +41,6 @@ extract_reference_cpu_state :: proc(filename: string) -> (Cpu, bool) {
} }
} else { } else {
reg_name := line[i:i+2] reg_name := line[i:i+2]
if reg_name == "ip" {
ignore_ip_reg = false
}
reg_value := get_cpu_register_by_name(&cpu, reg_name) reg_value := get_cpu_register_by_name(&cpu, reg_name)
hex_string := line[i+6:i+10] hex_string := line[i+6:i+10]
if hex_num,ok := strconv.parse_int(hex_string, 16); ok { if hex_num,ok := strconv.parse_int(hex_string, 16); ok {
@ -56,9 +52,5 @@ extract_reference_cpu_state :: proc(filename: string) -> (Cpu, bool) {
} }
} }
if ignore_ip_reg {
cpu.registers[.ip] = cpu.registers[.ip]
}
return cpu, true return cpu, true
} }

View File

@ -13,7 +13,6 @@ Register :: enum {
cs, cs,
ss, ss,
ds, ds,
ip,
} }
RegisterValue :: struct #raw_union { RegisterValue :: struct #raw_union {
@ -149,19 +148,13 @@ Instruction :: struct {
has_segment: Maybe(RegisterId), has_segment: Maybe(RegisterId),
has_lock: bool, has_lock: bool,
bytes_read: int, bytes_read: int,
segment_offset: int,
raw_data: []u8, raw_data: []u8,
debug_msg: string, debug_msg: string,
} }
DecodedInstructions :: struct {
inst_list: [dynamic]Instruction,
inst_map: map[int]Instruction,
total_bytes_decoded: int
}
Cpu :: struct { Cpu :: struct {
flags: [Flag]bool, flags: [Flag]bool,
registers: [Register]RegisterValue, registers: [Register]RegisterValue,
memory: [dynamic]u8, memory: [dynamic]u8,
total_bytes_processed: int
} }