perfaware/part1: Correctly load and execute program from 8086 1mb memory

This commit is contained in:
doyle 2023-04-18 21:41:19 +10:00
parent 35fe0b899a
commit dd83866f2b
6 changed files with 29047 additions and 114 deletions

View File

@ -235,3 +235,18 @@ nasm %build_dir_listing_0053%_disassembled.asm
fc /B %build_dir_listing_0053% %build_dir_listing_0053%_disassembled || exit /b 1 fc /B %build_dir_listing_0053% %build_dir_listing_0053%_disassembled || exit /b 1
fc /N %build_dir_listing_0053%.txt %build_dir_listing_0053%_disassembled.txt || exit /b 1 fc /N %build_dir_listing_0053%.txt %build_dir_listing_0053%_disassembled.txt || exit /b 1
REM ================================================================================================
set listing_0054=listing_0054_draw_rectangle
set build_dir_listing_0054=%build_dir%\%listing_0054%
copy /Y %script_dir%\%listing_0054% %build_dir% 1>NUL
copy /Y %script_dir%\%listing_0054%.txt %build_dir% 1>NUL
%build_dir%\sim8086.exe --exec --log-instruction-ptr %build_dir_listing_0054% > %build_dir_listing_0054%_disassembled.txt
%build_dir%\sim8086.exe %build_dir_listing_0054% > %build_dir_listing_0054%_disassembled.asm
nasm %build_dir_listing_0054%_disassembled.asm
fc /B %build_dir_listing_0054% %build_dir_listing_0054%_disassembled || exit /b 1
fc /N %build_dir_listing_0054%.txt %build_dir_listing_0054%_disassembled.txt || exit /b 1

Binary file not shown.

View File

@ -0,0 +1,43 @@
; ========================================================================
;
; (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 54
; ========================================================================
bits 16
; Start image after one row, to avoid overwriting our code!
mov bp, 64*4
mov dx, 0
y_loop_start:
mov cx, 0
x_loop_start:
; Fill pixel
mov word [bp + 0], cx ; Red
mov word [bp + 2], dx ; Blue
mov byte [bp + 3], 255 ; Alpha
; Advance pixel location
add bp, 4
; Advance X coordinate and loop
add cx, 1
cmp cx, 64
jnz x_loop_start
; Advance Y coordinate and loop
add dx, 1
cmp dx, 64
jnz y_loop_start

File diff suppressed because it is too large Load Diff

View File

@ -349,7 +349,6 @@ void S86_DecodeEffectiveAddr(S86_Opcode *opcode, S86_BufferIterator *buffer_it,
S86_Opcode S86_DecodeOpcode(S86_BufferIterator *buffer_it, S86_Opcode S86_DecodeOpcode(S86_BufferIterator *buffer_it,
S86_OpDecode const *decode_table, S86_OpDecode const *decode_table,
uint16_t decode_table_size, uint16_t decode_table_size,
uint16_t opcode_index,
bool *lock_prefix, bool *lock_prefix,
S86_MnemonicOp *seg_reg) S86_MnemonicOp *seg_reg)
{ {
@ -845,8 +844,6 @@ S86_Opcode S86_DecodeOpcode(S86_BufferIterator *buffer_it,
size_t buffer_end_index = buffer_it->index; size_t buffer_end_index = buffer_it->index;
result.byte_size = S86_CAST(uint8_t)(buffer_end_index - buffer_start_index); result.byte_size = S86_CAST(uint8_t)(buffer_end_index - buffer_start_index);
result.instruction_ptr = S86_CAST(uint16_t)buffer_start_index;
result.index = S86_CAST(uint16_t)opcode_index;
S86_ASSERT(result.immediate < S86_CAST(uint16_t)-1); S86_ASSERT(result.immediate < S86_CAST(uint16_t)-1);
return result; return result;
} }
@ -857,31 +854,6 @@ typedef struct S86_MnemonicOpToRegisterFileMap {
S86_RegisterByte byte; ///< The 'byte' that the mnemonic operates on (hi, lo or nil e.g. word) S86_RegisterByte byte; ///< The 'byte' that the mnemonic operates on (hi, lo or nil e.g. word)
} S86_MnemonicOpToRegisterFileMap; } S86_MnemonicOpToRegisterFileMap;
S86_Opcode *S86_OpcodeAtInstructionPtr(S86_Opcode *opcode_array,
size_t opcode_size,
S86_Opcode *prev_opcode,
uint16_t instruction_ptr)
{
S86_Opcode NIL_OPCODE = {0};
if (!prev_opcode)
prev_opcode = &NIL_OPCODE;
S86_Opcode *result = NULL;
if (instruction_ptr < prev_opcode->instruction_ptr) {
for (size_t index = prev_opcode->index - 1; !result && index < opcode_size; index--) {
if (opcode_array[index].instruction_ptr == instruction_ptr)
result = opcode_array + index;
}
} else {
for (size_t index = prev_opcode->index; !result && index < opcode_size; index++) {
if (opcode_array[index].instruction_ptr == instruction_ptr)
result = opcode_array + index;
}
}
return result;
}
char const CLI_ARG_EXEC[] = "--exec"; char const CLI_ARG_EXEC[] = "--exec";
char const CLI_ARG_LOG_INSTRUCTION_PTR[] = "--log-instruction-ptr"; char const CLI_ARG_LOG_INSTRUCTION_PTR[] = "--log-instruction-ptr";
#define PRINT_USAGE \ #define PRINT_USAGE \
@ -1237,6 +1209,7 @@ int main(int argc, char **argv)
S86_RegisterFile register_file = {0}; S86_RegisterFile register_file = {0};
uint8_t *memory = VirtualAlloc(0, 1024 * 1024, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE); uint8_t *memory = VirtualAlloc(0, 1024 * 1024, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);
memcpy(memory, buffer.data, buffer.size);
S86_MnemonicOpToRegisterFileMap mnemonic_op_to_register_file_map[] = { S86_MnemonicOpToRegisterFileMap mnemonic_op_to_register_file_map[] = {
{.mnemonic_op = S86_MnemonicOp_AX, .reg = &register_file.reg.file.ax, .byte = S86_RegisterByte_Nil}, {.mnemonic_op = S86_MnemonicOp_AX, .reg = &register_file.reg.file.ax, .byte = S86_RegisterByte_Nil},
@ -1271,56 +1244,21 @@ int main(int argc, char **argv)
{.mnemonic_op = S86_MnemonicOp_BP_DI, .reg = &register_file.reg.file.bp, .byte = S86_RegisterByte_Nil}, {.mnemonic_op = S86_MnemonicOp_BP_DI, .reg = &register_file.reg.file.bp, .byte = S86_RegisterByte_Nil},
}; };
// NOTE: Count opcodes, allocate then decode in 1 swoop
// =========================================================================
S86_Opcode *opcode_array = NULL;
uint16_t opcode_size = 0;
{
bool lock_prefix = false;
S86_MnemonicOp seg_reg = S86_CAST(S86_MnemonicOp)0;
for (S86_BufferIterator it = S86_BufferIteratorInit(buffer);
S86_BufferIteratorHasMoreBytes(it);
opcode_size++) {
S86_DecodeOpcode(&it, DECODE_TABLE, S86_ARRAY_UCOUNT(DECODE_TABLE), opcode_size, &lock_prefix, &seg_reg);
}
if (opcode_size == 0)
return 0;
lock_prefix = false;
seg_reg = S86_CAST(S86_MnemonicOp)0;
opcode_array = VirtualAlloc(NULL, sizeof(*opcode_array) * opcode_size, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
if (!opcode_array) {
S86_PrintLnFmt("ERROR: Failed to allocate memory for decoding opcode stream");
return -1;
}
uint16_t opcode_index = 0;
for (S86_BufferIterator it = S86_BufferIteratorInit(buffer); S86_BufferIteratorHasMoreBytes(it); opcode_index++) {
opcode_array[opcode_index] = S86_DecodeOpcode(&it, DECODE_TABLE, S86_ARRAY_UCOUNT(DECODE_TABLE), opcode_index, &lock_prefix, &seg_reg);
}
}
// NOTE: Execute the assembly // NOTE: Execute the assembly
// ========================================================================= // =========================================================================
for (S86_Opcode *prev_opcode = NULL, *opcode = NULL; bool lock_prefix = false;
register_file.instruction_ptr < buffer.size; S86_MnemonicOp seg_reg = S86_CAST(S86_MnemonicOp)0;
prev_opcode = opcode) { for (uint16_t prev_ip = 0; register_file.instruction_ptr < buffer.size; prev_ip = register_file.instruction_ptr) {
opcode = S86_OpcodeAtInstructionPtr(opcode_array, S86_Buffer instruction_buffer = {0};
opcode_size, instruction_buffer.data = (char *)&memory[register_file.instruction_ptr];
prev_opcode, instruction_buffer.size = buffer.size - register_file.instruction_ptr;
register_file.instruction_ptr);
if (!opcode) {
S86_PrintLnFmt("ERROR: Could not find opcode with matching instruction pointer, execution failed! [file=\"%.*s\", prev_ip=0x%x, ip=0x%x]",
S86_STR8_FMT(file_path),
prev_opcode->instruction_ptr,
register_file.instruction_ptr);
return -1;
}
S86_PrintOpcode(*opcode); S86_BufferIterator instruction_it = S86_BufferIteratorInit(instruction_buffer);
register_file.instruction_ptr += opcode->byte_size; S86_Opcode opcode = S86_DecodeOpcode(&instruction_it, DECODE_TABLE, S86_ARRAY_UCOUNT(DECODE_TABLE), &lock_prefix, &seg_reg);
if (opcode->mnemonic == S86_Mnemonic_LOCK || opcode->mnemonic == S86_Mnemonic_SEGMENT) S86_PrintOpcode(opcode);
register_file.instruction_ptr += opcode.byte_size;
if (opcode.mnemonic == S86_Mnemonic_LOCK || opcode.mnemonic == S86_Mnemonic_SEGMENT)
continue; continue;
if (!exec_mode) { if (!exec_mode) {
@ -1330,7 +1268,7 @@ int main(int argc, char **argv)
// NOTE: Simulate instruction ============================================================== // NOTE: Simulate instruction ==============================================================
S86_RegisterFile prev_register_file = register_file; S86_RegisterFile prev_register_file = register_file;
switch (opcode->mnemonic) { switch (opcode.mnemonic) {
case S86_Mnemonic_PUSH: /*FALLTHRU*/ case S86_Mnemonic_PUSH: /*FALLTHRU*/
case S86_Mnemonic_POP: /*FALLTHRU*/ case S86_Mnemonic_POP: /*FALLTHRU*/
case S86_Mnemonic_XCHG: /*FALLTHRU*/ case S86_Mnemonic_XCHG: /*FALLTHRU*/
@ -1410,22 +1348,22 @@ int main(int argc, char **argv)
case S86_Mnemonic_MOV: { case S86_Mnemonic_MOV: {
uint16_t src = 0; uint16_t src = 0;
bool byte_op = opcode->dest >= S86_MnemonicOp_AL && opcode->dest <= S86_MnemonicOp_BH; bool byte_op = opcode.dest >= S86_MnemonicOp_AL && opcode.dest <= S86_MnemonicOp_BH;
if (opcode->src == S86_MnemonicOp_Immediate) { if (opcode.src == S86_MnemonicOp_Immediate) {
if (byte_op) { if (byte_op) {
S86_ASSERT(opcode->immediate < S86_CAST(uint8_t)-1); S86_ASSERT(opcode.immediate < S86_CAST(uint8_t)-1);
src = S86_CAST(uint8_t)opcode->immediate; src = S86_CAST(uint8_t)opcode.immediate;
} else { } else {
src = S86_CAST(uint16_t)opcode->immediate; src = S86_CAST(uint16_t)opcode.immediate;
} }
} else if (opcode->src == S86_MnemonicOp_DirectAddress) { } else if (opcode.src == S86_MnemonicOp_DirectAddress) {
S86_ASSERT(opcode->displacement >= 0); S86_ASSERT(opcode.displacement >= 0);
src = memory[opcode->displacement]; src = memory[opcode.displacement];
} else { } else {
S86_MnemonicOpToRegisterFileMap const *src_map = NULL; S86_MnemonicOpToRegisterFileMap const *src_map = NULL;
for (size_t index = 0; !src_map && index < S86_ARRAY_UCOUNT(mnemonic_op_to_register_file_map); index++) { for (size_t index = 0; !src_map && index < S86_ARRAY_UCOUNT(mnemonic_op_to_register_file_map); index++) {
S86_MnemonicOpToRegisterFileMap const *item = mnemonic_op_to_register_file_map + index; S86_MnemonicOpToRegisterFileMap const *item = mnemonic_op_to_register_file_map + index;
if (item->mnemonic_op == opcode->src) if (item->mnemonic_op == opcode.src)
src_map = item; src_map = item;
} }
@ -1451,27 +1389,27 @@ int main(int argc, char **argv)
uint8_t *dest_lo = NULL; uint8_t *dest_lo = NULL;
uint8_t *dest_hi = NULL; uint8_t *dest_hi = NULL;
if (opcode->dest == S86_MnemonicOp_DirectAddress) { if (opcode.dest == S86_MnemonicOp_DirectAddress) {
// NOTE: The 8086 doesn't support load to store directly // NOTE: The 8086 doesn't support load to store directly
// memory to memory afaict // memory to memory afaict
S86_ASSERT(opcode->dest != opcode->src); S86_ASSERT(opcode.dest != opcode.src);
S86_ASSERT(opcode->displacement >= 0); S86_ASSERT(opcode.displacement >= 0);
dest_lo = memory + opcode->displacement; dest_lo = memory + opcode.displacement;
dest_hi = byte_op ? NULL : memory + (opcode->displacement + 1); dest_hi = byte_op ? NULL : memory + (opcode.displacement + 1);
} else { } else {
S86_MnemonicOpToRegisterFileMap const *dest_map = NULL; S86_MnemonicOpToRegisterFileMap const *dest_map = NULL;
for (size_t index = 0; !dest_map && index < S86_ARRAY_UCOUNT(mnemonic_op_to_register_file_map); index++) { for (size_t index = 0; !dest_map && index < S86_ARRAY_UCOUNT(mnemonic_op_to_register_file_map); index++) {
S86_MnemonicOpToRegisterFileMap const *item = mnemonic_op_to_register_file_map + index; S86_MnemonicOpToRegisterFileMap const *item = mnemonic_op_to_register_file_map + index;
if (item->mnemonic_op == opcode->dest) if (item->mnemonic_op == opcode.dest)
dest_map = item; dest_map = item;
} }
S86_ASSERT(dest_map); S86_ASSERT(dest_map);
// NOTE: Effective address means we're store/load from memory // NOTE: Effective address means we're store/load from memory
// The opcode value is the address. // The opcode value is the address.
if (opcode->effective_addr == S86_EffectiveAddress_Dest && opcode->effective_addr_loads_mem) { if (opcode.effective_addr == S86_EffectiveAddress_Dest && opcode.effective_addr_loads_mem) {
uint16_t address = dest_map->reg->word + S86_CAST(uint16_t)opcode->displacement; uint16_t address = dest_map->reg->word + S86_CAST(uint16_t)opcode.displacement;
if (dest_map->mnemonic_op == S86_MnemonicOp_BX_SI) { if (dest_map->mnemonic_op == S86_MnemonicOp_BX_SI) {
address = dest_map->reg->word + register_file.reg.file.si.word; address = dest_map->reg->word + register_file.reg.file.si.word;
} else if (dest_map->mnemonic_op == S86_MnemonicOp_BX_DI) { } else if (dest_map->mnemonic_op == S86_MnemonicOp_BX_DI) {
@ -1506,32 +1444,32 @@ int main(int argc, char **argv)
S86_MnemonicOpToRegisterFileMap *dest_map = NULL; S86_MnemonicOpToRegisterFileMap *dest_map = NULL;
for (size_t index = 0; !dest_map && index < S86_ARRAY_UCOUNT(mnemonic_op_to_register_file_map); index++) { for (size_t index = 0; !dest_map && index < S86_ARRAY_UCOUNT(mnemonic_op_to_register_file_map); index++) {
S86_MnemonicOpToRegisterFileMap *item = mnemonic_op_to_register_file_map + index; S86_MnemonicOpToRegisterFileMap *item = mnemonic_op_to_register_file_map + index;
if (item->mnemonic_op == opcode->dest) if (item->mnemonic_op == opcode.dest)
dest_map = item; dest_map = item;
} }
S86_ASSERT(dest_map); S86_ASSERT(dest_map);
bool subtract = opcode->mnemonic != S86_Mnemonic_ADD; bool subtract = opcode.mnemonic != S86_Mnemonic_ADD;
S86_Register16 dest = *dest_map->reg; S86_Register16 dest = *dest_map->reg;
bool byte_op = opcode->dest >= S86_MnemonicOp_AL && opcode->dest <= S86_MnemonicOp_BH; bool byte_op = opcode.dest >= S86_MnemonicOp_AL && opcode.dest <= S86_MnemonicOp_BH;
uint16_t src = 0; uint16_t src = 0;
if (opcode->src == S86_MnemonicOp_Immediate) { if (opcode.src == S86_MnemonicOp_Immediate) {
if (byte_op) { if (byte_op) {
S86_ASSERT(opcode->immediate < S86_CAST(uint8_t)-1); S86_ASSERT(opcode.immediate < S86_CAST(uint8_t)-1);
src = S86_CAST(uint8_t)opcode->immediate; src = S86_CAST(uint8_t)opcode.immediate;
} else { } else {
S86_ASSERT(opcode->immediate < S86_CAST(uint16_t)-1); S86_ASSERT(opcode.immediate < S86_CAST(uint16_t)-1);
src = S86_CAST(uint16_t)opcode->immediate; src = S86_CAST(uint16_t)opcode.immediate;
} }
} else if (opcode->src == S86_MnemonicOp_DirectAddress) { } else if (opcode.src == S86_MnemonicOp_DirectAddress) {
S86_ASSERT(opcode->displacement >= 0); S86_ASSERT(opcode.displacement >= 0);
src = memory[opcode->displacement]; src = memory[opcode.displacement];
} else { } else {
S86_MnemonicOpToRegisterFileMap const *src_map = NULL; S86_MnemonicOpToRegisterFileMap const *src_map = NULL;
for (size_t index = 0; !src_map && index < S86_ARRAY_UCOUNT(mnemonic_op_to_register_file_map); index++) { for (size_t index = 0; !src_map && index < S86_ARRAY_UCOUNT(mnemonic_op_to_register_file_map); index++) {
S86_MnemonicOpToRegisterFileMap const *item = mnemonic_op_to_register_file_map + index; S86_MnemonicOpToRegisterFileMap const *item = mnemonic_op_to_register_file_map + index;
if (item->mnemonic_op == opcode->src) if (item->mnemonic_op == opcode.src)
src_map = item; src_map = item;
} }
@ -1621,34 +1559,34 @@ int main(int argc, char **argv)
int lo_bit_count = _mm_popcnt_u32(S86_CAST(uint32_t)dest.bytes[S86_RegisterByte_Lo]); int lo_bit_count = _mm_popcnt_u32(S86_CAST(uint32_t)dest.bytes[S86_RegisterByte_Lo]);
register_file.flags.parity = lo_bit_count % 2 == 0; register_file.flags.parity = lo_bit_count % 2 == 0;
register_file.flags.zero = byte_op ? dest.bytes[dest_map->byte] == 0 : dest.word == 0; register_file.flags.zero = byte_op ? dest.bytes[dest_map->byte] == 0 : dest.word == 0;
if (opcode->mnemonic != S86_Mnemonic_CMP) if (opcode.mnemonic != S86_Mnemonic_CMP)
*dest_map->reg = dest; *dest_map->reg = dest;
} break; } break;
case S86_Mnemonic_JNE_JNZ: { case S86_Mnemonic_JNE_JNZ: {
if (!register_file.flags.zero) if (!register_file.flags.zero)
register_file.instruction_ptr += S86_CAST(int16_t)opcode->displacement; register_file.instruction_ptr += S86_CAST(int16_t)opcode.displacement;
} break; } break;
case S86_Mnemonic_JE_JZ: { case S86_Mnemonic_JE_JZ: {
if (register_file.flags.zero) if (register_file.flags.zero)
register_file.instruction_ptr += S86_CAST(int16_t)opcode->displacement; register_file.instruction_ptr += S86_CAST(int16_t)opcode.displacement;
} break; } break;
case S86_Mnemonic_JP_JPE: { case S86_Mnemonic_JP_JPE: {
if (register_file.flags.parity) if (register_file.flags.parity)
register_file.instruction_ptr += S86_CAST(int16_t)opcode->displacement; register_file.instruction_ptr += S86_CAST(int16_t)opcode.displacement;
} break; } break;
case S86_Mnemonic_JB_JNAE: { case S86_Mnemonic_JB_JNAE: {
if (register_file.flags.carry) if (register_file.flags.carry)
register_file.instruction_ptr += S86_CAST(int16_t)opcode->displacement; register_file.instruction_ptr += S86_CAST(int16_t)opcode.displacement;
} break; } break;
case S86_Mnemonic_LOOPNZ_LOOPNE: { case S86_Mnemonic_LOOPNZ_LOOPNE: {
register_file.reg.file.cx.word -= 1; register_file.reg.file.cx.word -= 1;
if (register_file.reg.file.cx.word != 0 && !register_file.flags.zero) if (register_file.reg.file.cx.word != 0 && !register_file.flags.zero)
register_file.instruction_ptr += S86_CAST(int16_t)opcode->displacement; register_file.instruction_ptr += S86_CAST(int16_t)opcode.displacement;
} break; } break;
} }
@ -1673,7 +1611,7 @@ int main(int argc, char **argv)
// NOTE: Instruction Pointer // NOTE: Instruction Pointer
if (log_instruction_ptr) if (log_instruction_ptr)
S86_PrintFmt("ip:0x%x->0x%x ", opcode->instruction_ptr, register_file.instruction_ptr); S86_PrintFmt("ip:0x%x->0x%x ", prev_ip, register_file.instruction_ptr);
// NOTE: Flags // NOTE: Flags
if (!S86_RegisterFileFlagsEq(register_file.flags, prev_register_file.flags)) { if (!S86_RegisterFileFlagsEq(register_file.flags, prev_register_file.flags)) {

View File

@ -323,8 +323,6 @@ typedef enum S86_WordBytePrefix {
typedef struct S86_Opcode { typedef struct S86_Opcode {
uint8_t byte_size; ///< Number of bytes used to encode this opcode uint8_t byte_size; ///< Number of bytes used to encode this opcode
uint16_t instruction_ptr; ///< The instruction pointer value at this opcode
uint16_t index; ///< Opcode index in the opcode array this was decoded from
S86_Mnemonic mnemonic; ///< Mnemonic type S86_Mnemonic mnemonic; ///< Mnemonic type
S86_EffectiveAddress effective_addr; ///< Src/dest op is an effective address calculation S86_EffectiveAddress effective_addr; ///< Src/dest op is an effective address calculation
bool effective_addr_loads_mem; ///< Effective address uses '[]' notation to load address memory bool effective_addr_loads_mem; ///< Effective address uses '[]' notation to load address memory