Compare commits
3 Commits
da78d875c1
...
831a307975
Author | SHA1 | Date | |
---|---|---|---|
831a307975 | |||
ea7b65994b | |||
ab2b107e7e |
35
asm_files/list-0053.asm
Normal file
35
asm_files/list-0053.asm
Normal file
@ -0,0 +1,35 @@
|
||||
; ========================================================================
|
||||
;
|
||||
; (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 53
|
||||
; ========================================================================
|
||||
|
||||
bits 16
|
||||
|
||||
mov dx, 6
|
||||
mov bp, 1000
|
||||
|
||||
mov si, 0
|
||||
init_loop_start:
|
||||
mov word [bp + si], si
|
||||
add si, 2
|
||||
cmp si, dx
|
||||
jnz init_loop_start
|
||||
|
||||
mov bx, 0
|
||||
mov si, dx
|
||||
sub bp, 2
|
||||
add_loop_start:
|
||||
add bx, word [bp + si]
|
||||
sub si, 2
|
||||
jnz add_loop_start
|
35
asm_files/list-0053.txt
Normal file
35
asm_files/list-0053.txt
Normal file
@ -0,0 +1,35 @@
|
||||
--- test\listing_0053_add_loop_challenge execution ---
|
||||
mov dx, 6 ; dx:0x0->0x6 ip:0x0->0x3
|
||||
mov bp, 1000 ; bp:0x0->0x3e8 ip:0x3->0x6
|
||||
mov si, 0 ; ip:0x6->0x9
|
||||
mov word [bp+si], si ; ip:0x9->0xb
|
||||
add si, 2 ; si:0x0->0x2 ip:0xb->0xe
|
||||
cmp si, dx ; ip:0xe->0x10 flags:->CPAS
|
||||
jne $-7 ; ip:0x10->0x9
|
||||
mov word [bp+si], si ; ip:0x9->0xb
|
||||
add si, 2 ; si:0x2->0x4 ip:0xb->0xe flags:CPAS->
|
||||
cmp si, dx ; ip:0xe->0x10 flags:->CAS
|
||||
jne $-7 ; ip:0x10->0x9
|
||||
mov word [bp+si], si ; ip:0x9->0xb
|
||||
add si, 2 ; si:0x4->0x6 ip:0xb->0xe flags:CAS->P
|
||||
cmp si, dx ; ip:0xe->0x10 flags:P->PZ
|
||||
jne $-7 ; ip:0x10->0x12
|
||||
mov bx, 0 ; ip:0x12->0x15
|
||||
mov si, dx ; ip:0x15->0x17
|
||||
sub bp, 2 ; bp:0x3e8->0x3e6 ip:0x17->0x1a flags:PZ->
|
||||
add bx, [bp+si] ; bx:0x0->0x4 ip:0x1a->0x1c
|
||||
sub si, 2 ; si:0x6->0x4 ip:0x1c->0x1f
|
||||
jne $-5 ; ip:0x1f->0x1a
|
||||
add bx, [bp+si] ; bx:0x4->0x6 ip:0x1a->0x1c flags:->P
|
||||
sub si, 2 ; si:0x4->0x2 ip:0x1c->0x1f flags:P->
|
||||
jne $-5 ; ip:0x1f->0x1a
|
||||
add bx, [bp+si] ; ip:0x1a->0x1c flags:->P
|
||||
sub si, 2 ; si:0x2->0x0 ip:0x1c->0x1f flags:P->PZ
|
||||
jne $-5 ; ip:0x1f->0x21
|
||||
|
||||
Final registers:
|
||||
bx: 0x0006 (6)
|
||||
dx: 0x0006 (6)
|
||||
bp: 0x03e6 (998)
|
||||
ip: 0x0021 (33)
|
||||
flags: PZ
|
@ -102,10 +102,13 @@ parse_operand :: proc(inst: InstructionInfo, opinfo: OperandInfo, data: []u8, pr
|
||||
op = MemoryAddr{ addr_id = rm }
|
||||
}
|
||||
} else if mod == 1 {
|
||||
op = MemoryAddr{ addr_id = rm , displacement_value = (i16)(data[2]) , displacement_size = .Signed8 }
|
||||
// NOTE: Per the 8086 manual in order to detect signed displacement, you have
|
||||
// to sign extend a byte to a word. In Odin like in other languages, first
|
||||
// cast to an i8, before casting to an i16 and it'll sign extend
|
||||
op = MemoryAddr{ addr_id = rm , displacement = i16(i8(data[2])) }
|
||||
processed^ += 1
|
||||
} else if mod == 2 {
|
||||
op = MemoryAddr{ addr_id = rm , displacement_value = get_i16(data[2:]) , displacement_size = .Signed16 }
|
||||
op = MemoryAddr{ addr_id = rm , displacement = get_i16(data[2:]) }
|
||||
processed^ += 2
|
||||
} else if mod == 3 {
|
||||
if word {
|
||||
@ -122,13 +125,10 @@ parse_operand :: proc(inst: InstructionInfo, opinfo: OperandInfo, data: []u8, pr
|
||||
word_signed &&= data[0] & 0b0000_0010 == 0
|
||||
}
|
||||
value: i16 = word_signed ? get_i16(data[data_idx:]) : i16(i8(data[data_idx]))
|
||||
operand = Immediate { value = value, size = word_signed ? .Signed16 : .Signed8 }
|
||||
operand = Immediate { value = value, size = word_signed ? ._16 : ._8 }
|
||||
processed^ += word_signed ? 2 : 1
|
||||
case .ImmediateUnsigned:
|
||||
operand = Immediate { value = i16(data[processed^]), size = .Unsigned8 }
|
||||
processed^ += 1
|
||||
case .Accumulator:
|
||||
operand = RegisterId { name = Register(0), access = word ? .Full : .Low }
|
||||
operand = RegisterId { name = .ax, access = word ? .Full : .Low }
|
||||
case .DirectAddress:
|
||||
// operand = DirectAddress { value = get_i16(data[1:]) }
|
||||
operand = (DirectAddress)(get_i16(data[1:]))
|
||||
@ -138,10 +138,17 @@ parse_operand :: proc(inst: InstructionInfo, opinfo: OperandInfo, data: []u8, pr
|
||||
// NOTE: In order to mimic the label offset, you have to take the value you got and add two
|
||||
operand = (Jump)((i8)(data[1]) + 2)
|
||||
case .VariablePort:
|
||||
operand = RegisterId { name = Register.dx, access = .Full }
|
||||
// NOTE: This isn't documented in the manual, but you can tell if we need to use
|
||||
// the dx register or an immediate if the 4th LSB bit is set
|
||||
if (data[0] & 0b1000) >> 3 == 1 {
|
||||
operand = RegisterId { name = .dx, access = .Full }
|
||||
} else {
|
||||
operand = Immediate { value = i16(i8(data[1])), size = ._8 }
|
||||
processed^ += 1
|
||||
}
|
||||
case .ShiftRotate:
|
||||
v_flag := data[0] & 0b10 != 0
|
||||
operand = v_flag ? RegisterId { name = Register(1), access = .Low } : Immediate { value = 1 }
|
||||
operand = v_flag ? RegisterId { name = .cx, access = .Low } : Immediate { value = 1 }
|
||||
case .Repeat:
|
||||
bits := (data[1] & 0b1110) >> 1
|
||||
w := (data[1] & 0b1) == 1 ? "w" : "b"
|
||||
@ -157,7 +164,7 @@ parse_operand :: proc(inst: InstructionInfo, opinfo: OperandInfo, data: []u8, pr
|
||||
processed^ += 1
|
||||
case .DirectWithinSegment:
|
||||
value := (int)(get_i16(data[1:])) + total_bytes_processed + 3
|
||||
operand = Immediate { value = i16(value), size = .Signed16 }
|
||||
operand = Immediate { value = i16(value), size = ._16 }
|
||||
processed^ += 2
|
||||
case .Intersegment:
|
||||
operand = Intersegment {
|
||||
|
@ -5,6 +5,7 @@ import "core:fmt"
|
||||
import "core:math"
|
||||
import "core:strings"
|
||||
import "core:reflect"
|
||||
import "core:strconv"
|
||||
|
||||
get_ip :: proc(cpu: ^Cpu) -> int { return int(cpu.registers[.ip].full) }
|
||||
|
||||
@ -38,7 +39,7 @@ get_operand_value :: proc(cpu: ^Cpu, operand: Operand) -> i16 {
|
||||
value := i16(u16(cpu.memory[opr+1] << 8) | u16(cpu.memory[opr]))
|
||||
return value
|
||||
case MemoryAddr:
|
||||
idx := get_effective_address_value(cpu, opr.addr_id) + opr.displacement_value
|
||||
idx := get_effective_address_value(cpu, opr.addr_id) + opr.displacement
|
||||
value := i16(u16(cpu.memory[idx+1] << 8) | u16(cpu.memory[idx]))
|
||||
// fmt.println("Checking", idx)
|
||||
// for i in 0..<6 {
|
||||
@ -136,7 +137,7 @@ execute_instruction :: proc(cpu: ^Cpu, inst: Instruction) {
|
||||
#partial switch inst.opname {
|
||||
case .MOV:
|
||||
if imm,ok := inst.src.(Immediate); ok {
|
||||
if imm.size == .Signed16 {
|
||||
if imm.size == ._16 {
|
||||
cpu.memory[addr] = u8(imm.value & 0x00FF)
|
||||
cpu.memory[addr+1] = u8((u16(imm.value) & 0xFF00) >> 8)
|
||||
} else {
|
||||
@ -150,7 +151,7 @@ execute_instruction :: proc(cpu: ^Cpu, inst: Instruction) {
|
||||
value := get_operand_value(cpu, inst.src)
|
||||
|
||||
effective_addr_val := get_effective_address_value(cpu, mem_addr.addr_id)
|
||||
addr := effective_addr_val + mem_addr.displacement_value
|
||||
addr := effective_addr_val + mem_addr.displacement
|
||||
cpu.memory[addr] = u8(value & 0x00FF)
|
||||
cpu.memory[addr+1] = u8((u16(value) & 0xFF00) >> 8)
|
||||
|
||||
|
@ -194,16 +194,13 @@ instructions := [?]InstructionInfo {
|
||||
dst = .Accumulator, src = .Register,
|
||||
reg_info = .FirstByteLast3, has_flip = true, word_size = .Always16 },
|
||||
{ opname = .IN, desc = "", mask = 0b11111110, encoding = 0b11100100,
|
||||
dst = .Accumulator, src = .ImmediateUnsigned },
|
||||
dst = .Accumulator, src = .Immediate, word_size = .LastBit },
|
||||
{ opname = .IN, desc = "", mask = 0b11111110, encoding = 0b11101100,
|
||||
dst = .Accumulator, src = .VariablePort,
|
||||
word_size = .LastBit, },
|
||||
dst = .Accumulator, src = .VariablePort, word_size = .LastBit, },
|
||||
{ opname = .OUT, desc = "", mask = 0b11111110, encoding = 0b11100110,
|
||||
dst = .ImmediateUnsigned, src = .Accumulator,
|
||||
word_size = .LastBit, },
|
||||
dst = .VariablePort, src = .Accumulator, word_size = .LastBit },
|
||||
{ opname = .OUT, desc = "", mask = 0b11111110, encoding = 0b11101110,
|
||||
dst = .VariablePort, src = .Accumulator,
|
||||
word_size = .LastBit, },
|
||||
dst = .VariablePort, src = .Accumulator, word_size = .LastBit, },
|
||||
{ opname = .TEST, desc = "", mask = 0b11111110, encoding = 0b10101000,
|
||||
dst = .Accumulator, src = .Immediate, word_size = .LastBit },
|
||||
{ opname = .XLAT, desc = "", mask = 0b11111111, encoding = 0b11010111,},
|
||||
|
@ -39,7 +39,7 @@ calculate_effective_address :: proc(r_m: u8) -> string {
|
||||
|
||||
get_memory_string :: proc(memoryAddr: MemoryAddr, has_segment: Maybe(RegisterId)) -> string {
|
||||
disp: string
|
||||
value := memoryAddr.displacement_value
|
||||
value := memoryAddr.displacement
|
||||
if value != 0 {
|
||||
disp = fmt.aprintf(" %s %d", value > 0 ? "+" : "-", math.abs(value))
|
||||
}
|
||||
@ -70,14 +70,7 @@ get_operand_string :: proc(operand: Operand, has_segment: Maybe(RegisterId)) ->
|
||||
case RegisterId:
|
||||
string_val = get_register_name(val)
|
||||
case Immediate:
|
||||
switch val.size {
|
||||
case .Signed8:
|
||||
string_val = fmt.aprintf("%d", i8(val.value))
|
||||
case .Unsigned8:
|
||||
string_val = fmt.aprintf("%d", u8(val.value))
|
||||
case .Signed16:
|
||||
string_val = fmt.aprintf("%d", i16(val.value))
|
||||
}
|
||||
string_val = fmt.aprintf("%d", i16(val.value))
|
||||
case MemoryAddr:
|
||||
string_val = get_memory_string(val, has_segment)
|
||||
case DirectAddress:
|
||||
@ -132,13 +125,7 @@ get_instruction_string :: proc(inst_info: InstructionInfo, instruction: Instruct
|
||||
}
|
||||
fmt.sbprintf(&instruction_builder, "%s%s %s%s", opname, interseg_string, size_string, src_str)
|
||||
} else {
|
||||
// note: i don't know why this is the case, but only the move has the word/byte
|
||||
// keyword next to the immediate, but other instructions have it on the memory address
|
||||
if opname == "mov" {
|
||||
fmt.sbprintf(&instruction_builder, "%s %s, %s%s", opname, dst_str, size_string, src_str)
|
||||
} else {
|
||||
fmt.sbprintf(&instruction_builder, "%s %s%s, %s", opname, size_string, dst_str, src_str)
|
||||
}
|
||||
fmt.sbprintf(&instruction_builder, "%s %s%s, %s", opname, size_string, dst_str, src_str)
|
||||
}
|
||||
|
||||
// Prepare padding and comment to add debug info
|
||||
|
11
sim8086.odin
11
sim8086.odin
@ -6,6 +6,7 @@ import "core:fmt"
|
||||
import "core:math"
|
||||
import "core:strings"
|
||||
import "core:reflect"
|
||||
import "core:strconv"
|
||||
|
||||
main :: proc() {
|
||||
f,err := os.open(os.args[1])
|
||||
@ -47,7 +48,12 @@ main :: proc() {
|
||||
memory = make([dynamic]u8, 1024 * 1024), // 1,048,576
|
||||
}
|
||||
|
||||
execute_instructions(&cpu, decoded_insts)
|
||||
num_idx := strings.last_index(os.args[1], "list-") + 5
|
||||
if listing_num,ok := strconv.parse_int(os.args[1][num_idx:num_idx+4]); ok {
|
||||
if listing_num > 42 {
|
||||
execute_instructions(&cpu, decoded_insts)
|
||||
}
|
||||
}
|
||||
|
||||
if what_to_print == "registers" || what_to_print == "all" {
|
||||
fmt.println("Registers")
|
||||
@ -66,8 +72,9 @@ main :: proc() {
|
||||
|
||||
failed_ref: bool
|
||||
path,ok := strings.replace(os.args[1], ".bin", ".txt", 1)
|
||||
ref_cpu,_ := extract_reference_cpu_state(path)
|
||||
ref_cpu,_ := parse_reference_cpu_state(path)
|
||||
for reg_val,name in ref_cpu.registers {
|
||||
if name == .ip && reg_val.full == 0 do continue
|
||||
if cpu.registers[name].full != reg_val.full {
|
||||
msg := "%s register does not match reference - Expected 0x%04x | Actual 0x%04x"
|
||||
fmt.printfln(msg, name, reg_val.full, cpu.registers[name].full)
|
||||
|
10
testing.odin
10
testing.odin
@ -7,7 +7,7 @@ import "core:strings"
|
||||
import "core:strconv"
|
||||
import "core:reflect"
|
||||
|
||||
extract_reference_cpu_state :: proc(filename: string) -> (Cpu, bool) {
|
||||
parse_reference_cpu_state :: proc(filename: string) -> (Cpu, bool) {
|
||||
cpu: Cpu
|
||||
|
||||
data,ok := os.read_entire_file(filename)
|
||||
@ -20,7 +20,6 @@ extract_reference_cpu_state :: proc(filename: string) -> (Cpu, bool) {
|
||||
lines := strings.split(content, "\n")
|
||||
defer delete(lines)
|
||||
|
||||
ignore_ip_reg := true
|
||||
for line in lines {
|
||||
space_count := 0
|
||||
for c,i in line {
|
||||
@ -42,9 +41,6 @@ extract_reference_cpu_state :: proc(filename: string) -> (Cpu, bool) {
|
||||
}
|
||||
} else {
|
||||
reg_name := line[i:i+2]
|
||||
if reg_name == "ip" {
|
||||
ignore_ip_reg = false
|
||||
}
|
||||
reg_value := get_cpu_register_by_name(&cpu, reg_name)
|
||||
hex_string := line[i+6:i+10]
|
||||
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
|
||||
}
|
||||
|
@ -58,9 +58,8 @@ RegisterId :: struct {
|
||||
access: RegisterAccess,
|
||||
}
|
||||
ImmediateSize :: enum {
|
||||
Signed8,
|
||||
Unsigned8,
|
||||
Signed16,
|
||||
_8,
|
||||
_16,
|
||||
}
|
||||
Immediate :: struct {
|
||||
value: i16,
|
||||
@ -68,8 +67,7 @@ Immediate :: struct {
|
||||
}
|
||||
MemoryAddr :: struct {
|
||||
addr_id: u8,
|
||||
displacement_value: i16,
|
||||
displacement_size: ImmediateSize,
|
||||
displacement: i16,
|
||||
}
|
||||
DirectAddress :: distinct i16
|
||||
Jump :: distinct i8
|
||||
@ -96,7 +94,6 @@ OperandInfo :: enum {
|
||||
SegmentRegister,
|
||||
RegisterMemory,
|
||||
Immediate,
|
||||
ImmediateUnsigned,
|
||||
Accumulator,
|
||||
DirectAddress,
|
||||
Jump,
|
||||
|
Loading…
x
Reference in New Issue
Block a user