perfaware/part1: Support add instructions

This commit is contained in:
doyle 2023-03-15 23:56:30 +11:00 committed by committed-name
parent 9f76a7d181
commit bcd509318c
10 changed files with 690 additions and 73 deletions

View File

@ -8,7 +8,7 @@
;
; Please see https://computerenhance.com for further information
;
; ======================================================================== */
; ========================================================================
; ========================================================================
; LISTING 37

View File

@ -8,7 +8,7 @@
;
; Please see https://computerenhance.com for further information
;
; ======================================================================== */
; ========================================================================
; ========================================================================
; LISTING 38

Binary file not shown.

View File

@ -21,10 +21,12 @@ mov si, bx
mov dh, al
; 8-bit immediate-to-register
mov cx, 12
mov cx, -12
mov cl, 12
mov ch, -12
; 16-bit immediate-to-register
mov cx, 12
mov cx, -12
mov dx, 3948
mov dx, -3948

Binary file not shown.

View File

@ -0,0 +1,121 @@
; ========================================================================
;
; (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 41
; ========================================================================
bits 16
add bx, [bx+si]
add bx, [bp]
add si, 2
add bp, 2
add cx, 8
add bx, [bp + 0]
add cx, [bx + 2]
add bh, [bp + si + 4]
add di, [bp + di + 6]
add [bx+si], bx
add [bp], bx
add [bp + 0], bx
add [bx + 2], cx
add [bp + si + 4], bh
add [bp + di + 6], di
add byte [bx], 34
add word [bp + si + 1000], 29
add ax, [bp]
add al, [bx + si]
add ax, bx
add al, ah
add ax, 1000
add al, -30
add al, 9
sub bx, [bx+si]
sub bx, [bp]
sub si, 2
sub bp, 2
sub cx, 8
sub bx, [bp + 0]
sub cx, [bx + 2]
sub bh, [bp + si + 4]
sub di, [bp + di + 6]
sub [bx+si], bx
sub [bp], bx
sub [bp + 0], bx
sub [bx + 2], cx
sub [bp + si + 4], bh
sub [bp + di + 6], di
sub byte [bx], 34
sub word [bx + di], 29
sub ax, [bp]
sub al, [bx + si]
sub ax, bx
sub al, ah
sub ax, 1000
sub al, -30
sub al, 9
cmp bx, [bx+si]
cmp bx, [bp]
cmp si, 2
cmp bp, 2
cmp cx, 8
cmp bx, [bp + 0]
cmp cx, [bx + 2]
cmp bh, [bp + si + 4]
cmp di, [bp + di + 6]
cmp [bx+si], bx
cmp [bp], bx
cmp [bp + 0], bx
cmp [bx + 2], cx
cmp [bp + si + 4], bh
cmp [bp + di + 6], di
cmp byte [bx], 34
cmp word [4834], 29
cmp ax, [bp]
cmp al, [bx + si]
cmp ax, bx
cmp al, ah
cmp ax, 1000
cmp al, -30
cmp al, 9
test_label0:
jnz test_label1
jnz test_label0
test_label1:
jnz test_label0
jnz test_label1
label:
je label
jl label
jle label
jb label
jbe label
jp label
jo label
js label
jne label
jnl label
jg label
jnb label
ja label
jnp label
jno label
jns label
loop label
loopz label
loopnz label
jcxz label

Binary file not shown.

View File

@ -0,0 +1,425 @@
; ========================================================================
;
; (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 42
; ========================================================================
;
; NOTE(casey): This is not meant to be a real compliance test for 8086
; disassemblers. It's just a reasonable selection of opcodes and patterns
; to use as a first pass in making sure a disassembler handles a large
; cross-section of the encoding. To be absolutely certain you haven't
; missed something, you would need a more exhaustive listing!
;
bits 16
mov si, bx
mov dh, al
mov cl, 12
mov ch, -12
mov cx, 12
mov cx, -12
mov dx, 3948
mov dx, -3948
mov al, [bx + si]
mov bx, [bp + di]
mov dx, [bp]
mov ah, [bx + si + 4]
mov al, [bx + si + 4999]
mov [bx + di], cx
mov [bp + si], cl
mov [bp], ch
mov ax, [bx + di - 37]
mov [si - 300], cx
mov dx, [bx - 32]
mov [bp + di], byte 7
mov [di + 901], word 347
mov bp, [5]
mov bx, [3458]
mov ax, [2555]
mov ax, [16]
mov [2554], ax
mov [15], ax
push word [bp + si]
push word [3000]
push word [bx + di - 30]
push cx
push ax
push dx
push cs
pop word [bp + si]
pop word [3]
pop word [bx + di - 3000]
pop sp
pop di
pop si
pop ds
xchg ax, [bp - 1000]
xchg [bx + 50], bp
xchg ax, ax
xchg ax, dx
xchg ax, sp
xchg ax, si
xchg ax, di
xchg cx, dx
xchg si, cx
xchg cl, ah
in al, 200
in al, dx
in ax, dx
out 44, ax
out dx, al
xlat
lea ax, [bx + di + 1420]
lea bx, [bp - 50]
lea sp, [bp - 1003]
lea di, [bx + si - 7]
lds ax, [bx + di + 1420]
lds bx, [bp - 50]
lds sp, [bp - 1003]
lds di, [bx + si - 7]
les ax, [bx + di + 1420]
les bx, [bp - 50]
les sp, [bp - 1003]
les di, [bx + si - 7]
lahf
sahf
pushf
popf
add cx, [bp]
add dx, [bx + si]
add [bp + di + 5000], ah
add [bx], al
add sp, 392
add si, 5
add ax, 1000
add ah, 30
add al, 9
add cx, bx
add ch, al
adc cx, [bp]
adc dx, [bx + si]
adc [bp + di + 5000], ah
adc [bx], al
adc sp, 392
adc si, 5
adc ax, 1000
adc ah, 30
adc al, 9
adc cx, bx
adc ch, al
inc ax
inc cx
inc dh
inc al
inc ah
inc sp
inc di
inc byte [bp + 1002]
inc word [bx + 39]
inc byte [bx + si + 5]
inc word [bp + di - 10044]
inc word [9349]
inc byte [bp]
aaa
daa
sub cx, [bp]
sub dx, [bx + si]
sub [bp + di + 5000], ah
sub [bx], al
sub sp, 392
sub si, 5
sub ax, 1000
sub ah, 30
sub al, 9
sub cx, bx
sub ch, al
sbb cx, [bp]
sbb dx, [bx + si]
sbb [bp + di + 5000], ah
sbb [bx], al
sbb sp, 392
sbb si, 5
sbb ax, 1000
sbb ah, 30
sbb al, 9
sbb cx, bx
sbb ch, al
dec ax
dec cx
dec dh
dec al
dec ah
dec sp
dec di
dec byte [bp + 1002]
dec word [bx + 39]
dec byte [bx + si + 5]
dec word [bp + di - 10044]
dec word [9349]
dec byte [bp]
neg ax
neg cx
neg dh
neg al
neg ah
neg sp
neg di
neg byte [bp + 1002]
neg word [bx + 39]
neg byte [bx + si + 5]
neg word [bp + di - 10044]
neg word [9349]
neg byte [bp]
cmp bx, cx
cmp dh, [bp + 390]
cmp [bp + 2], si
cmp bl, 20
cmp byte [bx], 34
cmp ax, 23909
aas
das
mul al
mul cx
mul word [bp]
mul byte [bx + di + 500]
imul ch
imul dx
imul byte [bx]
imul word [9483]
aam
div bl
div sp
div byte [bx + si + 2990]
div word [bp + di + 1000]
idiv ax
idiv si
idiv byte [bp + si]
idiv word [bx + 493]
aad
cbw
cwd
not ah
not bl
not sp
not si
not word [bp]
not byte [bp + 9905]
shl ah, 1
shr ax, 1
sar bx, 1
rol cx, 1
ror dh, 1
rcl sp, 1
rcr bp, 1
shl word [bp + 5], 1
shr byte [bx + si - 199], 1
sar byte [bx + di - 300], 1
rol word [bp], 1
ror word [4938], 1
rcl byte [3], 1
rcr word [bx], 1
shl ah, cl
shr ax, cl
sar bx, cl
rol cx, cl
ror dh, cl
rcl sp, cl
rcr bp, cl
shl word [bp + 5], cl
shr word [bx + si - 199], cl
sar byte [bx + di - 300], cl
rol byte [bp], cl
ror byte [4938], cl
rcl byte [3], cl
rcr word [bx], cl
and al, ah
and ch, cl
and bp, si
and di, sp
and al, 93
and ax, 20392
and [bp + si + 10], ch
and [bx + di + 1000], dx
and bx, [bp]
and cx, [4384]
and byte [bp - 39], 239
and word [bx + si - 4332], 10328
test bx, cx
test dh, [bp + 390]
test [bp + 2], si
test bl, 20
test byte [bx], 34
test ax, 23909
or al, ah
or ch, cl
or bp, si
or di, sp
or al, 93
or ax, 20392
or [bp + si + 10], ch
or [bx + di + 1000], dx
or bx, [bp]
or cx, [4384]
or byte [bp - 39], 239
or word [bx + si - 4332], 10328
xor al, ah
xor ch, cl
xor bp, si
xor di, sp
xor al, 93
xor ax, 20392
xor [bp + si + 10], ch
xor [bx + di + 1000], dx
xor bx, [bp]
xor cx, [4384]
xor byte [bp - 39], 239
xor word [bx + si - 4332], 10328
rep movsb
rep cmpsb
rep scasb
rep lodsb
rep movsw
rep cmpsw
rep scasw
rep lodsw
; NOTE(casey): Special thanks (as always!) to Mārtiņš Možeiko for figuring out why NASM
; wouldn't compile "rep stds" instructions. It was because it was a misprint in the 8086
; manual! It was really just "rep stos", which of course is still in x64, and NASM
; assembles it just fine.
rep stosb
rep stosw
call [39201]
call [bp - 100]
call sp
call ax
jmp ax
jmp di
jmp [12]
jmp [4395]
ret -7
ret 500
ret
label:
je label
jl label
jle label
jb label
jbe label
jp label
jo label
js label
jne label
jnl label
jg label
jnb label
ja label
jnp label
jno label
jns label
loop label
loopz label
loopnz label
jcxz label
int 13
int 3
into
iret
clc
cmc
stc
cld
std
cli
sti
hlt
wait
lock not byte [bp + 9905]
lock xchg [100], al
mov al, cs:[bx + si]
mov bx, ds:[bp + di]
mov dx, es:[bp]
mov ah, ss:[bx + si + 4]
and ss:[bp + si + 10], ch
or ds:[bx + di + 1000], dx
xor bx, es:[bp]
cmp cx, es:[4384]
test byte cs:[bp - 39], 239
sbb word cs:[bx + si - 4332], 10328
lock not byte CS:[bp + 9905]
;
; TODO(casey): I would like to uncomment this, but as far as I can tell, NASM doesn't recognize the ESC instruction :(
; so even if I just force the assembler to output the bits here, our disasm will fail to assemble because it will (correctly!)
; print the esc instruction and NASM will error because it doesn't know what that is.
;
; esc 938,ax
;
;
; TODO(casey): According to NASM, "rep movsb" is "not lockable". However the 8086 manual seems to think it is, and
; even describes what happens when you you lock a rep: the lock is held for the duration of the rep operation. So...
; yeah. Not sure why this doesn't work in NASM:
;
; lock rep movsb
;

View File

@ -76,6 +76,9 @@ typedef enum S86_InstructionType {
S86_InstructionType_MOVAccumToMem,
S86_InstructionType_MOVRegOrMemToSegReg,
S86_InstructionType_MOVSegRegToRegOrMem,
S86_InstructionType_ADDRegOrMemToOrFromReg,
S86_InstructionType_ADDImmediateToRegOrMem,
S86_InstructionType_ADDImmediateToAccum,
S86_InstructionType_Count,
} S86_InstructionType;
@ -128,6 +131,21 @@ S86_Instruction const S86_INSTRUCTIONS[S86_InstructionType_Count] = {
.op_bits0 = 0b1000'1100,
.op_mask1 = 0b0010'0000,
.op_bits1 = 0b0000'0000},
[S86_InstructionType_ADDRegOrMemToOrFromReg] = {.op_mask0 = 0b1111'1100,
.op_bits0 = 0b0000'0000,
.op_mask1 = 0b0000'0000,
.op_bits1 = 0b0000'0000},
[S86_InstructionType_ADDImmediateToRegOrMem] = {.op_mask0 = 0b1111'1100,
.op_bits0 = 0b1000'0000,
.op_mask1 = 0b0011'1000,
.op_bits1 = 0b0000'0000},
[S86_InstructionType_ADDImmediateToAccum] = {.op_mask0 = 0b1111'1110,
.op_bits0 = 0b0000'0100,
.op_mask1 = 0b0000'0000,
.op_bits1 = 0b0000'0000},
};
typedef struct S86_EffectiveAddressStr8 {
@ -135,7 +153,7 @@ typedef struct S86_EffectiveAddressStr8 {
size_t size;
} S86_EffectiveAddressStr8;
S86_EffectiveAddressStr8 S86_EffectiveAddressCalc(S86_BufferIterator *buffer_it, uint8_t rm, uint8_t mod);
S86_EffectiveAddressStr8 S86_EffectiveAddressCalc(S86_BufferIterator *buffer_it, uint8_t rm, uint8_t mod, uint8_t w);
// NOTE: Implementation
// ============================================================================
@ -282,7 +300,8 @@ void S86_PrintLnFmt(char const *fmt, ...)
va_end(args);
}
S86_EffectiveAddressStr8 S86_EffectiveAddressCalc(S86_BufferIterator *buffer_it, uint8_t rm, uint8_t mod)
S86_Str8 REGISTER_FIELD_ENCODING[2][8];
S86_EffectiveAddressStr8 S86_EffectiveAddressCalc(S86_BufferIterator *buffer_it, uint8_t rm, uint8_t mod, uint8_t w)
{
// NOTE: Calculate displacement
// =========================================================================
@ -295,12 +314,17 @@ S86_EffectiveAddressStr8 S86_EffectiveAddressCalc(S86_BufferIterator *buffer_it,
} else if (mod == 0b01) { // Mem mode 8 bit displacement
displacement = (int8_t)S86_BufferIteratorNextByte(buffer_it);
} else {
S86_ASSERT(mod == 0b00 /*Mem mode (no displacement)*/);
S86_ASSERT(mod == 0b00 || mod == 0b11 /*Mem mode (no displacement)*/);
}
S86_EffectiveAddressStr8 result = {0};
if (mod == 0b11) {
S86_Str8 register_field = REGISTER_FIELD_ENCODING[w][rm];
memcpy(result.data, register_field.data, register_field.size);
result.size = register_field.size;
} else {
// NOTE: Effective address calculation w/ displacement
// =========================================================================
S86_EffectiveAddressStr8 result = {0};
result.data[result.size++] = '[';
if (direct_address) {
result.size += snprintf(result.data + result.size,
@ -332,6 +356,7 @@ S86_EffectiveAddressStr8 S86_EffectiveAddressCalc(S86_BufferIterator *buffer_it,
}
}
result.data[result.size++] = ']';
}
S86_ASSERT(result.size < S86_ARRAY_UCOUNT(result.data));
return result;
@ -356,30 +381,23 @@ int main(int argc, char **argv)
// NOTE: Sim8086
// =========================================================================
// Mapping from a 'reg' encoding to the register name.
S86_Str8 const REGISTER_FIELD_ENCODING[2][8] = {
[0b0] =
{
S86_STR8("al"),
S86_STR8("cl"),
S86_STR8("dl"),
S86_STR8("bl"),
S86_STR8("ah"),
S86_STR8("ch"),
S86_STR8("dh"),
S86_STR8("bh"),
},
[0b1] =
{
S86_STR8("ax"),
S86_STR8("cx"),
S86_STR8("dx"),
S86_STR8("bx"),
S86_STR8("sp"),
S86_STR8("bp"),
S86_STR8("si"),
S86_STR8("di"),
},
};
REGISTER_FIELD_ENCODING[0b0][0] = S86_STR8("al");
REGISTER_FIELD_ENCODING[0b0][1] = S86_STR8("cl");
REGISTER_FIELD_ENCODING[0b0][2] = S86_STR8("dl");
REGISTER_FIELD_ENCODING[0b0][3] = S86_STR8("bl");
REGISTER_FIELD_ENCODING[0b0][4] = S86_STR8("ah");
REGISTER_FIELD_ENCODING[0b0][5] = S86_STR8("ch");
REGISTER_FIELD_ENCODING[0b0][6] = S86_STR8("dh");
REGISTER_FIELD_ENCODING[0b0][7] = S86_STR8("bh");
REGISTER_FIELD_ENCODING[0b1][0] = S86_STR8("ax");
REGISTER_FIELD_ENCODING[0b1][1] = S86_STR8("cx");
REGISTER_FIELD_ENCODING[0b1][2] = S86_STR8("dx");
REGISTER_FIELD_ENCODING[0b1][3] = S86_STR8("bx");
REGISTER_FIELD_ENCODING[0b1][4] = S86_STR8("sp");
REGISTER_FIELD_ENCODING[0b1][5] = S86_STR8("bp");
REGISTER_FIELD_ENCODING[0b1][6] = S86_STR8("si");
REGISTER_FIELD_ENCODING[0b1][7] = S86_STR8("di");
// NOTE: Decode assembly
// =========================================================================
@ -430,6 +448,7 @@ int main(int argc, char **argv)
switch (instruction_type) {
case S86_InstructionType_ADDRegOrMemToOrFromReg: /*FALLTHRU*/
case S86_InstructionType_MOVRegOrMemToOrFromReg: {
// NOTE: Instruction does not have opcode bits in the 2nd byte
S86_ASSERT(op_code_size == 1);
@ -446,55 +465,90 @@ int main(int argc, char **argv)
S86_ASSERT(reg < 8);
S86_ASSERT(rm < 8);
S86_Str8 op = {0};
if (instruction_type == S86_InstructionType_MOVRegOrMemToOrFromReg) {
op = S86_STR8("mov");
} else {
op = S86_STR8("add");
S86_ASSERT(instruction_type == S86_InstructionType_ADDRegOrMemToOrFromReg);
}
if (mod == 0b11) {
// NOTE: Register-to-register move
// =========================================================
S86_Str8 src_op = REGISTER_FIELD_ENCODING[w][d ? rm : reg];
S86_Str8 dest_op = REGISTER_FIELD_ENCODING[w][d ? reg : rm];
S86_PrintLnFmt("mov %.*s, %.*s", S86_STR8_FMT(dest_op), S86_STR8_FMT(src_op));
S86_PrintLnFmt("%.*s %.*s, %.*s", S86_STR8_FMT(op), S86_STR8_FMT(dest_op), S86_STR8_FMT(src_op));
} else {
// NOTE: Memory mode w/ effective address calculation
// =========================================================
S86_EffectiveAddressStr8 effective_address = S86_EffectiveAddressCalc(&buffer_it, rm, mod);
S86_EffectiveAddressStr8 effective_address = S86_EffectiveAddressCalc(&buffer_it, rm, mod, w);
S86_Str8 addr = { .data = effective_address.data, .size = effective_address.size };
S86_Str8 dest_op = d ? REGISTER_FIELD_ENCODING[w][reg] : addr;
S86_Str8 src_op = d ? addr : REGISTER_FIELD_ENCODING[w][reg];
S86_PrintLnFmt("mov %.*s, %.*s", S86_STR8_FMT(dest_op), S86_STR8_FMT(src_op));
S86_PrintLnFmt("%.*s %.*s, %.*s", S86_STR8_FMT(op), S86_STR8_FMT(dest_op), S86_STR8_FMT(src_op));
}
} break;
case S86_InstructionType_ADDImmediateToRegOrMem: /*FALLTHRU*/
case S86_InstructionType_MOVImmediateToRegOrMem: {
S86_ASSERT(op_code_size == 2);
uint8_t w = (op_code_bytes[0] & 0b0000'0001) >> 0;
uint8_t s = (op_code_bytes[0] & 0b0000'0010) >> 1;
uint8_t mod = (op_code_bytes[1] & 0b1100'0000) >> 6;
uint8_t rm = (op_code_bytes[1] & 0b0000'0111) >> 0;
S86_ASSERT(w < 2);
S86_ASSERT(mod < 4);
S86_ASSERT(rm < 8);
S86_ASSERT(mod != 0b11); // NOTE: Op is IMM->Reg, register-to-register not permitted
// NOTE: Memory mode w/ effective address calculation
// =========================================================
S86_EffectiveAddressStr8 effective_address = S86_EffectiveAddressCalc(&buffer_it, rm, mod);
S86_EffectiveAddressStr8 effective_address = S86_EffectiveAddressCalc(&buffer_it, rm, mod, w);
// NOTE: Parse data payload
// =============================================================
uint16_t data = S86_BufferIteratorNextByte(&buffer_it);
bool sign_extend_8bit_data = false;
if (w) { // 16 bit data
if (instruction_type == S86_InstructionType_ADDImmediateToRegOrMem && s) {
sign_extend_8bit_data = true;
} else {
uint8_t data_hi = S86_BufferIteratorNextByte(&buffer_it);
data |= (uint16_t)(data_hi) << 8;
}
}
S86_Str8 op = {0};
if (instruction_type == S86_InstructionType_MOVImmediateToRegOrMem) {
S86_ASSERT(mod != 0b11); // NOTE: Op is IMM->Reg, register-to-register not permitted
op = S86_STR8("mov");
} else {
S86_ASSERT(instruction_type == S86_InstructionType_ADDImmediateToRegOrMem);
op = S86_STR8("add");
}
// NOTE: Disassemble
// =========================================================
S86_PrintLnFmt("mov %.*s, %s %u", effective_address.size, effective_address.data, w ? "word" : "byte", data);
if (instruction_type == S86_InstructionType_MOVImmediateToRegOrMem) {
S86_PrintLnFmt("%.*s %.*s, %s %u", S86_STR8_FMT(op), effective_address.size, effective_address.data, w ? "word" : "byte", data);
} else {
if (sign_extend_8bit_data) {
S86_PrintLnFmt("%.*s %.*s, %d", S86_STR8_FMT(op), effective_address.size, effective_address.data, (int16_t)data);
} else {
S86_PrintLnFmt("%.*s %.*s, %u", S86_STR8_FMT(op), effective_address.size, effective_address.data, data);
}
}
} break;
case S86_InstructionType_ADDImmediateToAccum: /*FALLTHRU*/
case S86_InstructionType_MOVImmediateToReg: {
// NOTE: Parse opcode control bits
// =============================================================
S86_ASSERT(op_code_size == 1);
uint8_t w = (op_code_bytes[0] & 0b0000'1000) >> 3;
uint8_t w = 0;
if (instruction_type == S86_InstructionType_ADDImmediateToAccum) {
w = (op_code_bytes[0] & 0b0000'0001) >> 0;
} else {
w = (op_code_bytes[0] & 0b0000'1000) >> 3;
}
uint8_t reg = (op_code_bytes[0] & 0b0000'0111) >> 0;
// NOTE: Parse data payload
@ -507,8 +561,23 @@ int main(int argc, char **argv)
// NOTE: Disassemble
// =============================================================
S86_Str8 dest_register = REGISTER_FIELD_ENCODING[w][reg];
S86_PrintLnFmt("mov %.*s, %d", S86_STR8_FMT(dest_register), (int16_t)data);
S86_Str8 op = {0};
S86_Str8 dest_register = {0};
if (instruction_type == S86_InstructionType_MOVImmediateToReg) {
op = S86_STR8("mov");
dest_register = REGISTER_FIELD_ENCODING[w][reg];
} else {
S86_ASSERT(instruction_type == S86_InstructionType_ADDImmediateToAccum);
op = S86_STR8("add");
if (w) {
dest_register = S86_STR8("ax");
} else {
data = (uint16_t)(int8_t)data; // Sign extension
dest_register = S86_STR8("al");
}
}
S86_PrintLnFmt("%.*s %.*s, %d", S86_STR8_FMT(op), S86_STR8_FMT(dest_register), (int16_t)data);
} break;
case S86_InstructionType_MOVAccumToMem: /*FALLTHRU*/

Binary file not shown.