Finished the first hw.

On to the second...
This commit is contained in:
Edward R. Gonzalez 2023-03-05 20:57:40 -05:00
parent 5b230d5653
commit 9be706b28d
9 changed files with 350 additions and 301 deletions

19
.vscode/launch.json vendored Normal file
View File

@ -0,0 +1,19 @@
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"name": "(Windows) Launch",
"type": "cppvsdbg",
"request": "launch",
"program": "${workspaceRoot}/part_1/build/sim_8086.exe",
"args": [],
"stopAtEntry": false,
"cwd": "${workspaceRoot}/part_1",
"environment": [],
"console": "externalTerminal"
}
]
}

View File

@ -1,6 +1,8 @@
{
"files.associations": {
"*.rmd": "markdown",
"zpl.h": "c"
"zpl.h": "c",
"cstddef": "cpp",
"xtr1common": "cpp"
}
}

111
part_1/bloat.hpp Normal file
View File

@ -0,0 +1,111 @@
#if __clang__
# pragma clang diagnostic ignored "-Wunused-const-variable"
# pragma clang diagnostic ignored "-Wswitch"
# pragma clang diagnostic ignored "-Wunused-variable"
#endif
#pragma region ZPL INCLUDE
#if __clang__
# pragma clang diagnostic push
# pragma clang diagnostic ignored "-Wmissing-braces"
# pragma clang diagnostic ignored "-Wbraced-scalar-init"
#endif
#define ZPL_IMPLEMENTATION
// # define ZPL_HEAP_ANALYSIS
# define ZPL_NO_MATH_H
# define ZPL_CUSTOM_MODULES
# define ZPL_MODULE_ESSENTIALS
# define ZPL_MODULE_CORE
# define ZPL_MODULE_TIMER
// # define ZPL_MODULE_HASHING
// # define ZPL_MODULE_REGEX
// # define ZPL_MODULE_EVENT
// # define ZPL_MODULE_DLL
// # define ZPL_MODULE_OPTS
// # define ZPL_MODULE_PROCESS
// # define ZPL_MODULE_MATH
// # define ZPL_MODULE_THREADING
// # define ZPL_MODULE_JOBS
// # define ZPL_MODULE_PARSER
extern "C" {
#include "zpl.h"
}
#if __clang__
# pragma clang diagnostic pop
#endif
#undef cast
#pragma endregion ZPL INCLUDE
#define bit( Value_ ) ( 1 << Value_ )
#define bitfield_is_equal( Field_, Mask_ ) ( ( Mask_ & Field_ ) == Mask_ )
#define cast( Type_ , Value_ ) ( ( Type_ )( Value_ ) )
#define ct constexpr
#define gen( ... ) template< __VA_ARGS__ >
#define forceinline ZPL_ALWAYS_INLINE
#define print_nl( _) zpl_printf("\n")
#define Msg_Invalid_Value "INVALID VALUE PROVIDED"
#define scast static_cast
#define rcast reinterpret_cast
using u8 = zpl_u8;
using u16 = zpl_u16;
using u32 = zpl_u32;
using uw = zpl_usize;
using sw = zpl_isize;
ct inline
char char_binary( u8 value, u8 pos )
{
u8 mask = 1 << pos ;
return ( (1 << pos) & value) == mask ? '1' : '0';
}
inline
void str_binary( char* result, u8 value )
{
#if 0
result[0] = char_binary( value, 0);
result[1] = char_binary( value, 1);
result[2] = char_binary( value, 2);
result[3] = char_binary( value, 3);
result[4] = char_binary( value, 4);
result[5] = char_binary( value, 5);
result[6] = char_binary( value, 6);
result[7] = char_binary( value, 7);
#else
result[0] = char_binary( value, 7);
result[1] = char_binary( value, 6);
result[2] = char_binary( value, 5);
result[3] = char_binary( value, 4);
result[4] = char_binary( value, 3);
result[5] = char_binary( value, 2);
result[6] = char_binary( value, 1);
result[7] = char_binary( value, 0);
#endif
}
inline
void print_as_binary( u8* data, uw size, char const* byte_term )
{
char
binary_string[9];
binary_string[8] = '\0';
while ( size-- )
{
str_binary( binary_string, data[0]);
printf( "%s%s", binary_string, byte_term );
data += 1;
}
}

View File

@ -4,4 +4,16 @@ if not exist build\nul (
meson setup build
)
echo:
if not exist tests\listing_0037_single_register_mov (
echo Assembling: listing_0037_single_register_mov.asm
call nasm ".\tests\listing_0037_single_register_mov.asm"
)
if not exist tests\listing_0038_many_register_mov (
echo Assembling: listing_0038_many_register_mov.asm
call nasm ".\tests\listing_0038_many_register_mov.asm"
)
echo:
ninja -C build

BIN
part_1/debug.rdbg Normal file

Binary file not shown.

View File

@ -2,4 +2,12 @@ project( 'sim_8086', 'c', 'cpp', default_options : ['buildtype=debug'] )
include_thirdparty = include_directories( '../thirdparty' )
# add_global_arguments('-E', language : 'cpp')
if get_option('buildtype').startswith('debug')
add_project_arguments('-DBuild_Debug', language : 'cpp')
endif
executable( 'sim_8086', 'sim_8086.cpp', include_directories : include_thirdparty )

View File

@ -1,149 +1,42 @@
#if __clang__
#pragma clang diagnostic ignored "-Wunused-const-variable"
#endif
#include "bloat.hpp"
zpl_arena BlobArena {};
#define allocator zpl_arena_allocator( & BlobArena)
#pragma region ZPL INCLUDE
void setup()
{
zpl_arena_init_from_allocator( & BlobArena, zpl_heap(), zpl_megabytes(10) );
#if __clang__
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wmissing-braces"
#pragma clang diagnostic ignored "-Wbraced-scalar-init"
#endif
if ( BlobArena.total_size == 0 )
{
zpl_assert_crash( "Failed to reserve memory for Tests:: BLobArena" );
}
}
#define ZPL_IMPLEMENTATION
#include "zpl.h"
void cleanup()
{
zpl_arena_free( & BlobArena);
}
#if __clang__
#pragma clang diagnostic pop
#endif
#pragma endregion ZPL INCLUDE
// This has me screwing around with generating lookup tables.
// Going to measure the difference between them and jump tables instructions at some point...
// Binary formatting for literals is used heavily as that is
// Binary formatting for literals is used heavily as that its
// how the encoding is conveyed in the hardware reference from intel.
#define bit( value ) ( 1 << value )
#define bitfield_is_equal( field_, mask_ ) ( ( mask_ & field_ ) == mask_ )
#define ct constexpr
#define forceinline ZPL_ALWAYS_INLINE
#define MSG_INVALID_VALUE "INVALID VALUE PROVIDED"
using u8 = unsigned char;
using u16 = unsigned short;
using u32 = unsigned long;
// using bytecode = u8;
inline
char char_binary( u8 value, u8 pos )
{
constexpr u8 baseline = 1;
u8 mask = baseline << pos;
return ( (baseline << pos) & value) == mask ? '1' : '0';
}
inline
void str_binary( char* result, u8 value )
{
#if 0
result[0] = char_binary( value, 0);
result[1] = char_binary( value, 1);
result[2] = char_binary( value, 2);
result[3] = char_binary( value, 3);
result[4] = char_binary( value, 4);
result[5] = char_binary( value, 5);
result[6] = char_binary( value, 6);
result[7] = char_binary( value, 7);
#else
result[0] = char_binary( value, 7);
result[1] = char_binary( value, 6);
result[2] = char_binary( value, 5);
result[3] = char_binary( value, 4);
result[4] = char_binary( value, 3);
result[5] = char_binary( value, 2);
result[6] = char_binary( value, 1);
result[7] = char_binary( value, 0);
#endif
}
#define USE_LOOKUP_TABLE 0
namespace Op
{
ct u8 Mask = 0b11111100;
#define D_Opcodes \
D_Entry( mov_88 ) \
D_Entry( mov_89 ) \
using Type = u8;
enum
enum Code : u8
{
#if USE_LOOKUP_TABLE
#define D_Entry( Entry_ ) Entry_,
D_Opcodes
#undef D_Entry
#else
Mask = 0b11111100,
mov_88 = 0b10001000,
mov_89 = 0b11000100,
#endif
Num = 2
};
#if USE_LOOKUP_TABLE
ct u8 code( Type type )
char const* str( Code type )
{
constexpr u8
type_to_code[ Num ] =
{
0b01000100, // mov_88
0b11000100, // mov_89
};
return type_to_code[ type ];
}
ct u8 type( u8 code )
{
switch ( code )
{
#define D_Entry
case 0b01000100:
return mov_88;
#undef D_Entry
}
return Num;
}
#endif
char const* str( Type type )
{
#if Op_USE_LOOKUP_TABLE
static char const*
type_to_str[ Num ] =
{
#define D_Entry( Entry_ ) #Entry_,
D_Opcodes
#undef D_Entry
};
return type_to_str[ type ];
#else
switch ( type )
{
case mov_88:
@ -152,11 +45,10 @@ namespace Op
return "mov_89";
}
return MSG_INVALID_VALUE;
#endif
return Msg_Invalid_Value;
}
ct char const* helper_meumonic( Type type )
ct char const* helper_meumonic( Code type )
{
switch ( type )
{
@ -165,23 +57,11 @@ namespace Op
return "mov";
}
return MSG_INVALID_VALUE;
return Msg_Invalid_Value;
}
char const* meumonic( Type type )
char const* meumonic( Code type )
{
#if USE_LOOKUP_TABLE
static char const*
type_to_meumonic[ Num ] =
{
#define D_Entry( Entry_ ) helper_meumonic( Entry_ ),
D_Opcodes
#undef D_Entry
};
return type_to_meumonic[ type ];
#else
switch ( type )
{
case mov_88:
@ -189,11 +69,10 @@ namespace Op
return "mov";
}
return MSG_INVALID_VALUE;
#endif
return Msg_Invalid_Value;
}
ct char const* helper_intuitive( Type type )
ct char const* helper_intuitive( Code type )
{
switch ( type )
{
@ -202,24 +81,11 @@ namespace Op
return "move";
}
return MSG_INVALID_VALUE;
return Msg_Invalid_Value;
}
char const* intuitive( Type type )
char const* intuitive( Code type )
{
#if USE_LOOKUP_TABLE
static char const*
type_to_meumonic[ Num ] =
{
#define D_Entry( Entry_ ) helper_intuitive( Entry_ ),
D_Opcodes
#undef D_Entry
};
return type_to_meumonic[ type ];
#else
switch ( type )
{
case mov_88:
@ -227,8 +93,7 @@ namespace Op
return "move";
}
return MSG_INVALID_VALUE;
#endif
return Msg_Invalid_Value;
}
#undef D_Opcodes
@ -236,51 +101,46 @@ namespace Op
namespace Field
{
#if 0
using Type = u8;
enum
{
Dir_REG_Dest = 0b00,
Dir_REG_Src = 0b10,
};
#endif
ct u8 Mask_Dir = 0b00000010;
ct u8 Mask_Width = 0b00000001;
ct u8 Mask_Mode = 0b11000000;
// Mask: Effective Address
inline u8 mask_reg_operand( u8 reg )
// Effective Address offset
inline
u8 effective_address( u8 reg )
{
return reg << 3;
}
// https://i.imgur.com/drsyYnM.png
ct u8 Dir_REG_Src = 0b00;
ct u8 Dir_REG_Dest = 0b10;
enum Direction : u8
{
Mask_Dir = 0b00000010,
Dir_Src = 0b00,
Dir_Dest = 0b10,
};
inline
char const* str_direction( u8 direction )
char const* str_direction( Direction direction )
{
switch ( direction )
{
case Dir_REG_Dest:
case Dir_Dest:
return "Destination";
case Dir_REG_Src:
case Dir_Src:
return "Source";
}
return MSG_INVALID_VALUE;
return Msg_Invalid_Value;
}
// https://i.imgur.com/9Op8Lnd.png
ct u8 Width_Byte = 0b00;
ct u8 Width_Word = 0b01;
enum Width : u8
{
Mask_Width = 0b00000001,
Width_Byte = 0b00,
Width_Word = 0b01,
};
inline
char const* str_width( u8 width )
char const* str_width( Width width )
{
switch ( width )
{
@ -291,16 +151,20 @@ namespace Field
return "Word";
}
return MSG_INVALID_VALUE;
return Msg_Invalid_Value;
}
// https://i.imgur.com/Job2oPd.png
ct u8 Mode_Mem = 0b00000000;
ct u8 Mode_Mem8 = 0b01000000;
ct u8 Mode_Mem16 = 0b10000000;
ct u8 Mode_Reg = 0b11000000;
enum Mode : u8
{
Mask_Mode = 0b11000000,
Mode_Mem = 0b00000000,
Mode_Mem8 = 0b01000000,
Mode_Mem16 = 0b10000000,
Mode_Reg = 0b11000000,
};
char const* str_mode( u8 mode )
char const* str_mode( Mode mode )
{
switch (mode)
{
@ -317,7 +181,7 @@ namespace Field
return "Register";
}
return MSG_INVALID_VALUE;
return Msg_Invalid_Value;
}
#if 0
@ -352,10 +216,8 @@ namespace Register
D_Entry( SI ) \
D_Entry( DI ) \
using Type = u8;
enum
enum Type : u8
{
// Endianness is prob wrong...
AL = 0b000,
CL = 0b001,
DL = 0b010,
@ -415,7 +277,7 @@ namespace Register
"Destination.Index",
};
return Type_To_Intuitive[ type + 8 * Width ];
return Type_To_Intuitive[ type + Width * 7 + 1 ];
}
}
@ -434,69 +296,75 @@ struct POD_Instruction
struct Instruction : public POD_Instruction
{
using Code = Op::Code;
using Direction = Field::Direction;
using Mode = Field::Mode;
using Width = Field::Width;
using Reg = Register::Type;
inline
u8 direction()
Direction direction()
{
u8 direction = Byte_1 & Field::Mask_Dir;
Direction
direction = cast(Direction, Byte_1 & Field::Mask_Dir);
return direction;
}
inline
u8 mode()
Mode mode()
{
u8 mode = Byte_2 & Field::Mask_Mode;
Mode mode = cast( Mode, Byte_2 & Field::Mask_Mode );
return mode;
}
inline
u8 opcode()
Code opcode()
{
u8
opcode = Byte_1 & Op::Mask;
Op::Code
opcode = cast( Op::Code, Byte_1 & Op::Mask );
return opcode;
}
inline
u8 operand_left()
Reg operand_left()
{
u8
operand = Byte_2 & Register::Mask_Left;
operand >>= 3;
return operand;
return cast(Reg, operand);
}
inline
u8 operand_right()
Reg operand_right()
{
u8
operand = Byte_2 & Register::Mask_Right;
// operand <<= 1;
u8 operand = Byte_2 & Register::Mask_Right;
return operand;
return cast( Reg, operand );
}
inline
u8 width()
Width width()
{
u8 width = Byte_1 & Field::Mask_Width;
Width width = cast( Width, Byte_1 & Field::Mask_Width );
return width;
}
void disassemble()
void dissassemble( zpl_string* result_out )
{
using namespace Field;
using namespace Op;
using namespace Register;
u8 width_val = width();
Direction dir = direction();
Width width_val = width();
char const* opcode_str = Op::meumonic( opcode() );
char const* direction_str = Field::str_direction( direction() );
char const* direction_str = Field::str_direction( dir );
char const* width_str = Field::str_width( width_val );
char const* mode_str = Field::str_mode( mode() );
char const* operand_left_str = Register::meumonic( operand_left(), width_val );
@ -506,10 +374,11 @@ struct Instruction : public POD_Instruction
binary_string[9];
binary_string[8] = '\0';
#if Build_Debug
str_binary( binary_string, opcode() );
zpl_printf("\nOpcode : %s : %s", binary_string, opcode_str);
str_binary( binary_string, direction() );
str_binary( binary_string, dir );
zpl_printf("\nDirection : %s : %s", binary_string, direction_str);
str_binary( binary_string, width_val );
@ -523,107 +392,124 @@ struct Instruction : public POD_Instruction
str_binary( binary_string, operand_right() );
zpl_printf("\nOperand EA : %s : %s", binary_string, operand_right_str);
#endif
if ( result_out == nullptr )
return;
if ( * result_out == nullptr )
* result_out = zpl_string_make_reserve( allocator, zpl_kilobytes(1) );
zpl_string assembly = zpl_string_make_reserve( allocator, 32);
assembly = zpl_string_sprintf( allocator, assembly, zpl_kilobytes(1) - 1, "\n%s %s, %s"
, opcode_str
, dir == Dir_Src ? operand_right_str : operand_left_str
, dir == Dir_Src ? operand_left_str : operand_right_str
);
* result_out = zpl_string_append( * result_out, assembly );
#if Build_Debug
zpl_printf("\n\nAssembly: %s\n\n", assembly);
#endif
}
};
namespace Tests
{
zpl_arena BlobArena {};
void Init()
{
zpl_arena_init_from_allocator( & BlobArena, zpl_heap(), zpl_megabytes(1) );
if ( BlobArena.total_size == 0 )
{
zpl_assert_crash( "Failed to reserve memory for Tests:: BLobArena" );
}
}
void Try_MockInstruction()
void try_mock_instruction()
{
using namespace Field;
using namespace Op;
using namespace Register;
Instruction
mock; // mov AX, BX
#if USE_LOOKUP_TABLE
mock.Byte_1 = Op::code(mov_88) | Dir_REG_Dest | Field::Width_Word;
#else
mock.Byte_1 = mov_88 | Dir_REG_Src | Field::Width_Word;
#endif
mock.Byte_2 = Field::Mode_Reg | Field::mask_reg_operand(BX) | CX;
mock; // mov CX, BX
mock.Byte_1 = mov_88 | Dir_Src | Field::Width_Word;
mock.Byte_2 = Field::Mode_Reg | effective_address(BX) | CX;
zpl_printf("\n\nAttempting Mock Instruction: mov CX, BX");
char
binary_string[9];
binary_string[8] = '\0';
print_nl();
print_as_binary( & mock.Byte_1, 1, " " );
print_as_binary( & mock.Byte_2, 1, " " );
print_nl();
str_binary( binary_string, mock.Byte_1 );
zpl_printf("\n%s", binary_string);
zpl_string dissasembly = nullptr;
str_binary( binary_string, mock.Byte_2 );
zpl_printf("\n%s", binary_string);
mock.disassemble();
mock.dissassemble( & dissasembly);
}
void Try_Blob_SingleRegisterMove()
void try_blob_single_instruction()
{
zpl_printf("\n\nAttempting to read blob: listing_0037_single_register_mov");
zpl_file_contents
blob = zpl_file_read_contents( zpl_arena_allocator( & BlobArena), false,
"tests/listing_0037_single_register_mov"
// "tests/listing_0038_many_register_mov"
);
blob = zpl_file_read_contents( allocator, false, "tests/listing_0037_single_register_mov" );
if (blob.data == nullptr )
{
return;
}
printf("\nContents:\n");
u32 left = blob.size;
u8* data = cast(u8*)blob.data
// + blob.size - 1
;
u8* data = cast( u8*, blob.data );
char
binary_string[9];
binary_string[8] = '\0';
print_nl();
print_as_binary( data, left, " " );
print_nl();
while ( left-- )
{
str_binary( binary_string, data[0]);
printf("%s\n", binary_string );
data += 1;
}
printf("\n");
left = blob.size;
// data += blob.size;
data -= blob.size;
while ( left-- )
{
str_binary( binary_string, data[0]);
printf("%X", data[0] );
data += 1;
}
zpl_string dissasembly = nullptr;
Instruction
instr;
instr.Byte_1 = ((u8*)blob.data)[0];
instr.Byte_2 = ((u8*)blob.data)[1];
instr.disassemble();
instr.Byte_1 = data[0];
instr.Byte_2 = data[1];
instr.dissassemble( & dissasembly);
}
void try_blob_many_instructions()
{
zpl_printf("\n\nAttempting to read blob: listing_0038_many_register_mov");
zpl_file_contents
blob = zpl_file_read_contents( allocator, false, "tests/listing_0038_many_register_mov" );
if (blob.data == nullptr )
return;
u32 left = blob.size;
u8* data = cast( u8*, blob.data );
print_nl();
print_as_binary( data, left, " " );
print_nl();
zpl_string dissasembly = zpl_string_make( allocator, "bits 16\n");
while ( left )
{
Instruction
instr;
instr.Byte_1 = data[0];
instr.Byte_2 = data[1];
instr.dissassemble( & dissasembly);
data += 2;
left -= 2;
}
zpl_printf("\n\nDissassemlby:\n%s", dissasembly);
dissasembly = zpl_string_append_fmt( dissasembly, "\n" );
zpl_printf("\n\nSaving to file");
zpl_file_write_contents("tests/listing_0038_many_register_mov.out.asm"
, dissasembly
, zpl_string_length(dissasembly)
, nullptr
);
}
#undef allocator
}
@ -631,17 +517,23 @@ int main()
{
zpl_printf("sim 8086!");
Tests::Init();
setup();
zpl_f64 start = zpl_time_rel();
Tests::Try_MockInstruction();
Tests::Try_Blob_SingleRegisterMove();
#if 0
Tests::try_mock_instruction();
Tests::try_blob_single_instruction();
#endif
Tests::try_blob_many_instructions();
zpl_f64 end = zpl_time_rel();
zpl_f64 ms = (end - start) * 100;
printf("\n\nElapsed Time: %lf", end - start);
printf("\n\nElapsed Time: %lf ms", ms);
printf("\n\n");
cleanup();
return 0;
}

View File

@ -1,8 +0,0 @@
@echo off
echo Assembling: listing_0037_single_register_mov.asm
call nasm ".\tests\listing_0037_single_register_mov.asm"
echo Assembling: listing_0038_many_register_mov.asm
call nasm ".\tests\listing_0038_many_register_mov.asm"

View File

@ -0,0 +1,13 @@
bits 16
mov CX, BX
mov DH, CH
mov DX, BX
mov SI, BX
mov BX, DI
mov CL, DL
mov DH, DH
mov BX, AX
mov BX, SI
mov SP, DI
mov BP, AX