diff --git a/part1/build.bat b/part1/build.bat index d539379..a0e8f17 100644 --- a/part1/build.bat +++ b/part1/build.bat @@ -85,3 +85,10 @@ copy /Y %script_dir%\%listing_0046%.txt %build_dir% 1>NUL set build_dir_listing_0046=%build_dir%\%listing_0046% %build_dir%\sim8086.exe --exec %build_dir_listing_0046% > %build_dir_listing_0046%_disassembled.txt fc /N %build_dir_listing_0046%.txt %build_dir_listing_0046%_disassembled.txt || exit /b 1 + +set listing_0047=listing_0047_challenge_flags +copy /Y %script_dir%\%listing_0047% %build_dir% 1>NUL +copy /Y %script_dir%\%listing_0047%.txt %build_dir% 1>NUL +set build_dir_listing_0047=%build_dir%\%listing_0047% +%build_dir%\sim8086.exe --exec %build_dir_listing_0047% > %build_dir_listing_0047%_disassembled.txt +fc /N %build_dir_listing_0047%.txt %build_dir_listing_0047%_disassembled.txt || exit /b 1 diff --git a/part1/listing_0047_challenge_flags b/part1/listing_0047_challenge_flags new file mode 100644 index 0000000..58edaa0 Binary files /dev/null and b/part1/listing_0047_challenge_flags differ diff --git a/part1/listing_0047_challenge_flags.asm b/part1/listing_0047_challenge_flags.asm new file mode 100644 index 0000000..0a4cd1f --- /dev/null +++ b/part1/listing_0047_challenge_flags.asm @@ -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 47 +; ======================================================================== + +bits 16 + +add bx, 30000 +add bx, 10000 +sub bx, 5000 +sub bx, 5000 + +mov bx, 1 +mov cx, 100 +add bx, cx + +mov dx, 10 +sub cx, dx + +add bx, 40000 +add cx, -90 + +mov sp, 99 +mov bp, 98 +cmp bp, sp diff --git a/part1/listing_0047_challenge_flags.txt b/part1/listing_0047_challenge_flags.txt new file mode 100644 index 0000000..d33a1ca --- /dev/null +++ b/part1/listing_0047_challenge_flags.txt @@ -0,0 +1,23 @@ +--- test\listing_0047_challenge_flags execution --- +add bx, 30000 ; bx:0x0->0x7530 flags:->P +add bx, 10000 ; bx:0x7530->0x9c40 flags:P->SO +sub bx, 5000 ; bx:0x9c40->0x88b8 flags:SO->PAS +sub bx, 5000 ; bx:0x88b8->0x7530 flags:PAS->PO +mov bx, 1 ; bx:0x7530->0x1 +mov cx, 100 ; cx:0x0->0x64 +add bx, cx ; bx:0x1->0x65 flags:PO->P +mov dx, 10 ; dx:0x0->0xa +sub cx, dx ; cx:0x64->0x5a flags:P->PA +add bx, 40000 ; bx:0x65->0x9ca5 flags:PA->PS +add cx, -90 ; cx:0x5a->0x0 flags:PS->CPAZ +mov sp, 99 ; sp:0x0->0x63 +mov bp, 98 ; bp:0x0->0x62 +cmp bp, sp ; flags:CPAZ->CPAS + +Final registers: + bx: 0x9ca5 (40101) + dx: 0x000a (10) + sp: 0x0063 (99) + bp: 0x0062 (98) + flags: CPAS + diff --git a/part1/sim8086.c b/part1/sim8086.c index 0bd9b1e..946b190 100644 --- a/part1/sim8086.c +++ b/part1/sim8086.c @@ -22,15 +22,16 @@ typedef enum S86_RegisterByte { typedef union S86_Register16 { uint16_t word; - struct { uint8_t lo; uint8_t hi; } byte; uint8_t bytes[S86_RegisterByte_Count]; } S86_Register16; typedef struct S86_RegisterFileFlags { + bool carry; bool zero; bool sign; - bool parity; bool overflow; + bool parity; + bool auxiliary_carry; } S86_RegisterFileFlags; typedef struct S86_RegisterFile { @@ -54,10 +55,12 @@ typedef struct S86_RegisterFile { bool S86_RegisterFileFlagsEq(S86_RegisterFileFlags lhs, S86_RegisterFileFlags rhs) { - bool result = lhs.sign == rhs.sign && - lhs.zero == rhs.zero && - lhs.parity == rhs.parity && - lhs.overflow == rhs.overflow; + bool result = lhs.carry == rhs.carry && + lhs.zero == rhs.zero && + lhs.sign == rhs.sign && + lhs.overflow == rhs.overflow && + lhs.parity == rhs.parity && + lhs.auxiliary_carry == rhs.auxiliary_carry; return result; } @@ -278,7 +281,11 @@ void S86_PrintOpcodeMnemonicOp(S86_Opcode opcode, bool src) opcode.displacement > 0 ? '+' : '-', opcode.displacement > 0 ? opcode.displacement : -opcode.displacement); } else if (mnemonic_op == S86_MnemonicOp_Immediate) { - S86_PrintFmt("%u", opcode.immediate); + if (opcode.immediate_is_8bit) { + S86_PrintFmt("%d", (int8_t)opcode.immediate); + } else { + S86_PrintFmt("%u", (uint16_t)opcode.immediate); + } } else if (mnemonic_op == S86_MnemonicOp_DirectInterSegment) { uint16_t left = (uint32_t)opcode.displacement >> 16; uint16_t right = (uint32_t)opcode.displacement & 0xFFFF; @@ -623,8 +630,14 @@ S86_Opcode S86_DecodeOpcode(S86_BufferIterator *buffer_it, op_decode_type == S86_OpDecodeType_TESTImmediateAndRegOrMem || op_decode_type == S86_OpDecodeType_ORImmediateToRegOrMem || op_decode_type == S86_OpDecodeType_XORImmediateToRegOrMem) && s) { - // NOTE: Sign extend 8 bit, since we store into a - // int32_t in opcode this is done for free for us. + // NOTE: Sign extend 8 bit to 16 bit + uint16_t sign_mask = 0b1000'0000; + uint8_t data_u8 = S86_CAST(uint8_t)data; + if (sign_mask & data_u8) { + data = (uint16_t)((uint8_t)~data_u8 + 1); // Convert back to 8 bit unsigned + data = ~data + 1; // Convert to 16bit signed + result.immediate_is_8bit = true; + } } else { uint8_t data_hi = S86_BufferIteratorNextByte(buffer_it); data |= (uint16_t)(data_hi) << 8; @@ -635,8 +648,16 @@ S86_Opcode S86_DecodeOpcode(S86_BufferIterator *buffer_it, S86_ASSERT(mod != 0b11); // NOTE: Op is IMM->Reg, register-to-register not permitted } - result.immediate = data; - result.src = S86_MnemonicOp_Immediate; + // NOTE: Sign extend 16bit to 32bit + uint16_t sign_mask16 = 0b1000'0000'0000'0000; + if (data & sign_mask16) { + uint32_t data_no_sign_bit = (uint32_t)((uint16_t)~data + 1); // Convert back to 16 bit unsigned + result.immediate = ~data_no_sign_bit + 1; // Convert to signed 32bit + } else { + result.immediate = data; + } + + result.src = S86_MnemonicOp_Immediate; if (op_decode_type == S86_OpDecodeType_MOVImmediateToRegOrMem) result.wide_prefix = S86_WidePrefix_Src; else if (result.effective_addr_loads_mem) @@ -1362,17 +1383,19 @@ int main(int argc, char **argv) } S86_ASSERT(dest_map); - uint16_t sign = opcode.mnemonic == S86_Mnemonic_ADD ? 1 : -1; - S86_Register16 dest = *dest_map->reg; - uint16_t prev_dest16 = dest.word; - bool byte_op = opcode.dest >= S86_MnemonicOp_AL && opcode.dest <= S86_MnemonicOp_BH; + bool subtract = opcode.mnemonic != S86_Mnemonic_ADD; + S86_Register16 dest = *dest_map->reg; + S86_Register16 prev_dest = dest; + bool byte_op = opcode.dest >= S86_MnemonicOp_AL && opcode.dest <= S86_MnemonicOp_BH; + + uint16_t src = 0; if (opcode.src == S86_MnemonicOp_Immediate) { if (byte_op) { S86_ASSERT(opcode.immediate < S86_CAST(uint8_t)-1); - dest.bytes[dest_map->byte] += S86_CAST(uint8_t)(opcode.immediate * sign); + src = S86_CAST(uint8_t)opcode.immediate; } else { S86_ASSERT(opcode.immediate < S86_CAST(uint16_t)-1); - dest.word += S86_CAST(uint16_t)(opcode.immediate * sign); + src = S86_CAST(uint16_t)opcode.immediate; } } else { S86_MnemonicOpToRegisterFileMap const *src_map = NULL; @@ -1381,25 +1404,83 @@ int main(int argc, char **argv) if (item->mnemonic_op == opcode.src) src_map = item; } - - if (byte_op) - dest.bytes[dest_map->byte] += S86_CAST(uint8_t)(src_map->reg->bytes[src_map->byte] * sign); - else - dest.word += S86_CAST(uint16_t)(src_map->reg->word * sign); + src = byte_op ? src_map->reg->bytes[src_map->byte] : src_map->reg->word; } - int bit_count = _mm_popcnt_u32(S86_CAST(uint32_t)dest.bytes[S86_RegisterByte_Lo]); - register_file.flags.parity = (bit_count % 2 == 0); + // NOTE: Overflow if the sign masks were initially the same, + // but, after the operation the sign masked changed. + uint8_t const sign_mask8 = 0b1000'0000; + uint16_t const sign_mask16 = 0b1000'0000'0000'0000; + if (byte_op) { + uint8_t src_u8 = S86_CAST(uint8_t)src; + if (subtract) + src_u8 = ~src_u8 + 1; - S86_Str8 dest_reg16 = S86_MnemonicOpStr8(dest_map->mnemonic_op_reg16); - register_file.flags.sign = dest_map->reg->word & (byte_op ? 0b0000'0000'1000'0000 : 0b1000'0000'0000'0000); + uint8_t dest_u8 = dest.bytes[dest_map->byte]; + uint8_t new_dest_u8 = dest_u8 + src_u8; + + // NOTE: Overflow check + bool initially_matching_sign_masks = (dest_u8 & sign_mask8) == (src_u8 & sign_mask8); + bool sign_masks_changed = (dest_u8 & sign_mask8) != (new_dest_u8 & sign_mask8); + register_file.flags.overflow = initially_matching_sign_masks && sign_masks_changed; + + // NOTE: Carry check + register_file.flags.carry = subtract ? new_dest_u8 > dest_u8 : new_dest_u8 < dest_u8; + + // NOTE: Auxiliary carry check + uint8_t dest_u8_nibble_lo = dest_u8 & 0b0000'1111 >> 0; + uint8_t dest_u8_nibble_hi = dest_u8 & 0b1111'0000 >> 4; + uint8_t new_dest_u8_nibble_lo = new_dest_u8 & 0b0000'1111 >> 0; + uint8_t new_dest_u8_nibble_hi = new_dest_u8 & 0b1111'0000 >> 4; + register_file.flags.auxiliary_carry = subtract ? new_dest_u8_nibble_hi > dest_u8_nibble_hi + : new_dest_u8_nibble_lo < dest_u8_nibble_lo; + + // NOTE: Sign check + register_file.flags.sign = new_dest_u8 & 0b1000'0000; + + // NOTE: Update the register + dest.bytes[dest_map->byte] = new_dest_u8; + } else { + if (subtract) + src = ~src + 1; + + S86_Register16 new_dest = {0}; + new_dest.word = dest.word + src; + + // NOTE: Overflow check + bool initially_matching_sign_masks = (dest.word & sign_mask16) == (src & sign_mask16); + bool sign_masks_changed = (dest.word & sign_mask16) != (new_dest.word & sign_mask16); + register_file.flags.overflow = initially_matching_sign_masks && sign_masks_changed; + + // NOTE: Auxiliary carry check + uint8_t dest_lo_nibble_lo = dest.bytes[S86_RegisterByte_Lo] & 0b0000'1111 >> 0; + uint8_t dest_lo_nibble_hi = dest.bytes[S86_RegisterByte_Lo] & 0b1111'0000 >> 4; + uint8_t new_dest_lo_nibble_lo = new_dest.bytes[S86_RegisterByte_Lo] & 0b0000'1111 >> 0; + uint8_t new_dest_lo_nibble_hi = new_dest.bytes[S86_RegisterByte_Lo] & 0b1111'0000 >> 4; + register_file.flags.auxiliary_carry = subtract ? new_dest_lo_nibble_hi > dest_lo_nibble_hi + : new_dest_lo_nibble_lo < dest_lo_nibble_lo; + + // NOTE: Carry check + register_file.flags.carry = subtract ? new_dest.word > dest.word : new_dest.word < dest.word; + + // NOTE: Sign check + register_file.flags.sign = new_dest.word & 0b1000'0000'0000'0000; + + // NOTE: Update the register + dest.word = new_dest.word; + } + + 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.zero = false; + if (opcode.mnemonic == S86_Mnemonic_ADD || opcode.mnemonic == S86_Mnemonic_SUB) + register_file.flags.zero = byte_op ? dest.bytes[dest_map->byte] == 0 : dest.word == 0; if (opcode.mnemonic == S86_Mnemonic_CMP) { S86_PrintFmt(" ; "); } else { - if (opcode.mnemonic == S86_Mnemonic_SUB) - register_file.flags.zero = byte_op ? dest.bytes[dest_map->byte] == 0 : dest.word == 0; - S86_PrintFmt(" ; %.*s:0x%x->0x%x ", S86_STR8_FMT(dest_reg16), prev_dest16, dest.word); + S86_Str8 dest_reg16 = S86_MnemonicOpStr8(dest_map->mnemonic_op_reg16); + S86_PrintFmt(" ; %.*s:0x%x->0x%x ", S86_STR8_FMT(dest_reg16), prev_dest.word, dest.word); *dest_map->reg = dest; } } break; @@ -1407,23 +1488,31 @@ int main(int argc, char **argv) if (!S86_RegisterFileFlagsEq(register_file.flags, prev_flags)) { S86_PrintFmt("flags:"); - if (prev_flags.parity && !register_file.flags.parity) + if (prev_flags.carry) + S86_PrintFmt("C"); + if (prev_flags.parity) S86_PrintFmt("P"); - if (prev_flags.zero && !register_file.flags.zero) + if (prev_flags.auxiliary_carry) + S86_PrintFmt("A"); + if (prev_flags.zero) S86_PrintFmt("Z"); - if (prev_flags.sign && !register_file.flags.sign) + if (prev_flags.sign) S86_PrintFmt("S"); - if (prev_flags.overflow && !register_file.flags.overflow) + if (prev_flags.overflow) S86_PrintFmt("O"); S86_PrintFmt("->"); - if (!prev_flags.parity && register_file.flags.parity) + if (register_file.flags.carry) + S86_PrintFmt("C"); + if (register_file.flags.parity) S86_PrintFmt("P"); - if (!prev_flags.zero && register_file.flags.zero) + if (register_file.flags.auxiliary_carry) + S86_PrintFmt("A"); + if (register_file.flags.zero) S86_PrintFmt("Z"); - if (!prev_flags.sign && register_file.flags.sign) + if (register_file.flags.sign) S86_PrintFmt("S"); - if (!prev_flags.overflow && register_file.flags.overflow) + if (register_file.flags.overflow) S86_PrintFmt("O"); S86_PrintFmt(" "); } @@ -1458,8 +1547,12 @@ int main(int argc, char **argv) S86_RegisterFileFlags nil_flags = {0}; if (!S86_RegisterFileFlagsEq(register_file.flags, nil_flags)) { S86_PrintFmt(" flags: "); + if (register_file.flags.carry) + S86_PrintFmt("C"); if (register_file.flags.parity) S86_PrintFmt("P"); + if (register_file.flags.auxiliary_carry) + S86_PrintFmt("A"); if (register_file.flags.zero) S86_PrintFmt("Z"); if (register_file.flags.sign) diff --git a/part1/sim8086.h b/part1/sim8086.h index 3d139ad..534908c 100644 --- a/part1/sim8086.h +++ b/part1/sim8086.h @@ -333,6 +333,7 @@ typedef struct S86_Opcode { S86_MnemonicOp dest; ///< Destination op for the mnemonic int32_t displacement; ///< Opcode has displacement/data/offset int32_t immediate; ///< Immediate value when src/dest op is an immediate + bool immediate_is_8bit; ///< Immediate was 8bit and sign extended S86_MnemonicOp seg_reg_prefix; ///< Segment register that should prefix the upcoming instruction } S86_Opcode; diff --git a/project.rdbg b/project.rdbg index 9768cde..97d0b68 100644 Binary files a/project.rdbg and b/project.rdbg differ