add simulating writing to memory
This commit is contained in:
parent
0d7a1beedd
commit
91d8bbcc3d
@ -4,4 +4,8 @@ For [Computer, Enhance!](https://www.computerenhance.com/)
|
||||
|
||||
Examples gotten from: https://github.com/cmuratori/computer_enhance/tree/main/perfaware/part1
|
||||
|
||||
Reference: https://edge.edx.org/c4x/BITSPilani/EEE231/asset/8086_family_Users_Manual_1_.pdf
|
||||
8086 reference manual: https://edge.edx.org/c4x/BITSPilani/EEE231/asset/8086_family_Users_Manual_1_.pdf
|
||||
Important pages in manual:
|
||||
* Registers - 24
|
||||
* Instruction structures - 163
|
||||
* Memory addressing - 83
|
30
examples/51_memory_mov.asm
Normal file
30
examples/51_memory_mov.asm
Normal file
@ -0,0 +1,30 @@
|
||||
; ========================================================================
|
||||
;
|
||||
; (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 51
|
||||
; ========================================================================
|
||||
|
||||
bits 16
|
||||
|
||||
mov word [1000], 1
|
||||
mov word [1002], 2
|
||||
mov word [1004], 3
|
||||
mov word [1006], 4
|
||||
|
||||
mov bx, 1000
|
||||
mov word [bx + 4], 10
|
||||
|
||||
mov bx, word [1000]
|
||||
mov cx, word [1002]
|
||||
mov dx, word [1004]
|
||||
mov bp, word [1006]
|
18
examples/51_memory_mov.txt
Normal file
18
examples/51_memory_mov.txt
Normal file
@ -0,0 +1,18 @@
|
||||
--- test\listing_0051_memory_mov execution ---
|
||||
mov word [+1000], 1 ; ip:0x0->0x6
|
||||
mov word [+1002], 2 ; ip:0x6->0xc
|
||||
mov word [+1004], 3 ; ip:0xc->0x12
|
||||
mov word [+1006], 4 ; ip:0x12->0x18
|
||||
mov bx, 1000 ; bx:0x0->0x3e8 ip:0x18->0x1b
|
||||
mov word [bx+4], 10 ; ip:0x1b->0x20
|
||||
mov bx, [+1000] ; bx:0x3e8->0x1 ip:0x20->0x24
|
||||
mov cx, [+1002] ; cx:0x0->0x2 ip:0x24->0x28
|
||||
mov dx, [+1004] ; dx:0x0->0xa ip:0x28->0x2c
|
||||
mov bp, [+1006] ; bp:0x0->0x4 ip:0x2c->0x30
|
||||
|
||||
Final registers:
|
||||
bx: 0x0001 (1)
|
||||
cx: 0x0002 (2)
|
||||
dx: 0x000a (10)
|
||||
bp: 0x0004 (4)
|
||||
ip: 0x0030 (48)
|
36
examples/52_memory_add_loop.asm
Normal file
36
examples/52_memory_add_loop.asm
Normal file
@ -0,0 +1,36 @@
|
||||
; ========================================================================
|
||||
;
|
||||
; (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 52
|
||||
; ========================================================================
|
||||
|
||||
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, 0
|
||||
add_loop_start:
|
||||
mov cx, word [bp + si]
|
||||
add bx, cx
|
||||
add si, 2
|
||||
cmp si, dx
|
||||
jnz add_loop_start
|
42
examples/52_memory_add_loop.txt
Normal file
42
examples/52_memory_add_loop.txt
Normal file
@ -0,0 +1,42 @@
|
||||
--- test\listing_0052_memory_add_loop 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, 0 ; si:0x6->0x0 ip:0x15->0x18
|
||||
mov cx, [bp+si] ; ip:0x18->0x1a
|
||||
add bx, cx ; ip:0x1a->0x1c
|
||||
add si, 2 ; si:0x0->0x2 ip:0x1c->0x1f flags:PZ->
|
||||
cmp si, dx ; ip:0x1f->0x21 flags:->CPAS
|
||||
jne $-9 ; ip:0x21->0x18
|
||||
mov cx, [bp+si] ; cx:0x0->0x2 ip:0x18->0x1a
|
||||
add bx, cx ; bx:0x0->0x2 ip:0x1a->0x1c flags:CPAS->
|
||||
add si, 2 ; si:0x2->0x4 ip:0x1c->0x1f
|
||||
cmp si, dx ; ip:0x1f->0x21 flags:->CAS
|
||||
jne $-9 ; ip:0x21->0x18
|
||||
mov cx, [bp+si] ; cx:0x2->0x4 ip:0x18->0x1a
|
||||
add bx, cx ; bx:0x2->0x6 ip:0x1a->0x1c flags:CAS->P
|
||||
add si, 2 ; si:0x4->0x6 ip:0x1c->0x1f
|
||||
cmp si, dx ; ip:0x1f->0x21 flags:P->PZ
|
||||
jne $-9 ; ip:0x21->0x23
|
||||
|
||||
Final registers:
|
||||
bx: 0x0006 (6)
|
||||
cx: 0x0004 (4)
|
||||
dx: 0x0006 (6)
|
||||
bp: 0x03e8 (1000)
|
||||
si: 0x0006 (6)
|
||||
ip: 0x0023 (35)
|
||||
flags: PZ
|
@ -130,13 +130,14 @@ int simulate(FILE *src) {
|
||||
fprintf(stderr, "ERROR: Failed to decode instruction at 0x%08x: %s\n", state.ip, decode_error_to_str(err));
|
||||
return -1;
|
||||
}
|
||||
execute_instruction(&state, &inst);
|
||||
execute_instruction(&mem, &state, &inst);
|
||||
}
|
||||
|
||||
printf("Final registers:\n");
|
||||
printf(" ax: 0x%04x (%d)\n", state.ax, state.ax);
|
||||
printf(" bx: 0x%04x (%d)\n", state.bx, state.bx);
|
||||
printf(" cx: 0x%04x (%d)\n", state.cx, state.cx);
|
||||
printf(" dx: 0x%04x (%d)\n", state.dx, state.dx);
|
||||
printf(" sp: 0x%04x (%d)\n", state.sp, state.sp);
|
||||
printf(" bp: 0x%04x (%d)\n", state.bp, state.bp);
|
||||
printf(" si: 0x%04x (%d)\n", state.si, state.si);
|
||||
|
@ -42,8 +42,7 @@ static void reg_or_mem_to_str(char *buff, size_t max_size, struct reg_or_mem_val
|
||||
}
|
||||
|
||||
static void src_to_str(char *buff, size_t max_size, struct src_value *value) {
|
||||
switch (value->variant)
|
||||
{
|
||||
switch (value->variant) {
|
||||
case SRC_VALUE_REG:
|
||||
strncpy(buff, reg_to_str(value->reg), max_size);
|
||||
break;
|
||||
@ -71,8 +70,7 @@ static const char *operation_to_str(enum operation op) {
|
||||
}
|
||||
|
||||
static void instruction_to_str(char *buff, size_t max_size, struct instruction *inst) {
|
||||
switch (inst->op)
|
||||
{
|
||||
switch (inst->op) {
|
||||
case OP_MOV:
|
||||
case OP_CMP:
|
||||
case OP_SUB:
|
||||
|
@ -95,6 +95,7 @@ struct src_value {
|
||||
};
|
||||
};
|
||||
|
||||
// TODO: Store "wide" flag on instruction, it is useful to know when doing most operations
|
||||
struct instruction {
|
||||
enum operation op;
|
||||
struct reg_or_mem_value dest;
|
||||
|
@ -43,8 +43,7 @@ static i16 extend_sign_bit(i8 number) {
|
||||
}
|
||||
|
||||
const char *decode_error_to_str(enum decode_error err) {
|
||||
switch (err)
|
||||
{
|
||||
switch (err) {
|
||||
case DECODE_OK:
|
||||
return "ok";
|
||||
case DECODE_ERR_EOF:
|
||||
@ -83,19 +82,19 @@ static void decode_reg_or_mem(
|
||||
value->is_reg = true;
|
||||
value->reg = decode_reg(rm, wide);
|
||||
} else if (mod == 0b10) { // Mod = 0b10, memory with i16 displacement
|
||||
i16 displacement = pull_byte_at(mem, addr) | (pull_byte_at(mem, addr) << 8);
|
||||
i16 displacement = pull_u16_at(mem, addr);
|
||||
value->is_reg = false;
|
||||
value->mem.base = decode_mem_base(rm);
|
||||
value->mem.disp = displacement;
|
||||
} else if (mod == 0b01) { // Mod = 0b01, memory with i8 displacement
|
||||
i8 displacement = pull_byte_at(mem, addr);
|
||||
i8 displacement = pull_u8_at(mem, addr);
|
||||
value->is_reg = false;
|
||||
value->mem.base = decode_mem_base(rm);
|
||||
value->mem.disp = extend_sign_bit(displacement);
|
||||
} else if (mod == 0b00) { // Mod = 0b00, memory no displacement (most of the time)
|
||||
value->is_reg = false;
|
||||
if (rm == 0b110) { // Direct address
|
||||
u16 address = pull_byte_at(mem, addr) | (pull_byte_at(mem, addr) << 8);
|
||||
u16 address = pull_u16_at(mem, addr);
|
||||
value->mem.base = MEM_BASE_DIRECT_ADDRESS;
|
||||
value->mem.disp = address;
|
||||
} else {
|
||||
@ -130,11 +129,11 @@ static void deocde_reg_or_mem_to_src(
|
||||
// TODO: add handling for 'DECODE_ERR_MISSING_BYTES'
|
||||
// Handy reference: Table 4-12. 8086 Instruction Encoding
|
||||
enum decode_error decode_instruction(struct memory *mem, u16 *addr, struct instruction *output) {
|
||||
u8 byte1 = pull_byte_at(mem, addr);
|
||||
u8 byte1 = pull_u8_at(mem, addr);
|
||||
|
||||
// MOVE: Register memory to/from register
|
||||
if ((byte1 & 0b11111100) == 0b10001000) {
|
||||
u8 byte2 = pull_byte_at(mem, addr);
|
||||
u8 byte2 = pull_u8_at(mem, addr);
|
||||
bool wide = byte1 & 0b1;
|
||||
bool direction = (byte1 & 0b10) >> 1;
|
||||
|
||||
@ -164,16 +163,16 @@ enum decode_error decode_instruction(struct memory *mem, u16 *addr, struct instr
|
||||
|
||||
if (wide) {
|
||||
output->src.variant = SRC_VALUE_IMMEDIATE16;
|
||||
output->src.immediate = pull_byte_at(mem, addr) | (pull_byte_at(mem, addr) << 8);
|
||||
output->src.immediate = pull_u16_at(mem, addr);
|
||||
} else {
|
||||
output->src.variant = SRC_VALUE_IMMEDIATE8;
|
||||
output->src.immediate = pull_byte_at(mem, addr);
|
||||
output->src.immediate = pull_u8_at(mem, addr);
|
||||
}
|
||||
|
||||
|
||||
// MOVE: Immediate to register/memory
|
||||
} else if ((byte1 & 0b11111110) == 0b11000110) {
|
||||
u8 byte2 = pull_byte_at(mem, addr);
|
||||
u8 byte2 = pull_u8_at(mem, addr);
|
||||
|
||||
bool wide = byte1 & 0b1;
|
||||
u8 mod = (byte2 & 0b11000000) >> 6;
|
||||
@ -184,10 +183,10 @@ enum decode_error decode_instruction(struct memory *mem, u16 *addr, struct instr
|
||||
|
||||
if (wide) {
|
||||
output->src.variant = SRC_VALUE_IMMEDIATE16;
|
||||
output->src.immediate = pull_byte_at(mem, addr) | (pull_byte_at(mem, addr) << 8);
|
||||
output->src.immediate = pull_u16_at(mem, addr);
|
||||
} else {
|
||||
output->src.variant = SRC_VALUE_IMMEDIATE8;
|
||||
output->src.immediate = pull_byte_at(mem, addr);
|
||||
output->src.immediate = pull_u8_at(mem, addr);
|
||||
}
|
||||
|
||||
// MOVE: Memory to accumulator
|
||||
@ -200,9 +199,9 @@ enum decode_error decode_instruction(struct memory *mem, u16 *addr, struct instr
|
||||
|
||||
bool wide = byte1 & 0b1;
|
||||
if (wide) {
|
||||
output->src.mem.disp = pull_byte_at(mem, addr) | (pull_byte_at(mem, addr) << 8);
|
||||
output->src.mem.disp = pull_u16_at(mem, addr);
|
||||
} else {
|
||||
output->src.mem.disp = pull_byte_at(mem, addr);
|
||||
output->src.mem.disp = pull_u8_at(mem, addr);
|
||||
}
|
||||
|
||||
// MOVE: Accumulator to memory
|
||||
@ -216,9 +215,9 @@ enum decode_error decode_instruction(struct memory *mem, u16 *addr, struct instr
|
||||
output->dest.mem.base = MEM_BASE_DIRECT_ADDRESS;
|
||||
|
||||
if (wide) {
|
||||
output->dest.mem.disp = pull_byte_at(mem, addr) | (pull_byte_at(mem, addr) << 8);
|
||||
output->dest.mem.disp = pull_u16_at(mem, addr);
|
||||
} else {
|
||||
output->dest.mem.disp = pull_byte_at(mem, addr);
|
||||
output->dest.mem.disp = pull_u8_at(mem, addr);
|
||||
}
|
||||
|
||||
// ADD/SUB/CMP: Reg/memory with register to either
|
||||
@ -235,7 +234,7 @@ enum decode_error decode_instruction(struct memory *mem, u16 *addr, struct instr
|
||||
bool wide = byte1 & 0b01;
|
||||
bool direction = (byte1 & 0b10) >> 1;
|
||||
|
||||
u8 byte2 = pull_byte_at(mem, addr);
|
||||
u8 byte2 = pull_u8_at(mem, addr);
|
||||
u8 mod = (byte2 & 0b11000000) >> 6;
|
||||
u8 reg = (byte2 & 0b00111000) >> 3;
|
||||
u8 rm = byte2 & 0b00000111;
|
||||
@ -252,7 +251,7 @@ enum decode_error decode_instruction(struct memory *mem, u16 *addr, struct instr
|
||||
|
||||
// ADD/SUB/CMP: immediate with register/memory
|
||||
} else if ((byte1 & 0b11111100) == 0b10000000) {
|
||||
u8 byte2 = pull_byte_at(mem, addr);
|
||||
u8 byte2 = pull_u8_at(mem, addr);
|
||||
u8 variant = (byte2 & 0b00111000) >> 3;
|
||||
|
||||
if (variant == 0b000) {
|
||||
@ -273,14 +272,14 @@ enum decode_error decode_instruction(struct memory *mem, u16 *addr, struct instr
|
||||
if (wide) {
|
||||
output->src.variant = SRC_VALUE_IMMEDIATE16;
|
||||
if (sign_extend) {
|
||||
output->src.immediate = pull_byte_at(mem, addr);
|
||||
output->src.immediate = pull_u8_at(mem, addr);
|
||||
output->src.immediate = extend_sign_bit(output->src.immediate);
|
||||
} else {
|
||||
output->src.immediate = pull_byte_at(mem, addr) | (pull_byte_at(mem, addr) << 8);
|
||||
output->src.immediate = pull_u16_at(mem, addr);
|
||||
}
|
||||
} else {
|
||||
output->src.variant = SRC_VALUE_IMMEDIATE8;
|
||||
output->src.immediate = pull_byte_at(mem, addr);
|
||||
output->src.immediate = pull_u8_at(mem, addr);
|
||||
}
|
||||
|
||||
// ADD/SUB/CMP: immediate with accumulator
|
||||
@ -301,22 +300,22 @@ enum decode_error decode_instruction(struct memory *mem, u16 *addr, struct instr
|
||||
|
||||
if (wide) {
|
||||
output->src.variant = SRC_VALUE_IMMEDIATE16;
|
||||
output->src.immediate = pull_byte_at(mem, addr) | (pull_byte_at(mem, addr) << 8);
|
||||
output->src.immediate = pull_u16_at(mem, addr);
|
||||
} else {
|
||||
output->src.variant = SRC_VALUE_IMMEDIATE8;
|
||||
output->src.immediate = pull_byte_at(mem, addr);
|
||||
output->src.immediate = pull_u8_at(mem, addr);
|
||||
}
|
||||
|
||||
// Conditional jumps
|
||||
} else if ((byte1 & 0b11110000) == 0b01110000) {
|
||||
i8 jmp_offset = pull_byte_at(mem, addr);
|
||||
i8 jmp_offset = pull_u8_at(mem, addr);
|
||||
u8 opcode = byte1 & 0b00001111;
|
||||
output->op = cond_jmp_lookup[opcode];
|
||||
output->jmp_offset = jmp_offset;
|
||||
|
||||
// Conditional loop jumps
|
||||
} else if ((byte1 & 0b11111100) == 0b11100000) {
|
||||
i8 jmp_offset = pull_byte_at(mem, addr);
|
||||
i8 jmp_offset = pull_u8_at(mem, addr);
|
||||
u8 opcode = byte1 & 0b00000011;
|
||||
output->op = cond_loop_jmp_lookup[opcode];
|
||||
output->jmp_offset = jmp_offset;
|
||||
|
@ -25,12 +25,31 @@ int load_mem_from_file(struct memory *mem, const char *filename, u32 start) {
|
||||
}
|
||||
|
||||
// TODO: Make this error some kind of error, when reading past end
|
||||
u8 read_byte_at(struct memory *mem, u16 address) {
|
||||
u8 read_u8_at(struct memory *mem, u16 address) {
|
||||
return mem->mem[address % MEMORY_SIZE];
|
||||
}
|
||||
|
||||
u8 pull_byte_at(struct memory *mem, u16 *address) {
|
||||
u8 byte = read_byte_at(mem, *address);
|
||||
u16 read_u16_at(struct memory *mem, u16 address) {
|
||||
return read_u8_at(mem, address) | (read_u8_at(mem, address+1) << 8);
|
||||
}
|
||||
|
||||
void write_u8_at(struct memory *mem, u16 address, u8 value) {
|
||||
mem->mem[address % MEMORY_SIZE] = value;
|
||||
}
|
||||
|
||||
void write_u16_at(struct memory *mem, u16 address, u16 value) {
|
||||
write_u8_at(mem, address+0, (value >> 0) & 0xFF);
|
||||
write_u8_at(mem, address+1, (value >> 8) & 0xFF);
|
||||
}
|
||||
|
||||
u16 pull_u16_at(struct memory *mem, u16 *address) {
|
||||
u16 value = read_u16_at(mem, *address);
|
||||
(*address) += 2;
|
||||
return value;
|
||||
}
|
||||
|
||||
u8 pull_u8_at(struct memory *mem, u16 *address) {
|
||||
u8 byte = read_u8_at(mem, *address);
|
||||
(*address)++;
|
||||
return byte;
|
||||
}
|
@ -1,118 +1,153 @@
|
||||
|
||||
u16 read_reg_value(struct cpu_state *state, enum reg_value reg)
|
||||
u16 read_reg_value(struct cpu_state *cpu, enum reg_value reg)
|
||||
{
|
||||
switch (reg)
|
||||
{
|
||||
case REG_AL: return state->ax & 0xFF;
|
||||
case REG_CL: return state->cx & 0xFF;
|
||||
case REG_DL: return state->dx & 0xFF;
|
||||
case REG_BL: return state->bx & 0xFF;
|
||||
case REG_AH: return (state->ax >> 8) & 0xFF;
|
||||
case REG_CH: return (state->cx >> 8) & 0xFF;
|
||||
case REG_DH: return (state->dx >> 8) & 0xFF;
|
||||
case REG_BH: return (state->bx >> 8) & 0xFF;
|
||||
case REG_AX: return state->ax;
|
||||
case REG_CX: return state->cx;
|
||||
case REG_DX: return state->dx;
|
||||
case REG_BX: return state->bx;
|
||||
case REG_SP: return state->sp;
|
||||
case REG_BP: return state->bp;
|
||||
case REG_SI: return state->si;
|
||||
case REG_DI: return state->di;
|
||||
switch (reg) {
|
||||
case REG_AL: return cpu->ax & 0xFF;
|
||||
case REG_CL: return cpu->cx & 0xFF;
|
||||
case REG_DL: return cpu->dx & 0xFF;
|
||||
case REG_BL: return cpu->bx & 0xFF;
|
||||
case REG_AH: return (cpu->ax >> 8) & 0xFF;
|
||||
case REG_CH: return (cpu->cx >> 8) & 0xFF;
|
||||
case REG_DH: return (cpu->dx >> 8) & 0xFF;
|
||||
case REG_BH: return (cpu->bx >> 8) & 0xFF;
|
||||
case REG_AX: return cpu->ax;
|
||||
case REG_CX: return cpu->cx;
|
||||
case REG_DX: return cpu->dx;
|
||||
case REG_BX: return cpu->bx;
|
||||
case REG_SP: return cpu->sp;
|
||||
case REG_BP: return cpu->bp;
|
||||
case REG_SI: return cpu->si;
|
||||
case REG_DI: return cpu->di;
|
||||
default: panic("Unhandled register '%s'", reg_to_str(reg));
|
||||
}
|
||||
}
|
||||
|
||||
void write_reg_value(struct cpu_state *state, enum reg_value reg, u16 value)
|
||||
void write_reg_value(struct cpu_state *cpu, enum reg_value reg, u16 value)
|
||||
{
|
||||
switch (reg)
|
||||
{
|
||||
switch (reg) {
|
||||
case REG_AL:
|
||||
state->ax = (state->ax & 0xFF00) & value;
|
||||
cpu->ax = (cpu->ax & 0xFF00) & value;
|
||||
break;
|
||||
case REG_CL:
|
||||
state->cx = (state->cx & 0xFF00) & value;
|
||||
cpu->cx = (cpu->cx & 0xFF00) & value;
|
||||
break;
|
||||
case REG_DL:
|
||||
state->dx = (state->dx & 0xFF00) & value;
|
||||
cpu->dx = (cpu->dx & 0xFF00) & value;
|
||||
break;
|
||||
case REG_BL:
|
||||
state->bx = (state->bx & 0xFF00) & value;
|
||||
cpu->bx = (cpu->bx & 0xFF00) & value;
|
||||
break;
|
||||
case REG_AH:
|
||||
state->ax = (state->ax & 0x00FF) & (value << 8);
|
||||
cpu->ax = (cpu->ax & 0x00FF) & (value << 8);
|
||||
break;
|
||||
case REG_CH:
|
||||
state->cx = (state->cx & 0x00FF) & (value << 8);
|
||||
cpu->cx = (cpu->cx & 0x00FF) & (value << 8);
|
||||
break;
|
||||
case REG_DH:
|
||||
state->dx = (state->dx & 0x00FF) & (value << 8);
|
||||
cpu->dx = (cpu->dx & 0x00FF) & (value << 8);
|
||||
break;
|
||||
case REG_BH:
|
||||
state->bx = (state->bx & 0x00FF) & (value << 8);
|
||||
cpu->bx = (cpu->bx & 0x00FF) & (value << 8);
|
||||
break;
|
||||
case REG_AX:
|
||||
state->ax = value;
|
||||
cpu->ax = value;
|
||||
break;
|
||||
case REG_CX:
|
||||
state->cx = value;
|
||||
cpu->cx = value;
|
||||
break;
|
||||
case REG_DX:
|
||||
state->dx = value;
|
||||
cpu->dx = value;
|
||||
break;
|
||||
case REG_BX:
|
||||
state->bx = value;
|
||||
cpu->bx = value;
|
||||
break;
|
||||
case REG_SP:
|
||||
state->sp = value;
|
||||
cpu->sp = value;
|
||||
break;
|
||||
case REG_BP:
|
||||
state->bp = value;
|
||||
cpu->bp = value;
|
||||
break;
|
||||
case REG_SI:
|
||||
state->si = value;
|
||||
cpu->si = value;
|
||||
break;
|
||||
case REG_DI:
|
||||
state->di = value;
|
||||
cpu->di = value;
|
||||
break;
|
||||
default:
|
||||
panic("Unhandled register '%s'", reg_to_str(reg));
|
||||
}
|
||||
}
|
||||
|
||||
u16 read_src_value(struct cpu_state *state, struct src_value *src) {
|
||||
switch (src->variant)
|
||||
{
|
||||
u16 read_mem_base_value(struct cpu_state *cpu, enum mem_base base) {
|
||||
switch (base) {
|
||||
case MEM_BASE_BX_SI: return read_reg_value(cpu, REG_BX) + read_reg_value(cpu, REG_SI);
|
||||
case MEM_BASE_BX_DI: return read_reg_value(cpu, REG_BX) + read_reg_value(cpu, REG_DI);
|
||||
case MEM_BASE_BP_SI: return read_reg_value(cpu, REG_BP) + read_reg_value(cpu, REG_SI);
|
||||
case MEM_BASE_BP_DI: return read_reg_value(cpu, REG_BP) + read_reg_value(cpu, REG_DI);
|
||||
case MEM_BASE_SI: return read_reg_value(cpu, REG_SI);
|
||||
case MEM_BASE_DI: return read_reg_value(cpu, REG_DI);
|
||||
case MEM_BASE_BP: return read_reg_value(cpu, REG_BP);
|
||||
case MEM_BASE_BX: return read_reg_value(cpu, REG_BX);
|
||||
default: return 0;
|
||||
}
|
||||
}
|
||||
|
||||
u16 calculate_mem_address(struct cpu_state *cpu, struct mem_value *addr) {
|
||||
if (addr->base == MEM_BASE_DIRECT_ADDRESS) {
|
||||
return addr->direct_address;
|
||||
} else {
|
||||
return read_mem_base_value(cpu, addr->base) + addr->disp;
|
||||
}
|
||||
}
|
||||
|
||||
u16 read_mem_value(struct memory *mem, struct cpu_state *cpu, struct mem_value *value, bool wide) {
|
||||
u16 addr = calculate_mem_address(cpu, value);
|
||||
|
||||
return wide ? read_u16_at(mem, addr) : read_u8_at(mem, addr);
|
||||
}
|
||||
|
||||
void write_mem_value(struct memory *mem, struct cpu_state *cpu, struct mem_value *location, u16 value, bool wide) {
|
||||
u16 addr = calculate_mem_address(cpu, location);
|
||||
|
||||
if (wide) {
|
||||
write_u16_at(mem, addr, value);
|
||||
} else {
|
||||
write_u8_at(mem, addr, value);
|
||||
}
|
||||
}
|
||||
|
||||
u16 read_src_value(struct memory *mem, struct cpu_state *cpu, struct src_value *src, bool wide) {
|
||||
switch (src->variant) {
|
||||
case SRC_VALUE_REG:
|
||||
return read_reg_value(state, src->reg);
|
||||
return read_reg_value(cpu, src->reg);
|
||||
case SRC_VALUE_IMMEDIATE8:
|
||||
case SRC_VALUE_IMMEDIATE16:
|
||||
return src->immediate;
|
||||
case SRC_VALUE_MEM:
|
||||
todo("Handle read from memory");
|
||||
return read_mem_value(mem, cpu, &src->mem, wide);
|
||||
default:
|
||||
panic("Unhandled src variant %d\n", src->variant);
|
||||
}
|
||||
}
|
||||
|
||||
u16 read_src_or_mem_value(struct cpu_state *state, struct reg_or_mem_value *reg_or_mem) {
|
||||
u16 read_reg_or_mem_value(struct memory *mem, struct cpu_state *cpu, struct reg_or_mem_value *reg_or_mem, bool wide) {
|
||||
if (reg_or_mem->is_reg) {
|
||||
return read_reg_value(state, reg_or_mem->reg);
|
||||
return read_reg_value(cpu, reg_or_mem->reg);
|
||||
} else {
|
||||
todo("Handle read from memory");
|
||||
return read_mem_value(mem, cpu, ®_or_mem->mem, wide);
|
||||
}
|
||||
}
|
||||
void write_src_or_mem_value(struct cpu_state *state, struct reg_or_mem_value *reg_or_mem, u16 value) {
|
||||
|
||||
void write_reg_or_mem_value(struct memory *mem, struct cpu_state *cpu, struct reg_or_mem_value *reg_or_mem, u16 value, bool wide) {
|
||||
if (reg_or_mem->is_reg) {
|
||||
write_reg_value(state, reg_or_mem->reg, value);
|
||||
write_reg_value(cpu, reg_or_mem->reg, value);
|
||||
} else {
|
||||
todo("Handle write to memory");
|
||||
write_mem_value(mem, cpu, ®_or_mem->mem, value, wide);
|
||||
}
|
||||
}
|
||||
|
||||
bool is_reg_16bit(enum reg_value reg) {
|
||||
switch (reg)
|
||||
{
|
||||
switch (reg) {
|
||||
case REG_AL:
|
||||
case REG_CL:
|
||||
case REG_DL:
|
||||
@ -140,66 +175,68 @@ bool are_instruction_operands_16bit(struct instruction *inst) {
|
||||
return is_reg_16bit(inst->dest.reg);
|
||||
} else if (inst->src.variant == SRC_VALUE_REG) {
|
||||
return is_reg_16bit(inst->src.reg);
|
||||
} else if (inst->src.variant == SRC_VALUE_IMMEDIATE8) {
|
||||
return false;
|
||||
} else if (inst->src.variant == SRC_VALUE_IMMEDIATE16) {
|
||||
return true;
|
||||
} else {
|
||||
return inst->src.variant == SRC_VALUE_IMMEDIATE16;
|
||||
panic("Failed to determine instruction width\n");
|
||||
}
|
||||
}
|
||||
|
||||
void execute_instruction(struct cpu_state *state, struct instruction *inst) {
|
||||
switch (inst->op)
|
||||
{
|
||||
void update_sign_flag(struct cpu_state *cpu, struct instruction *inst, u16 result) {
|
||||
if (are_instruction_operands_16bit(inst)) {
|
||||
cpu->flags.sign = (result >> 15) & 0b1;
|
||||
} else {
|
||||
cpu->flags.sign = (result >> 7) & 0b1;
|
||||
}
|
||||
}
|
||||
|
||||
void execute_instruction(struct memory *mem, struct cpu_state *cpu, struct instruction *inst) {
|
||||
switch (inst->op) {
|
||||
case OP_MOV: {
|
||||
u16 src_value = read_src_value(state, &inst->src);
|
||||
write_src_or_mem_value(state, &inst->dest, src_value);
|
||||
bool wide = are_instruction_operands_16bit(inst);
|
||||
u16 src_value = read_src_value(mem, cpu, &inst->src, wide);
|
||||
write_reg_or_mem_value(mem, cpu, &inst->dest, src_value, wide);
|
||||
break;
|
||||
}
|
||||
case OP_ADD: {
|
||||
u16 dest_value = read_src_or_mem_value(state, &inst->dest);
|
||||
u16 src_value = read_src_value(state, &inst->src);
|
||||
bool wide = are_instruction_operands_16bit(inst);
|
||||
u16 dest_value = read_reg_or_mem_value(mem, cpu, &inst->dest, wide);
|
||||
u16 src_value = read_src_value(mem, cpu, &inst->src, wide);
|
||||
u16 result = dest_value + src_value;
|
||||
|
||||
state->flags.zero = result == 0;
|
||||
if (are_instruction_operands_16bit(inst)) {
|
||||
state->flags.sign = (result >> 15) & 0b1;
|
||||
} else {
|
||||
state->flags.sign = (result >> 7) & 0b1;
|
||||
}
|
||||
cpu->flags.zero = result == 0;
|
||||
update_sign_flag(cpu, inst, result);
|
||||
|
||||
write_src_or_mem_value(state, &inst->dest, result);
|
||||
write_reg_or_mem_value(mem, cpu, &inst->dest, result, wide);
|
||||
break;
|
||||
}
|
||||
case OP_SUB: {
|
||||
u16 dest_value = read_src_or_mem_value(state, &inst->dest);
|
||||
u16 src_value = read_src_value(state, &inst->src);
|
||||
bool wide = are_instruction_operands_16bit(inst);
|
||||
u16 dest_value = read_reg_or_mem_value(mem, cpu, &inst->dest, wide);
|
||||
u16 src_value = read_src_value(mem, cpu, &inst->src, wide);
|
||||
u16 result = dest_value - src_value;
|
||||
|
||||
state->flags.zero = result == 0;
|
||||
if (are_instruction_operands_16bit(inst)) {
|
||||
state->flags.sign = (result >> 15) & 0b1;
|
||||
} else {
|
||||
state->flags.sign = (result >> 7) & 0b1;
|
||||
}
|
||||
cpu->flags.zero = result == 0;
|
||||
update_sign_flag(cpu, inst, result);
|
||||
|
||||
write_src_or_mem_value(state, &inst->dest, result);
|
||||
write_reg_or_mem_value(mem, cpu, &inst->dest, result, wide);
|
||||
break;
|
||||
}
|
||||
case OP_CMP: {
|
||||
u16 dest_value = read_src_or_mem_value(state, &inst->dest);
|
||||
u16 src_value = read_src_value(state, &inst->src);
|
||||
bool wide = are_instruction_operands_16bit(inst);
|
||||
u16 dest_value = read_reg_or_mem_value(mem, cpu, &inst->dest, wide);
|
||||
u16 src_value = read_src_value(mem, cpu, &inst->src, wide);
|
||||
u16 result = dest_value - src_value;
|
||||
|
||||
state->flags.zero = result == 0;
|
||||
if (are_instruction_operands_16bit(inst)) {
|
||||
state->flags.sign = (result >> 15) & 0b1;
|
||||
} else {
|
||||
state->flags.sign = (result >> 7) & 0b1;
|
||||
}
|
||||
cpu->flags.zero = result == 0;
|
||||
update_sign_flag(cpu, inst, result);
|
||||
break;
|
||||
}
|
||||
case OP_JNE: {
|
||||
if (!state->flags.zero) {
|
||||
i8 jmp_offset = inst->jmp_offset;
|
||||
state->ip += jmp_offset;
|
||||
if (!cpu->flags.zero) {
|
||||
cpu->ip += inst->jmp_offset;
|
||||
}
|
||||
|
||||
break;
|
||||
|
Loading…
Reference in New Issue
Block a user