6088 - start using setCC instructions

This commit is contained in:
Kartik Agaram 2020-03-06 17:41:36 -08:00
parent 0743b981a8
commit 5c26afb1de
10 changed files with 168 additions and 26 deletions

View File

@ -105,7 +105,7 @@ void test_jne_disp8_success() {
}
:(before "End Single-Byte Opcodes")
case 0x75: { // jump disp8 unless ZF
case 0x75: { // jump disp8 if !ZF
const int8_t offset = static_cast<int>(next());
if (!ZF) {
trace(Callstack_depth+1, "run") << "jump " << NUM(offset) << end();

View File

@ -105,7 +105,7 @@ void test_jne_disp32_success() {
}
:(before "End Two-Byte Opcodes Starting With 0f")
case 0x85: { // jump disp32 unless ZF
case 0x85: { // jump disp32 if !ZF
const int32_t offset = next32();
if (!ZF) {
trace(Callstack_depth+1, "run") << "jump " << offset << end();

View File

@ -66,7 +66,7 @@ case 0x88: { // copy r8 to r/m8
const uint8_t rsrc = (modrm>>3)&0x7;
trace(Callstack_depth+1, "run") << "copy " << rname_8bit(rsrc) << " to r8/m8-at-r32" << end();
// use unsigned to zero-extend 8-bit value to 32 bits
uint8_t* dest = reinterpret_cast<uint8_t*>(effective_byte_address(modrm));
uint8_t* dest = effective_byte_address(modrm);
const uint8_t* src = reg_8bit(rsrc);
*dest = *src; // Read/write multiple elements of vector<uint8_t> at once. Assumes sizeof(int) == 4 on the host as well.
trace(Callstack_depth+1, "run") << "storing 0x" << HEXBYTE << NUM(*dest) << end();
@ -105,7 +105,7 @@ case 0x8a: { // copy r/m8 to r8
const uint8_t rdest = (modrm>>3)&0x7;
trace(Callstack_depth+1, "run") << "copy r8/m8-at-r32 to " << rname_8bit(rdest) << end();
// use unsigned to zero-extend 8-bit value to 32 bits
const uint8_t* src = reinterpret_cast<uint8_t*>(effective_byte_address(modrm));
const uint8_t* src = effective_byte_address(modrm);
uint8_t* dest = reg_8bit(rdest);
trace(Callstack_depth+1, "run") << "storing 0x" << HEXBYTE << NUM(*src) << end();
*dest = *src; // Read/write multiple elements of vector<uint8_t> at once. Assumes sizeof(int) == 4 on the host as well.
@ -169,8 +169,104 @@ case 0xc6: { // copy imm8 to r/m8
exit(1);
}
// use unsigned to zero-extend 8-bit value to 32 bits
uint8_t* dest = reinterpret_cast<uint8_t*>(effective_byte_address(modrm));
uint8_t* dest = effective_byte_address(modrm);
*dest = src; // Write multiple elements of vector<uint8_t> at once. Assumes sizeof(int) == 4 on the host as well.
trace(Callstack_depth+1, "run") << "storing 0x" << HEXBYTE << NUM(*dest) << end();
break;
}
//:: set flags (setcc)
:(before "End Initialize Op Names")
put_new(Name_0f, "94", "set r8/m8-at-rm32 to 1 if equal, if ZF is set, 0 otherwise (setcc/setz/sete)");
put_new(Name_0f, "95", "set r8/m8-at-rm32 to 1 if not equal, if ZF is not set, 0 otherwise (setcc/setnz/setne)");
put_new(Name_0f, "9f", "set r8/m8-at-rm32 to 1 if greater (signed), if ZF is unset and SF == OF, 0 otherwise (setcc/setg/setnle)");
put_new(Name_0f, "97", "set r8/m8-at-rm32 to 1 if greater (unsigned), if ZF is unset and CF is unset, 0 otherwise (setcc/seta/setnbe)");
put_new(Name_0f, "9d", "set r8/m8-at-rm32 to 1 if greater or equal (signed), if SF == OF, 0 otherwise (setcc/setge/setnl)");
put_new(Name_0f, "93", "set r8/m8-at-rm32 to 1 if greater or equal (unsigned), if CF is unset, 0 otherwise (setcc/setae/setnb)");
put_new(Name_0f, "9c", "set r8/m8-at-rm32 to 1 if lesser (signed), if SF != OF, 0 otherwise (setcc/setl/setnge)");
put_new(Name_0f, "92", "set r8/m8-at-rm32 to 1 if lesser (unsigned), if CF is set, 0 otherwise (setcc/setb/setnae)");
put_new(Name_0f, "9e", "set r8/m8-at-rm32 to 1 if lesser or equal (signed), if ZF is set or SF != OF, 0 otherwise (setcc/setle/setng)");
put_new(Name_0f, "96", "set r8/m8-at-rm32 to 1 if lesser or equal (unsigned), if ZF is set or CF is set, 0 otherwise (setcc/setbe/setna)");
:(before "End Two-Byte Opcodes Starting With 0f")
case 0x94: { // set r8/m8-at-rm32 if ZF
const uint8_t modrm = next();
trace(Callstack_depth+1, "run") << "set r8/m8-at-rm32" << end();
uint8_t* dest = effective_byte_address(modrm);
*dest = ZF;
trace(Callstack_depth+1, "run") << "storing " << *dest << end();
break;
}
case 0x95: { // set r8/m8-at-rm32 if !ZF
const uint8_t modrm = next();
trace(Callstack_depth+1, "run") << "set r8/m8-at-rm32" << end();
uint8_t* dest = effective_byte_address(modrm);
*dest = !ZF;
trace(Callstack_depth+1, "run") << "storing " << *dest << end();
break;
}
case 0x9f: { // set r8/m8-at-rm32 if !SF and !ZF
const uint8_t modrm = next();
trace(Callstack_depth+1, "run") << "set r8/m8-at-rm32" << end();
uint8_t* dest = effective_byte_address(modrm);
*dest = !ZF && SF == OF;
trace(Callstack_depth+1, "run") << "storing " << *dest << end();
break;
}
case 0x97: { // set r8/m8-at-rm32 if !CF and !ZF
const uint8_t modrm = next();
trace(Callstack_depth+1, "run") << "set r8/m8-at-rm32" << end();
uint8_t* dest = effective_byte_address(modrm);
*dest = (!CF && !ZF);
trace(Callstack_depth+1, "run") << "storing " << *dest << end();
break;
}
case 0x9d: { // set r8/m8-at-rm32 if !SF
const uint8_t modrm = next();
trace(Callstack_depth+1, "run") << "set r8/m8-at-rm32" << end();
uint8_t* dest = effective_byte_address(modrm);
*dest = (SF == OF);
trace(Callstack_depth+1, "run") << "storing " << *dest << end();
break;
}
case 0x93: { // set r8/m8-at-rm32 if !CF
const uint8_t modrm = next();
trace(Callstack_depth+1, "run") << "set r8/m8-at-rm32" << end();
uint8_t* dest = effective_byte_address(modrm);
*dest = !CF;
trace(Callstack_depth+1, "run") << "storing " << *dest << end();
break;
}
case 0x9c: { // set r8/m8-at-rm32 if SF and !ZF
const uint8_t modrm = next();
trace(Callstack_depth+1, "run") << "set r8/m8-at-rm32" << end();
uint8_t* dest = effective_byte_address(modrm);
*dest = (SF != OF);
trace(Callstack_depth+1, "run") << "storing " << *dest << end();
break;
}
case 0x92: { // set r8/m8-at-rm32 if CF
const uint8_t modrm = next();
trace(Callstack_depth+1, "run") << "set r8/m8-at-rm32" << end();
uint8_t* dest = effective_byte_address(modrm);
*dest = CF;
trace(Callstack_depth+1, "run") << "storing " << *dest << end();
break;
}
case 0x9e: { // set r8/m8-at-rm32 if SF or ZF
const uint8_t modrm = next();
trace(Callstack_depth+1, "run") << "set r8/m8-at-rm32" << end();
uint8_t* dest = effective_byte_address(modrm);
*dest = (ZF || SF != OF);
trace(Callstack_depth+1, "run") << "storing " << *dest << end();
break;
}
case 0x96: { // set r8/m8-at-rm32 if ZF or CF
const uint8_t modrm = next();
trace(Callstack_depth+1, "run") << "set r8/m8-at-rm32" << end();
uint8_t* dest = effective_byte_address(modrm);
*dest = (ZF || CF);
trace(Callstack_depth+1, "run") << "storing " << *dest << end();
break;
}

View File

@ -649,6 +649,17 @@ put_new(Permitted_operands_0f, "8f", 0x10);
// imm32 imm8 disp32 |disp16 disp8 subop modrm
// 0 0 0 |0 0 0 1
put_new(Permitted_operands_0f, "af", 0x01);
// setcc
put_new(Permitted_operands_0f, "92", 0x01);
put_new(Permitted_operands_0f, "93", 0x01);
put_new(Permitted_operands_0f, "94", 0x01);
put_new(Permitted_operands_0f, "95", 0x01);
put_new(Permitted_operands_0f, "96", 0x01);
put_new(Permitted_operands_0f, "97", 0x01);
put_new(Permitted_operands_0f, "9c", 0x01);
put_new(Permitted_operands_0f, "9d", 0x01);
put_new(Permitted_operands_0f, "9e", 0x01);
put_new(Permitted_operands_0f, "9f", 0x01);
:(code)
void check_operands_0f(const line& inst, const word& op) {

BIN
apps/ex13 Executable file

Binary file not shown.

25
apps/ex13.subx Normal file
View File

@ -0,0 +1,25 @@
# Compare 3 and 3.
#
# To run:
# $ ./subx translate init.linux examples/ex13.subx -o examples/ex13
# $ ./subx run examples/ex13
# Expected result:
# $ echo $?
# 1
== code
# instruction effective address register displacement immediate
# . op subop mod rm32 base index scale r32
# . 1-3 bytes 3 bits 2 bits 3 bits 3 bits 3 bits 2 bits 2 bits 0/1/2/4 bytes 0/1/2/4 bytes
Entry:
b8/copy-to-eax 3/imm32
3d/compare-eax-and 3/imm32
0f 94/set-if-= 3/mod/direct 3/rm32/ebx . . . . . . # set ebx to ZF
81 4/subop/and 3/mod/direct 3/rm32/ebx . . . . . 0xff/imm32 # AND eax with 0xff
$exit:
# exit(ebx)
e8/call syscall_exit/disp32
# . . vim:nowrap:textwidth=0

BIN
apps/mu

Binary file not shown.

View File

@ -6867,14 +6867,10 @@ power-of-2?: # n: int -> result/eax: boolean
48/decrement-eax
# var tmp2/eax: int = n & tmp
0b/and-> *(ebp+8) 0/r32/eax
# return (tmp2 == 0) # TODO: replace with setcc
# return (tmp2 == 0)
3d/compare-eax-and 0/imm32
74/jump-if-= $power-of-2?:true/disp8
$power-of-2?:false:
b8/copy-to-eax 0/imm32/false
eb/jump $power-of-2?:end/disp8
$power-of-2?:true:
b8/copy-to-eax 1/imm32/true
0f 94/set-if-= %eax
81 4/subop/and %eax 0xff/imm32
$power-of-2?:end:
# . epilogue
89/<- %esp 5/r32/ebp
@ -9067,12 +9063,8 @@ subx-type-equal?: # a: (handle tree type-id), b: (handle tree type-id) -> resul
(is-literal-type? *(ebp+0xc)) # => eax
# return alit == blit
39/compare %eax 1/r32/ecx
74/jump-if-= $subx-type-equal?:true/disp8
$subx-type-equal?:false: # TODO: replace with setcc
b8/copy-to-eax 0/imm32/false
eb/jump $subx-type-equal?:end/disp8
$subx-type-equal?:true:
b8/copy-to-eax 1/imm32/true
0f 94/set-if-= %eax
81 4/subop/and %eax 0xff/imm32
$subx-type-equal?:end:
# . restore registers
59/pop-to-ecx
@ -9087,14 +9079,10 @@ is-literal-type?: # a: (handle tree type-id) -> result/eax: boolean
89/<- %ebp 4/r32/esp
#
8b/-> *(ebp+8) 0/r32/eax
# return (*eax == 0) # TODO: replace with setcc
# return (*eax == 0)
81 7/subop/compare *eax 0/imm32/literal-type-id # Atom-type
75/jump-if-!= $is-literal-type?:false/disp8
$is-literal-type?:true:
b8/copy-to-eax 1/imm32/true
eb/jump $is-literal-type?:end/disp8
$is-literal-type?:false:
b8/copy-to-eax 0/imm32/false
0f 94/set-if-= %eax
81 4/subop/and %eax 0xff/imm32
$is-literal-type?:end:
# . epilogue
89/<- %esp 5/r32/ebp

View File

@ -80,7 +80,7 @@ Opcodes currently supported by SubX:
c1: shift rm32 by imm8 bits depending on subop (sal/sar/shl/shr)
c3: return from most recent unfinished call (ret)
c6: copy imm8 to r8/m8-at-r32 (mov)
c7: copy imm32 to rm32 (mov)
c7: copy imm32 to rm32 with subop 0 (mov)
cd: software interrupt (int)
d3: shift rm32 by CL bits depending on subop (sal/sar/shl/shr)
e8: call disp32 (call)
@ -99,6 +99,16 @@ Opcodes currently supported by SubX:
0f 8d: jump disp32 bytes away if greater or equal (signed), if SF == OF (jcc/jge/jnl)
0f 8e: jump disp32 bytes away if lesser or equal (signed), if ZF is set or SF != OF (jcc/jle/jng)
0f 8f: jump disp32 bytes away if greater (signed), if ZF is unset and SF == OF (jcc/jg/jnle)
0f 92: set rm32 to 1 if lesser (unsigned), if CF is set, 0 otherwise (setcc/setb/setnae)
0f 93: set rm32 to 1 if greater or equal (unsigned), if CF is unset, 0 otherwise (setcc/setae/setnb)
0f 94: set rm32 to 1 if equal, if ZF is set, 0 otherwise (setcc/setz/sete)
0f 95: set rm32 to 1 if not equal, if ZF is not set, 0 otherwise (setcc/setnz/setne)
0f 96: set rm32 to 1 if lesser or equal (unsigned), if ZF is set or CF is set, 0 otherwise (setcc/setbe/setna)
0f 97: set rm32 to 1 if greater (unsigned), if ZF is unset and CF is unset, 0 otherwise (setcc/seta/setnbe)
0f 9c: set rm32 to 1 if lesser (signed), if SF != OF, 0 otherwise (setcc/setl/setnge)
0f 9d: set rm32 to 1 if greater or equal (signed), if SF == OF, 0 otherwise (setcc/setge/setnl)
0f 9e: set rm32 to 1 if lesser or equal (signed), if ZF is set or SF != OF, 0 otherwise (setcc/setle/setng)
0f 9f: set rm32 to 1 if greater (signed), if ZF is unset and SF == OF, 0 otherwise (setcc/setg/setnle)
0f af: multiply rm32 into r32 (imul)
Run `bootstrap help instructions` for details on words like 'r32' and 'disp8'.
For complete details on these instructions, consult the IA-32 manual (volume 2).

View File

@ -167,6 +167,18 @@ test "$1" = 'record' || git diff --exit-code apps/ex12
test $EMULATED && ./bootstrap run apps/ex12 # final byte of mmap'd address is well-nigh guaranteed to be 0
test $NATIVE && apps/ex12
echo ex13
./bootstrap translate init.$OS apps/ex13.subx -o apps/ex13
test "$1" = 'record' || git diff --exit-code apps/ex13
test $EMULATED && {
./bootstrap run apps/ex13 || ret=$?
test $ret -eq 1 # 3 == 3
}
test $NATIVE && {
apps/ex13 || ret=$?
test $ret -eq 1 # 3 == 3
}
# Larger apps that use the standard library.
echo factorial