1
0

add simulating writing to memory

This commit is contained in:
Rokas Puzonas 2023-05-07 15:45:09 +03:00
parent 0d7a1beedd
commit 91d8bbcc3d
11 changed files with 305 additions and 120 deletions

View File

@ -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

View 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]

View 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)

View 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

View 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

View File

@ -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);

View File

@ -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:

View File

@ -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;

View File

@ -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;

View File

@ -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;
}

View File

@ -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, &reg_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, &reg_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;