├── qbx.png ├── README.md ├── qbx_registers.asm ├── qbx_insn_helpers.asm ├── tests └── marquee2.qasm ├── qbx_assembler.asm ├── win64_helpers.asm ├── qbx_insn_list.asm └── qbx.asm /qbx.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nicebyte/qbxvm/HEAD/qbx.png -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![qbx Logo](/qbx.png) 2 | --- 3 | QBX (QBX Bytecode Xecutor) is an emulator for an imaginary instruction set, implemented from scratch in x86-64 assembly as an educational exercise. 4 | -------------------------------------------------------------------------------- /qbx_registers.asm: -------------------------------------------------------------------------------- 1 | ; QBX <--> x64 REGISTER MAPPING 2 | 3 | ; general-purpose registers 4 | q0 equ r12w 5 | q1 equ r13w 6 | q2 equ r14w 7 | q3 equ r15w 8 | q0w equ r12w 9 | q1w equ r13w 10 | q2w equ r14w 11 | q3w equ r15w 12 | qaddr equ r12 13 | QBX_NUM_REGISTERS equ 4 14 | 15 | q0b equ r12b 16 | q1b equ r13b 17 | q2b equ r14b 18 | q3b equ r15b 19 | 20 | ; stack ptr 21 | qsp equ rbx 22 | qspw equ bx 23 | qspb equ bl 24 | 25 | ; insn ptr 26 | qip equ rsi 27 | qipw equ si 28 | 29 | ; flags 30 | qflags equ r11 31 | qflagsw equ r11w -------------------------------------------------------------------------------- /qbx_insn_helpers.asm: -------------------------------------------------------------------------------- 1 | ; define constants for each instruction code. 2 | macro define_icodes base, [insn*] { 3 | common 4 | local next_icode 5 | next_icode = base 6 | forward 7 | insn = next_icode 8 | next_icode = next_icode + 1 9 | } 10 | 11 | ; define a jump table mapping instruction codes to implementations. 12 | macro define_jmp_table jmp_table_name*, [op*] { 13 | common 14 | jmp_table_name: 15 | forward 16 | ; IMPL_* labels are defined by the `insn` macro. 17 | dw IMPL_#op 18 | } 19 | 20 | ; marks the beginning of the QBX instruction implementation. 21 | macro insn name { 22 | IMPL_#name = $ - insn_base 23 | update_qbx_flags = 0 24 | } 25 | 26 | ; marks the end of a QBX instruction implementation. 27 | macro endinsn { 28 | if update_qbx_flags 29 | jmp update_flags_advance 30 | else 31 | jmp advance 32 | end if 33 | } -------------------------------------------------------------------------------- /tests/marquee2.qasm: -------------------------------------------------------------------------------- 1 | include '../qbx_assembler.asm' 2 | 3 | qj u, start ; jump to entrypoint 4 | 5 | ; constants and variables 6 | SCR_W_CHARS equ 80 7 | CHAR_SIZE_BYTES equ 4 8 | SCR_W_BYTES equ SCR_W_CHARS*CHAR_SIZE_BYTES 9 | msg db "MARQUEE!!!" 10 | MSG_LEN = $ - msg 11 | db (80 - MSG_LEN) dup '-' 12 | CHAR_ATTRS equ 0x001e 13 | 14 | start: ; execution starts here 15 | qxor w, q2, q2 16 | step_msg: 17 | qmov w, q3, screen_content 18 | fill_line_loop: 19 | ; loop must be terminated if we've reached the end of 1st line. 20 | qmov w, q0, SCR_W_BYTES 21 | qmov w, q1, screen_content 22 | qadd w, q0, q1 23 | qsub w, q0, q3 24 | qj z, loop_end 25 | ; get the index of char within the line into q1. 26 | qmov w, q1, q3 27 | qmov w, q0, screen_content 28 | qsub w, q1, q0 29 | qmov w, q0, 2 30 | qshr w, q1, q0 31 | ; add position in line to position of msg start 32 | qadd w, q1, q2 33 | ; get modulo 80 34 | qpush w, q2 35 | qmov w, q2, SCR_W_CHARS 36 | qmov w, q0, q1 37 | qxor w, q1, q1 38 | qsdiv w, q2 39 | qpop w, q2 40 | qmov w, q0, q1 41 | qmov w, q1, msg 42 | qadd w, q0, q1 ; compute address of byte in the message. 43 | qxor w, q1, q1 44 | qload b, [q0], q1 ; load char code from message into q1. 45 | qmov w, q0, q3 46 | qstor w, [q0], q1 ; store char code into screen memory. 47 | qmov w, q0, CHAR_SIZE_BYTES/2 48 | qadd w, q3, q0 ; compute address of attrs. 49 | qmov w, q0, q3 50 | qmov w, q1, CHAR_ATTRS 51 | qstor w, [q0], q1 ; set attrs. 52 | ; advance loop counter 53 | qmov w, q0, CHAR_SIZE_BYTES/2 54 | qadd w, q3, q0 55 | qj u, fill_line_loop 56 | loop_end: 57 | ; trigger dpy refresh 58 | qmov b, q1, 80 59 | qmov w, q0, 8 60 | qshl w, q1, q0 61 | qmov b, q1, 1 62 | qpush w, q2 63 | qmov w, q2, 0 64 | qmov w, q0, screen_content 65 | qyld 0x01 66 | qpop w, q2 67 | ; animation delay 68 | qmov w, q0, 100 69 | qyld 0x02 70 | ; advance msg forward 71 | qmov w, q0, 1 72 | qadd w, q2, q0 73 | qj u, step_msg 74 | 75 | screen_content: 76 | -------------------------------------------------------------------------------- /qbx_assembler.asm: -------------------------------------------------------------------------------- 1 | include 'qbx_insn_helpers.asm' 2 | include 'qbx_insn_list.asm' 3 | 4 | qbx_insns define_icodes, 0 5 | 6 | macro qnop { dw noop } 7 | macro qhalt { dw halt } 8 | 9 | macro _check_reg r { 10 | if ~((r eq q0)|(r eq q1)|(r eq q2)|(r eq q3)) 11 | err "invalid register", r 12 | end if 13 | } 14 | 15 | macro _check_opsize opsize { 16 | if ~(opsize eq b) & ~(opsize eq w) 17 | err "invalid operand size" 18 | end if 19 | } 20 | 21 | macro _mem_insn name* { 22 | macro q#name opsize*, addr*, r* \{ 23 | _check_opsize opsize 24 | _check_reg r 25 | local matched 26 | define matched 0 27 | match =0 [target], matched addr \\{ 28 | if target eq q0 29 | dw (#name\#opsize\#i\#r) 30 | else if ((target eq q1)|(target eq q2)|(target eq q3)) 31 | err "addr for indirect memop must be held in q0" 32 | else 33 | dw (#name\#opsize\#d\#r) 34 | dw target 35 | end if 36 | define matched 1 37 | \\} 38 | match =0 any, matched addr 39 | \\{ 40 | err "invalid address. must be [q0] or imm. value." 41 | \\} 42 | \} 43 | } 44 | 45 | _mem_insn stor 46 | _mem_insn load 47 | 48 | macro qmov opsize*, r1*, r2* { 49 | _check_opsize opsize 50 | _check_reg r1 51 | 52 | if ((r2 eq q0)|(r2 eq q1)|(r2 eq q2)|(r2 eq q3)) 53 | dw mov#opsize#r1#r2 54 | else 55 | dw movi#opsize#r1 56 | if opsize eq b 57 | db r2 58 | else if opsize eq w 59 | dw r2 60 | else 61 | err 62 | end if 63 | end if 64 | } 65 | 66 | macro _binop_alu_insn name { 67 | macro q#name opsize*, r1*, r2* \{ 68 | _check_reg (#r1) 69 | _check_reg (#r2) 70 | _check_opsize (#opsize) 71 | dw name\#opsize\#r1\#r2 72 | \} 73 | } 74 | 75 | _binop_alu_insn add 76 | _binop_alu_insn sub 77 | _binop_alu_insn and 78 | _binop_alu_insn or 79 | _binop_alu_insn xor 80 | _binop_alu_insn shr 81 | _binop_alu_insn shl 82 | _binop_alu_insn mul 83 | _binop_alu_insn smul 84 | 85 | macro _unop_alu_insn name { 86 | macro q#name opsize*, r* \{ 87 | if ((name eq div)|(name eq sdiv)) & \ 88 | (r eq q0) 89 | err "invalid instruction" 90 | end if 91 | _check_reg (#r) 92 | _check_opsize (#opsize) 93 | dw name\#opsize\#r 94 | \} 95 | } 96 | 97 | _unop_alu_insn div 98 | _unop_alu_insn sdiv 99 | _unop_alu_insn inv 100 | _unop_alu_insn push 101 | _unop_alu_insn pop 102 | 103 | macro qsleep r* { 104 | _check_reg r 105 | dw sleep#r 106 | } 107 | 108 | macro qj cond*, target* { 109 | if (target eq q0) 110 | dw j#cond#i 111 | else if ((target eq q1)|(target eq q2)|(target eq q3)) 112 | err "indirect jump or call address must be held in q0" 113 | else 114 | dw j#cond#d 115 | dw target 116 | end if 117 | } 118 | 119 | macro qret { dw return } 120 | 121 | macro qyld code { 122 | dw yld 123 | dw code 124 | } 125 | 126 | format binary -------------------------------------------------------------------------------- /win64_helpers.asm: -------------------------------------------------------------------------------- 1 | ; Gegenerates IDT for the listed libs. 2 | macro import_directory_table [lib*] { 3 | forward 4 | dd rva IAT__#lib 5 | dd 0 6 | dd 0 7 | dd rva NAME__#lib 8 | dd rva IAT__#lib 9 | 10 | common 11 | dd 5 dup(0) 12 | 13 | forward 14 | NAME__#lib db `lib, ".DLL", 0 15 | } 16 | 17 | ; Generates IAT for the functions from a given lib. 18 | macro import_functions libname, [funcnames*] { 19 | forward 20 | if $ & 1 21 | db 0 22 | end if 23 | IMPORTNAME__#funcnames dw 0 24 | db `funcnames, 0 25 | common 26 | IAT__#libname: 27 | forward 28 | funcnames dq rva IMPORTNAME__#funcnames 29 | 30 | common 31 | dq 0 32 | } 33 | 34 | ; Helper macro, puts parameter into a registers (if needed). 35 | macro call64_putreg param*, reg* 36 | { 37 | if ~ (reg eqtype rax) 38 | display "target must be a register" 39 | err 40 | end if 41 | if ~ param eq reg 42 | mov reg, param 43 | end if 44 | } 45 | 46 | ; (Partial) implementation of the Win64 calling convention 47 | macro call64 fn*, [arg] 48 | { 49 | common 50 | ; The `local' directive declares the following names 51 | ; a "local" to the macro - this is done so that each 52 | ; macro invocation gets its very own instance of those variables. 53 | local nargs, arg_idx, stack_space 54 | 55 | ; nargs is the number of arguments passed to the function. 56 | ; note that below we're simply forward-referencing nargs and relying 57 | ; on fasm to infer the actual value (see the section above on forward-referencing). 58 | 59 | ; align the stack on 16-byte boundary, and reserve space for arguments. 60 | ; we make the assumption that at the time of macro invocation, the stack 61 | ; is "16+8"-aligned (due to the return address having been pushed onto it 62 | ; by the current function's caller). 63 | if nargs <= 4 64 | ; even when the number of arguments is less than 4, 65 | ; the calling convention mandates that we reserve the 66 | ; so-called "shadow space" on the stack for 4 parameters. 67 | stack_space = 5 * 8 ; subtracting 40 from rsp will make it 16-byte aligned 68 | else if nargs & 1 69 | ; if we have an odd number of arguments, reserve just enough space for them, 70 | ; and the stack will become 16-byte aligned: 71 | ; rsp_old = 16 * K - 8 72 | ; rsp_new = 16 * K - 8 - 8 * (2 * Q + 1) = 16 * K - 16 * Q - 16 = 16 * (K - Q - 1) 73 | stack_space = nargs * 8 74 | else 75 | ; if we have an even number of arguments, we need 8 more bytes of padding to 76 | ; achieve alignment. 77 | stack_space = (nargs + 1) * 8 78 | end if 79 | 80 | if stack_space 81 | ; allocate space on the stack. 82 | sub rsp, stack_space 83 | end if 84 | 85 | arg_idx = 0 86 | 87 | forward 88 | match ,arg 89 | \{ 90 | ; this matches an empty argument list. 91 | ; unfortunately, when no variadic arugments are provided at the macro 92 | ; invocation site, the forward blocks are still processed once (with all groupargs empty). 93 | \} 94 | match any,arg 95 | \{ 96 | ; pass the first 4 arguments in registers and the rest on the stack. 97 | arg_idx = arg_idx + 1 98 | if arg_idx = 1 99 | call64_putreg arg, rcx 100 | else if arg_idx = 2 101 | call64_putreg arg, rdx 102 | else if arg_idx = 3 103 | call64_putreg arg, r8 104 | else if arg_idx = 4 105 | call64_putreg arg, r9 106 | else 107 | mov qword [rsp + (arg_idx-1)*8], arg 108 | end if 109 | 110 | \} 111 | common 112 | ; set value of the nargs assembly-time variable (and fasm will magically know to use 113 | ; this value up above...) 114 | nargs = arg_idx 115 | 116 | ; perform the call 117 | call fn 118 | 119 | ; clean up the stack as required by the calling convention 120 | if stack_space 121 | add rsp, stack_space 122 | end if 123 | } 124 | 125 | ; WIN32 API constants 126 | 127 | MB_ICONERROR equ 0x00000010 128 | GENERIC_READ equ 0x80000000 129 | GENERIC_WRITE equ 0x40000000 130 | OPEN_EXISTING equ 3 131 | CREATE_ALWAYS equ 2 132 | INVALID_HANDLE equ -1 133 | STD_OUTPUT_HANDLE equ -11 134 | STD_INPUT_HANDLE equ -10 135 | 136 | 137 | ; Some WIN32 API structures 138 | 139 | struc SMALL_RECT { 140 | .Left dw ? 141 | .Top dw ? 142 | .Right dw ? 143 | .Bottom dw ? 144 | } -------------------------------------------------------------------------------- /qbx_insn_list.asm: -------------------------------------------------------------------------------- 1 | macro qbx_insns m, [arg*] { 2 | common 3 | m arg, \ 4 | noop, \ 5 | halt, \ 6 | moviwq0, \ 7 | moviwq1, \ 8 | moviwq2, \ 9 | moviwq3, \ 10 | movibq0, \ 11 | movibq1, \ 12 | movibq2, \ 13 | movibq3, \ 14 | movwq0q1, \ 15 | movwq0q2, \ 16 | movwq0q3, \ 17 | movwq1q0, \ 18 | movwq1q2, \ 19 | movwq1q3, \ 20 | movwq2q0, \ 21 | movwq2q1, \ 22 | movwq2q3, \ 23 | movwq3q0, \ 24 | movwq3q1, \ 25 | movwq3q2, \ 26 | movbq0q1, \ 27 | movbq0q2, \ 28 | movbq0q3, \ 29 | movbq1q0, \ 30 | movbq1q2, \ 31 | movbq1q3, \ 32 | movbq2q0, \ 33 | movbq2q1, \ 34 | movbq2q3, \ 35 | movbq3q0, \ 36 | movbq3q1, \ 37 | movbq3q2, \ 38 | storbdq0, \ 39 | storbdq1, \ 40 | storbdq2, \ 41 | storbdq3, \ 42 | storwdq0, \ 43 | storwdq1, \ 44 | storwdq2, \ 45 | storwdq3, \ 46 | storbiq1, \ 47 | storbiq2, \ 48 | storbiq3, \ 49 | storwiq1, \ 50 | storwiq2, \ 51 | storwiq3, \ 52 | loadbdq0, \ 53 | loadbdq1, \ 54 | loadbdq2, \ 55 | loadbdq3, \ 56 | loadwdq0, \ 57 | loadwdq1, \ 58 | loadwdq2, \ 59 | loadwdq3, \ 60 | loadbiq1, \ 61 | loadbiq2, \ 62 | loadbiq3, \ 63 | loadwiq1, \ 64 | loadwiq2, \ 65 | loadwiq3, \ 66 | pushbq0, \ 67 | pushbq1, \ 68 | pushbq2, \ 69 | pushbq3, \ 70 | pushwq0, \ 71 | pushwq1, \ 72 | pushwq2, \ 73 | pushwq3, \ 74 | popbq0, \ 75 | popbq1, \ 76 | popbq2, \ 77 | popbq3, \ 78 | popwq0, \ 79 | popwq1, \ 80 | popwq2, \ 81 | popwq3, \ 82 | addbq0q0, \ 83 | addbq0q1, \ 84 | addbq0q2, \ 85 | addbq0q3, \ 86 | addbq1q0, \ 87 | addbq1q1, \ 88 | addbq1q2, \ 89 | addbq1q3, \ 90 | addbq2q0, \ 91 | addbq2q1, \ 92 | addbq2q2, \ 93 | addbq2q3, \ 94 | addbq3q0, \ 95 | addbq3q1, \ 96 | addbq3q2, \ 97 | addbq3q3, \ 98 | addwq0q0, \ 99 | addwq0q1, \ 100 | addwq0q2, \ 101 | addwq0q3, \ 102 | addwq1q0, \ 103 | addwq1q1, \ 104 | addwq1q2, \ 105 | addwq1q3, \ 106 | addwq2q0, \ 107 | addwq2q1, \ 108 | addwq2q2, \ 109 | addwq2q3, \ 110 | addwq3q0, \ 111 | addwq3q1, \ 112 | addwq3q2, \ 113 | addwq3q3, \ 114 | subbq0q0, \ 115 | subbq0q1, \ 116 | subbq0q2, \ 117 | subbq0q3, \ 118 | subbq1q0, \ 119 | subbq1q1, \ 120 | subbq1q2, \ 121 | subbq1q3, \ 122 | subbq2q0, \ 123 | subbq2q1, \ 124 | subbq2q2, \ 125 | subbq2q3, \ 126 | subbq3q0, \ 127 | subbq3q1, \ 128 | subbq3q2, \ 129 | subbq3q3, \ 130 | subwq0q0, \ 131 | subwq0q1, \ 132 | subwq0q2, \ 133 | subwq0q3, \ 134 | subwq1q0, \ 135 | subwq1q1, \ 136 | subwq1q2, \ 137 | subwq1q3, \ 138 | subwq2q0, \ 139 | subwq2q1, \ 140 | subwq2q2, \ 141 | subwq2q3, \ 142 | subwq3q0, \ 143 | subwq3q1, \ 144 | subwq3q2, \ 145 | subwq3q3, \ 146 | mulbq0q0, \ 147 | mulbq0q1, \ 148 | mulbq0q2, \ 149 | mulbq0q3, \ 150 | mulbq1q0, \ 151 | mulbq1q1, \ 152 | mulbq1q2, \ 153 | mulbq1q3, \ 154 | mulbq2q0, \ 155 | mulbq2q1, \ 156 | mulbq2q2, \ 157 | mulbq2q3, \ 158 | mulbq3q0, \ 159 | mulbq3q1, \ 160 | mulbq3q2, \ 161 | mulbq3q3, \ 162 | mulwq0q0, \ 163 | mulwq0q1, \ 164 | mulwq0q2, \ 165 | mulwq0q3, \ 166 | mulwq1q0, \ 167 | mulwq1q1, \ 168 | mulwq1q2, \ 169 | mulwq1q3, \ 170 | mulwq2q0, \ 171 | mulwq2q1, \ 172 | mulwq2q2, \ 173 | mulwq2q3, \ 174 | mulwq3q0, \ 175 | mulwq3q1, \ 176 | mulwq3q2, \ 177 | mulwq3q3, \ 178 | smulbq0q0, \ 179 | smulbq0q1, \ 180 | smulbq0q2, \ 181 | smulbq0q3, \ 182 | smulbq1q0, \ 183 | smulbq1q1, \ 184 | smulbq1q2, \ 185 | smulbq1q3, \ 186 | smulbq2q0, \ 187 | smulbq2q1, \ 188 | smulbq2q2, \ 189 | smulbq2q3, \ 190 | smulbq3q0, \ 191 | smulbq3q1, \ 192 | smulbq3q2, \ 193 | smulbq3q3, \ 194 | smulwq0q0, \ 195 | smulwq0q1, \ 196 | smulwq0q2, \ 197 | smulwq0q3, \ 198 | smulwq1q0, \ 199 | smulwq1q1, \ 200 | smulwq1q2, \ 201 | smulwq1q3, \ 202 | smulwq2q0, \ 203 | smulwq2q1, \ 204 | smulwq2q2, \ 205 | smulwq2q3, \ 206 | smulwq3q0, \ 207 | smulwq3q1, \ 208 | smulwq3q2, \ 209 | smulwq3q3, \ 210 | divbq1, \ 211 | divbq2, \ 212 | divbq3, \ 213 | divwq1, \ 214 | divwq2, \ 215 | divwq3, \ 216 | sdivbq1, \ 217 | sdivbq2, \ 218 | sdivbq3, \ 219 | sdivwq1, \ 220 | sdivwq2, \ 221 | sdivwq3, \ 222 | andbq0q0, \ 223 | andbq0q1, \ 224 | andbq0q2, \ 225 | andbq0q3, \ 226 | andbq1q0, \ 227 | andbq1q1, \ 228 | andbq1q2, \ 229 | andbq1q3, \ 230 | andbq2q0, \ 231 | andbq2q1, \ 232 | andbq2q2, \ 233 | andbq2q3, \ 234 | andbq3q0, \ 235 | andbq3q1, \ 236 | andbq3q2, \ 237 | andbq3q3, \ 238 | andwq0q0, \ 239 | andwq0q1, \ 240 | andwq0q2, \ 241 | andwq0q3, \ 242 | andwq1q0, \ 243 | andwq1q1, \ 244 | andwq1q2, \ 245 | andwq1q3, \ 246 | andwq2q0, \ 247 | andwq2q1, \ 248 | andwq2q2, \ 249 | andwq2q3, \ 250 | andwq3q0, \ 251 | andwq3q1, \ 252 | andwq3q2, \ 253 | andwq3q3, \ 254 | orbq0q0, \ 255 | orbq0q1, \ 256 | orbq0q2, \ 257 | orbq0q3, \ 258 | orbq1q0, \ 259 | orbq1q1, \ 260 | orbq1q2, \ 261 | orbq1q3, \ 262 | orbq2q0, \ 263 | orbq2q1, \ 264 | orbq2q2, \ 265 | orbq2q3, \ 266 | orbq3q0, \ 267 | orbq3q1, \ 268 | orbq3q2, \ 269 | orbq3q3, \ 270 | orwq0q0, \ 271 | orwq0q1, \ 272 | orwq0q2, \ 273 | orwq0q3, \ 274 | orwq1q0, \ 275 | orwq1q1, \ 276 | orwq1q2, \ 277 | orwq1q3, \ 278 | orwq2q0, \ 279 | orwq2q1, \ 280 | orwq2q2, \ 281 | orwq2q3, \ 282 | orwq3q0, \ 283 | orwq3q1, \ 284 | orwq3q2, \ 285 | orwq3q3, \ 286 | invbq0, \ 287 | invbq1, \ 288 | invbq2, \ 289 | invbq3, \ 290 | invwq0, \ 291 | invwq1, \ 292 | invwq2, \ 293 | invwq3, \ 294 | xorbq0q0, \ 295 | xorbq0q1, \ 296 | xorbq0q2, \ 297 | xorbq0q3, \ 298 | xorbq1q0, \ 299 | xorbq1q1, \ 300 | xorbq1q2, \ 301 | xorbq1q3, \ 302 | xorbq2q0, \ 303 | xorbq2q1, \ 304 | xorbq2q2, \ 305 | xorbq2q3, \ 306 | xorbq3q0, \ 307 | xorbq3q1, \ 308 | xorbq3q2, \ 309 | xorbq3q3, \ 310 | xorwq0q0, \ 311 | xorwq0q1, \ 312 | xorwq0q2, \ 313 | xorwq0q3, \ 314 | xorwq1q0, \ 315 | xorwq1q1, \ 316 | xorwq1q2, \ 317 | xorwq1q3, \ 318 | xorwq2q0, \ 319 | xorwq2q1, \ 320 | xorwq2q2, \ 321 | xorwq2q3, \ 322 | xorwq3q0, \ 323 | xorwq3q1, \ 324 | xorwq3q2, \ 325 | xorwq3q3, \ 326 | shrbq0q0, \ 327 | shrbq0q1, \ 328 | shrbq0q2, \ 329 | shrbq0q3, \ 330 | shrbq1q0, \ 331 | shrbq1q1, \ 332 | shrbq1q2, \ 333 | shrbq1q3, \ 334 | shrbq2q0, \ 335 | shrbq2q1, \ 336 | shrbq2q2, \ 337 | shrbq2q3, \ 338 | shrbq3q0, \ 339 | shrbq3q1, \ 340 | shrbq3q2, \ 341 | shrbq3q3, \ 342 | shrwq0q0, \ 343 | shrwq0q1, \ 344 | shrwq0q2, \ 345 | shrwq0q3, \ 346 | shrwq1q0, \ 347 | shrwq1q1, \ 348 | shrwq1q2, \ 349 | shrwq1q3, \ 350 | shrwq2q0, \ 351 | shrwq2q1, \ 352 | shrwq2q2, \ 353 | shrwq2q3, \ 354 | shrwq3q0, \ 355 | shrwq3q1, \ 356 | shrwq3q2, \ 357 | shrwq3q3, \ 358 | shlbq0q0, \ 359 | shlbq0q1, \ 360 | shlbq0q2, \ 361 | shlbq0q3, \ 362 | shlbq1q0, \ 363 | shlbq1q1, \ 364 | shlbq1q2, \ 365 | shlbq1q3, \ 366 | shlbq2q0, \ 367 | shlbq2q1, \ 368 | shlbq2q2, \ 369 | shlbq2q3, \ 370 | shlbq3q0, \ 371 | shlbq3q1, \ 372 | shlbq3q2, \ 373 | shlbq3q3, \ 374 | shlwq0q0, \ 375 | shlwq0q1, \ 376 | shlwq0q2, \ 377 | shlwq0q3, \ 378 | shlwq1q0, \ 379 | shlwq1q1, \ 380 | shlwq1q2, \ 381 | shlwq1q3, \ 382 | shlwq2q0, \ 383 | shlwq2q1, \ 384 | shlwq2q2, \ 385 | shlwq2q3, \ 386 | shlwq3q0, \ 387 | shlwq3q1, \ 388 | shlwq3q2, \ 389 | shlwq3q3, \ 390 | jui, \ 391 | jud, \ 392 | jzi, \ 393 | jzd, \ 394 | jnzi, \ 395 | jnzd, \ 396 | jai, \ 397 | jad, \ 398 | jbi, \ 399 | jbd, \ 400 | jli, \ 401 | jld, \ 402 | jgi, \ 403 | jgd, \ 404 | jaei, \ 405 | jaed, \ 406 | jbei, \ 407 | jbed, \ 408 | jlei, \ 409 | jled, \ 410 | jgei, \ 411 | jged, \ 412 | jcalli, \ 413 | jcalld, \ 414 | return, \ 415 | yld 416 | } -------------------------------------------------------------------------------- /qbx.asm: -------------------------------------------------------------------------------- 1 | format PE64 NX GUI 6.0 2 | 3 | ;QBX_BUILD_MODE_DEBUG = 1 4 | 5 | entry start 6 | 7 | include 'win64_helpers.asm' 8 | include 'qbx_insn_list.asm' 9 | include 'qbx_insn_helpers.asm' 10 | include 'qbx_registers.asm' 11 | 12 | ; define constants for all QBX instruction codes. 13 | qbx_insns define_icodes, 0 14 | 15 | ; screen dimension constants. 16 | SCR_CHAR_WIDTH equ 80 17 | SCR_CHAR_HEIGHT equ 25 18 | 19 | ; this data section contains import tables and some 20 | ; other global data structures that qbx uses. 21 | section '.data' import readable writeable executable 22 | import_directory_table KERNEL32, USER32, SHELL32 23 | import_functions KERNEL32, \ 24 | AllocConsole, \ 25 | CreateConsoleScreenBuffer, \ 26 | SetConsoleActiveScreenBuffer, \ 27 | SetConsoleTitleA, \ 28 | SetConsoleScreenBufferSize, \ 29 | SetConsoleCursorInfo, \ 30 | WriteConsoleOutputA, \ 31 | ExitProcess, \ 32 | CreateFileW, \ 33 | CreateFileA, \ 34 | ReadFile, \ 35 | WriteFile, \ 36 | CloseHandle, \ 37 | GetCommandLineW, \ 38 | GetLastError, \ 39 | Sleep, \ 40 | GetFileSize 41 | import_functions USER32, MessageBoxA 42 | import_functions SHELL32, CommandLineToArgvW 43 | ; string constants. 44 | err_msg_title db "Error", 0 45 | file_err_msg db "Failed to open the input file.", 0 46 | size_err_msg db "Image too big to fit in QBX memory.", 0 47 | noinput_err_msg db "Input file name not specified.", 0 48 | dump_err_msg db "Failed to open dump file for writing.",0 49 | dump_file_name db "qbx.dump", 0 50 | window_title db "QBX Bytecode Xecutor", 0 51 | ; the jump table that maps insn codes to insn implementations. 52 | qbx_insns define_jmp_table, qbx_jmp_table 53 | ; some variables. 54 | screen_rect SMALL_RECT ; defines target rect for screen updates. 55 | con_bbuf dq ? ; console back buffer handle. 56 | con_fbuf dq ? ; console front buffer handle. 57 | num_args dw ? ; stores the number of command line arguments. 58 | args_ptr dq ? ; pointer to the command line argument string. 59 | tmp_space dq ? ; temporary space for miscellaneous small data. 60 | 61 | ; this section contains the memory seen by programs executed by qbx. 62 | ; splitting it into a separate section reduces the final executable size. 63 | section '.mem' data readable writeable 64 | QBX_MEM_SIZE = 1024 * 16 ; 16 K 65 | qbx_mem db QBX_MEM_SIZE dup ? 66 | 67 | 68 | ; all executable code lives in this section. 69 | section '.code' code readable executable 70 | start: ; entry point - program starts here. 71 | ; parse the command line to extract the input file name. 72 | call64 [GetCommandLineW] 73 | call64 [CommandLineToArgvW], rax, num_args 74 | mov [args_ptr], rax 75 | cmp [num_args], 2 76 | jge open_input 77 | ; if an input file name is not provided, display an error message and exit. 78 | call64 [MessageBoxA], 0, noinput_err_msg, err_msg_title, MB_ICONERROR 79 | call64 [ExitProcess], 1 80 | open_input: 81 | ; attempt to open the image file. 82 | call64 [CreateFileW], [rax + 8], GENERIC_READ, 0, 0, OPEN_EXISTING, 0, 0 83 | cmp rax, INVALID_HANDLE 84 | jne check_size 85 | ; if the image could not be opened, display error message and exit. 86 | call64 [MessageBoxA], 0, file_err_msg, err_msg_title, MB_ICONERROR 87 | call64 [ExitProcess], 1 88 | check_size: 89 | ; check that the input file is small enough to fit into qbx memory. 90 | mov r12, rax 91 | call64 [GetFileSize], r12, tmp_space 92 | cmp [tmp_space], 0 93 | jnz file_size_error 94 | cmp rax, QBX_MEM_SIZE 95 | jl read_input 96 | file_size_error: 97 | ; exit if requested image is too large. 98 | call64 [MessageBoxA], 0, size_err_msg, err_msg_title, MB_ICONERROR 99 | call64 [ExitProcess], 1 100 | read_input: 101 | ; read the contents of the image into the qbx memory region. 102 | call64 [ReadFile], r12, qbx_mem, QBX_MEM_SIZE, tmp_space, 0 103 | call64 [CloseHandle], r12 104 | setup_console: 105 | ; allocate console, front and back screen buffers. 106 | call64 [AllocConsole] 107 | call64 [CreateConsoleScreenBuffer], GENERIC_WRITE, 0, 0, 1, 0 108 | mov [con_bbuf], rax 109 | call64 [CreateConsoleScreenBuffer], GENERIC_WRITE, 0, 0, 1, 0 110 | mov [con_fbuf], rax 111 | ; set up screen buffers. 112 | call64 [SetConsoleScreenBufferSize], [con_bbuf], ((SCR_CHAR_HEIGHT shl 16) or SCR_CHAR_WIDTH) 113 | call64 [SetConsoleScreenBufferSize], [con_fbuf], ((SCR_CHAR_HEIGHT shl 16) or SCR_CHAR_WIDTH) 114 | jmp set_cursor_info 115 | empty_cursor_info dq 0x01 116 | set_cursor_info: 117 | call64 [SetConsoleCursorInfo], [con_bbuf], empty_cursor_info 118 | call64 [SetConsoleCursorInfo], [con_fbuf], empty_cursor_info 119 | ; set console title. 120 | call64 [SetConsoleTitleA], window_title 121 | begin_execution: 122 | ; prep qbx for execution. 123 | xor q0, q0 124 | xor q1, q1 125 | xor q2, q2 126 | xor q3, q3 127 | xor qip, qip 128 | xor rdi, rdi 129 | mov qsp, QBX_MEM_SIZE - 1 ; initialize the stack pointer 130 | mov word [screen_rect.Left], 0 131 | mov word [screen_rect.Top], 0 132 | mov word [screen_rect.Right], SCR_CHAR_WIDTH 133 | mov word [screen_rect.Bottom], SCR_CHAR_HEIGHT 134 | if defined(QBX_BUILD_MODE_DEBUG) 135 | int3 ; breakpoint for the debugger 136 | end if 137 | advance: ; main qbx loop. 138 | mov di, word [qbx_mem + qip] ; read the next instruction. 139 | add qip, 2 ; advance instruction pointer. 140 | movzx r10, word [qbx_jmp_table + rdi * 2] ; read offset from jump table. 141 | add r10, insn_base ; compute address of insn implementation. 142 | mov rax, qflags ; prepare to set flags. 143 | sahf ; set flags. 144 | jmp r10 ; jump to insn implementation. 145 | 146 | insn_base: ; implementations of qbx instructions. 147 | 148 | ; do nothing. 149 | insn noop 150 | nop 151 | endinsn 152 | 153 | ; stop execution. 154 | insn halt 155 | ; check if state dump was requested. 156 | cmp [args_ptr], 3 157 | jl quit 158 | mov r10, [args_ptr] 159 | mov rdx, qword [r10 + 16] 160 | cmp byte [rdx], 'd' 161 | jne quit 162 | ; dump memory and registers to a file. 163 | call64 [CreateFileA], dump_file_name, GENERIC_WRITE, 0, 0, CREATE_ALWAYS, 0, 0 164 | cmp rax, INVALID_HANDLE 165 | jne dump 166 | ; couldn't open dump file for writing, report error and exit. 167 | call64 [MessageBoxA], 0, err_msg_title, dump_err_msg, MB_ICONERROR 168 | call64 [ExitProcess], 1 169 | dump: 170 | push rax 171 | ; first, write all memory to the file. 172 | call64 [WriteFile], rax, qbx_mem, QBX_MEM_SIZE, tmp_space, 0 173 | pop rax 174 | ; next, dump registers 175 | mov word [qbx_mem + 0], q0 176 | mov word [qbx_mem + 2], q1 177 | mov word [qbx_mem + 4], q2 178 | mov word [qbx_mem + 6], q3 179 | mov word [qbx_mem + 8], qipw 180 | mov word [qbx_mem + 10], qspw 181 | mov word [qbx_mem + 12], qflagsw 182 | push rax 183 | call64 [WriteFile], rax, qbx_mem, 13, tmp_space, 0 184 | pop rax 185 | call64 [CloseHandle], rax 186 | quit: 187 | call64 [ExitProcess], 0 188 | endinsn 189 | 190 | ; move immediate value into register. 191 | rept QBX_NUM_REGISTERS reg:0 { 192 | ; word-sized operands. 193 | insn moviwq#reg 194 | mov q#reg, word [qbx_mem + qip] 195 | add qip, 2 196 | endinsn 197 | 198 | ; byte-sized operands. 199 | insn movibq#reg 200 | mov q#reg#b, byte [qbx_mem + qip] 201 | add qip, 1 202 | endinsn 203 | } 204 | 205 | ; move value between registers. 206 | rept QBX_NUM_REGISTERS tgt:0 { 207 | rept 4 src:0 \{ 208 | if ~(tgt eq \src) ; src and dst must be different. 209 | ; word-sized operands. 210 | insn movwq#tgt#q\#src 211 | mov q#tgt, q\#src 212 | endinsn 213 | 214 | ; byte-sized operands. 215 | insn movbq#tgt#q\#src 216 | mov q#tgt#b, q\#src\#b 217 | endinsn 218 | 219 | end if 220 | \} 221 | } 222 | 223 | ; store value to direct address. 224 | rept QBX_NUM_REGISTERS reg:0 { 225 | ; word-sized operand. 226 | insn storwdq#reg 227 | movzx rcx, word [qbx_mem + qip] 228 | add qip, 2 229 | mov word [qbx_mem + rcx], q#reg 230 | endinsn 231 | 232 | ; byte-sized operand. 233 | insn storbdq#reg 234 | movzx rcx, word [qbx_mem + qip] 235 | add qip, 2 236 | mov byte [qbx_mem + rcx], q#reg#b 237 | endinsn 238 | } 239 | 240 | ; store value to address in q0. 241 | rept 3 reg { 242 | ; word-sized operand. 243 | insn storwiq#reg 244 | mov word [qbx_mem + qaddr], q#reg 245 | endinsn 246 | 247 | ; byte-sized operand. 248 | insn storbiq#reg 249 | mov byte [qbx_mem + qaddr], q#reg#b 250 | endinsn 251 | } 252 | 253 | ; load value from direct address. 254 | rept QBX_NUM_REGISTERS reg:0 { 255 | ; word-sized operand. 256 | insn loadwdq#reg 257 | movzx rcx, word [qbx_mem + qip] 258 | add qip, 2 259 | mov q#reg, word [qbx_mem + rcx] 260 | endinsn 261 | 262 | ; byte-sized operand. 263 | insn loadbdq#reg 264 | movzx rcx, word [qbx_mem + qip] 265 | add qip, 2 266 | mov q#reg#b, byte [qbx_mem + rcx] 267 | endinsn 268 | 269 | } 270 | 271 | ; load value from address in q0. 272 | rept 3 reg { 273 | insn loadwiq#reg 274 | mov q#reg, word [qbx_mem + qaddr] 275 | endinsn 276 | 277 | insn loadbiq#reg 278 | mov q#reg#b, byte [qbx_mem + qaddr] 279 | endinsn 280 | 281 | } 282 | 283 | ; push value onto the stack 284 | rept QBX_NUM_REGISTERS reg:0 { 285 | insn pushwq#reg 286 | sub qsp, 2 287 | mov word [qbx_mem + qsp + 1], q#reg 288 | endinsn 289 | 290 | insn pushbq#reg 291 | sub qsp, 1 292 | mov byte [qbx_mem + qsp + 1], q#reg#b 293 | endinsn 294 | } 295 | 296 | ; pop word-sized value from the stack. 297 | rept QBX_NUM_REGISTERS reg:0 { 298 | insn popwq#reg 299 | mov q#reg, word [qbx_mem + qsp + 1] 300 | add qsp, 2 301 | endinsn 302 | 303 | insn popbq#reg 304 | mov q#reg#b, byte [qbx_mem + qsp + 1] 305 | add qsp, 1 306 | endinsn 307 | } 308 | 309 | ; addition instructions 310 | rept QBX_NUM_REGISTERS sreg:0 { 311 | rept QBX_NUM_REGISTERS dreg:0 \{ 312 | ; byte-sized operands 313 | insn addbq\#dreg\#q#sreg 314 | add q\#dreg\#b, q#sreg#b 315 | update_qbx_flags = 1 316 | endinsn 317 | 318 | ; word-sized operands 319 | insn addwq\#dreg\#q#sreg 320 | add q\#dreg\#w, q#sreg#w 321 | update_qbx_flags = 1 322 | endinsn 323 | 324 | \} 325 | } 326 | 327 | ; subtraction instructions 328 | rept QBX_NUM_REGISTERS sreg:0 { 329 | rept QBX_NUM_REGISTERS dreg:0 \{ 330 | ; byte-sized operands 331 | insn subbq\#dreg\#q#sreg 332 | sub q\#dreg\#b, q#sreg#b 333 | update_qbx_flags = 1 334 | endinsn 335 | 336 | ; word-sized operands 337 | insn subwq\#dreg\#q#sreg 338 | sub q\#dreg\#w, q#sreg#w 339 | update_qbx_flags = 1 340 | endinsn 341 | 342 | \} 343 | } 344 | 345 | ; unsigned multiplication 346 | rept QBX_NUM_REGISTERS sreg:0 { 347 | rept QBX_NUM_REGISTERS dreg:0 \{ 348 | ; byte-sized operands 349 | insn mulbq\#dreg\#q#sreg 350 | xor rax, rax 351 | mov al, q\#dreg\#b 352 | mul q#sreg#b 353 | mov q\#dreg\#b, al 354 | update_qbx_flags = 1 355 | endinsn 356 | 357 | ; word-sized operands 358 | insn mulwq\#dreg\#q#sreg 359 | xor rax, rax 360 | mov ax, q\#dreg\#w 361 | mul q#sreg#w 362 | mov q\#dreg\#w, ax 363 | update_qbx_flags = 1 364 | endinsn 365 | \} 366 | } 367 | 368 | ; signed multiplication 369 | rept QBX_NUM_REGISTERS sreg:0 { 370 | rept QBX_NUM_REGISTERS dreg:0 \{ 371 | ; byte-sized operands 372 | insn smulbq\#dreg\#q#sreg 373 | xor rax, rax 374 | mov al, q\#dreg\#b 375 | imul q#sreg#b 376 | mov q\#dreg\#b, al 377 | update_qbx_flags = 1 378 | endinsn 379 | 380 | ; word-sized operands 381 | insn smulwq\#dreg\#q#sreg 382 | imul q\#dreg\#w, q#sreg#w 383 | update_qbx_flags = 1 384 | endinsn 385 | \} 386 | } 387 | 388 | ; unsigned division 389 | rept 3 sreg { 390 | ; byte-sized operands 391 | insn divbq#sreg 392 | xor dx, dx 393 | xor ax, ax 394 | mov ax, q0w 395 | movzx cx, q#sreg#b 396 | div cx 397 | mov q0, ax 398 | mov q1, dx 399 | update_qbx_flags = 1 400 | endinsn 401 | 402 | ; word-sized operands 403 | insn divwq#sreg 404 | xor rax, rax 405 | xor rdx, rdx 406 | mov ax, q0w 407 | div q#sreg#w 408 | mov q0, ax 409 | mov q1, dx 410 | update_qbx_flags = 1 411 | endinsn 412 | } 413 | 414 | ; signed division 415 | rept 3 sreg { 416 | ; byte-sized operands 417 | insn sdivbq#sreg 418 | xor dx, dx 419 | xor ax, ax 420 | mov ax, q0w 421 | movzx cx, q#sreg#b 422 | idiv cx 423 | mov q0, ax 424 | mov q1, dx 425 | update_qbx_flags = 1 426 | endinsn 427 | 428 | ; word-sized operands 429 | insn sdivwq#sreg 430 | xor rax, rax 431 | xor rdx, rdx 432 | mov ax, q0w 433 | idiv q#sreg#w 434 | mov q0, ax 435 | mov q1, dx 436 | update_qbx_flags = 1 437 | endinsn 438 | } 439 | 440 | 441 | ; bitwise and instructions 442 | rept QBX_NUM_REGISTERS sreg:0 { 443 | rept QBX_NUM_REGISTERS dreg:0 \{ 444 | ; byte-sized operands 445 | insn andbq\#dreg\#q#sreg 446 | and q\#dreg\#b, q#sreg#b 447 | update_qbx_flags = 1 448 | endinsn 449 | 450 | ; word-sized operands 451 | insn andwq\#dreg\#q#sreg 452 | and q\#dreg\#w, q#sreg#w 453 | update_qbx_flags = 1 454 | endinsn 455 | 456 | \} 457 | } 458 | 459 | ; bitwise or instructions 460 | rept QBX_NUM_REGISTERS sreg:0 { 461 | rept QBX_NUM_REGISTERS dreg:0 \{ 462 | ; byte-sized operands 463 | insn orbq\#dreg\#q#sreg 464 | or q\#dreg\#b, q#sreg#b 465 | update_qbx_flags = 1 466 | endinsn 467 | 468 | ; word-sized operands 469 | insn orwq\#dreg\#q#sreg 470 | or q\#dreg\#w, q#sreg#w 471 | update_qbx_flags = 1 472 | endinsn 473 | 474 | \} 475 | } 476 | 477 | ; bitwise not instructions 478 | rept QBX_NUM_REGISTERS dreg:0 { 479 | ; byte-sized operands 480 | insn invbq#dreg 481 | not q#dreg#b 482 | update_qbx_flags = 1 483 | endinsn 484 | 485 | ; word-sized operands 486 | insn invwq#dreg 487 | not q#dreg#w 488 | update_qbx_flags = 1 489 | endinsn 490 | 491 | } 492 | 493 | ; bitwise xor instructions 494 | rept QBX_NUM_REGISTERS sreg:0 { 495 | rept QBX_NUM_REGISTERS dreg:0 \{ 496 | ; byte-sized operands 497 | insn xorbq\#dreg\#q#sreg 498 | xor q\#dreg\#b, q#sreg#b 499 | update_qbx_flags = 1 500 | endinsn 501 | 502 | ; word-sized operands 503 | insn xorwq\#dreg\#q#sreg 504 | xor q\#dreg\#w, q#sreg#w 505 | update_qbx_flags = 1 506 | endinsn 507 | 508 | \} 509 | } 510 | 511 | ; shift left instructions 512 | rept QBX_NUM_REGISTERS sreg:0 { 513 | rept QBX_NUM_REGISTERS dreg:0 \{ 514 | ; byte-sized operands 515 | insn shlbq\#dreg\#q#sreg 516 | movzx rcx, q#sreg 517 | shl q\#dreg\#b, cl 518 | update_qbx_flags = 1 519 | endinsn 520 | 521 | ; word-sized operands 522 | insn shlwq\#dreg\#q#sreg 523 | movzx rcx, q#sreg 524 | shl q\#dreg\#w, cl 525 | update_qbx_flags = 1 526 | endinsn 527 | 528 | \} 529 | } 530 | 531 | ; shift right instructions 532 | rept QBX_NUM_REGISTERS sreg:0 { 533 | rept QBX_NUM_REGISTERS dreg:0 \{ 534 | ; byte-sized operands 535 | insn shrbq\#dreg\#q#sreg 536 | movzx rcx, q#sreg 537 | shr q\#dreg\#b, cl 538 | update_qbx_flags = 1 539 | endinsn 540 | 541 | ; word-sized operands 542 | insn shrwq\#dreg\#q#sreg 543 | movzx rcx, q#sreg 544 | shr q\#dreg\#w, cl 545 | update_qbx_flags = 1 546 | endinsn 547 | 548 | \} 549 | } 550 | 551 | ; unconditional jump to indirect address. 552 | insn jui 553 | mov qipw, q0 554 | endinsn 555 | 556 | ; unconditional jump to direct address. 557 | insn jud 558 | movzx rcx, word [qbx_mem + qip] 559 | mov qipw, cx 560 | endinsn 561 | 562 | ; convenience macro for defining conditional jump insn impls. 563 | macro _define_cond_jump cc* { 564 | insn j#cc#i 565 | cmov#cc qipw, q0 566 | endinsn 567 | 568 | ; note that the impl for conditional jump to direct address 569 | ; needs to advance the insn ptr ONLY if the condition is not 570 | ; fulfilled. to do this, we use x86 setCC with a negated 571 | ; condition, multiply result by 2 and add it to the insn ptr 572 | ; at the end. 573 | insn j#cc#d 574 | movzx rcx, word [qbx_mem + qip] 575 | setn#cc dl 576 | cmov#cc qipw, cx 577 | shl dl, 1 578 | add qipw, dx 579 | endinsn 580 | 581 | ; analogous implementations for the negative case. 582 | 583 | insn jn#cc#i 584 | cmovn#cc qipw, q0 585 | endinsn 586 | 587 | insn jn#cc#d 588 | movzx rcx, word [qbx_mem + qip] 589 | set#cc dl 590 | cmovn#cc qipw, cx 591 | shl dl, 1 592 | add qipw, dx 593 | endinsn 594 | } 595 | 596 | _define_cond_jump z 597 | _define_cond_jump a 598 | _define_cond_jump b 599 | _define_cond_jump g 600 | _define_cond_jump l 601 | _define_cond_jump ae 602 | _define_cond_jump be 603 | _define_cond_jump ge 604 | _define_cond_jump le 605 | 606 | ; call procedure at indirect address. 607 | insn jcalli 608 | sub qsp, 2 609 | mov word [qbx_mem + qsp], qipw 610 | mov qipw, q0 611 | endinsn 612 | 613 | ; call procedure at direct address. 614 | insn jcalld 615 | sub qsp, 2 616 | mov word [qbx_mem + qsp], qipw 617 | movzx rcx, word [qbx_mem + qip] 618 | mov qipw, cx 619 | endinsn 620 | 621 | ; return 622 | insn return 623 | mov qipw, word [qbx_mem + qsp] 624 | add qsp, 2 625 | endinsn 626 | 627 | insn yld 628 | ; save QBX state before handling yield event. 629 | pushfq ; save flags that haven't been committed to qflags yet. 630 | push q0 631 | push q1 632 | push q2 633 | push q3 634 | movzx rcx, word [qbx_mem + qip] ; read yield code into rcx. 635 | add qip, 2 ; advance ip. 636 | push qip ; save ip. 637 | push qsp 638 | push qflags 639 | ; find appropriate handler. 640 | cmp rcx, 0x01 641 | je yld_screenupd 642 | cmp rcx, 0x02 643 | je yld_sleep 644 | yld_debugbreak: 645 | ; YIELD CODE 0x00 -- debug break. 646 | ; this is also triggered when no other matching handler is found. 647 | int3 648 | jmp yld_return 649 | yld_screenupd: 650 | ; YIELD CODE 0x01 -- screen update. 651 | ; q0 has pointer to screen area in memory; 652 | ; q1 has width/height of updated rect; 653 | ; q2 has X/Y coords of upper left corner of updated rect. 654 | add r12, qbx_mem 655 | xor rax, rax 656 | mov bx, r13w 657 | mov ah, bl 658 | shl rax, 8 659 | mov al, bh 660 | mov bx, q2 661 | and bx, 0xff00 662 | shr bx, 8 663 | mov word [screen_rect.Left], bx 664 | and q2, 0x00ff 665 | mov word [screen_rect.Top], q2 666 | call64 [WriteConsoleOutputA], [con_bbuf], r12, rax, 0, screen_rect 667 | call64 [SetConsoleActiveScreenBuffer], [con_bbuf] 668 | mov r10, [con_bbuf] 669 | mov r11, [con_fbuf] 670 | mov [con_bbuf], r11 671 | mov [con_fbuf], r10 672 | xor r12, r12 673 | jmp yld_return 674 | yld_sleep: 675 | ; YIELD CODE 0x02 -- sleep. 676 | ; q0 contains the number of milliseconds to sleep. 677 | movzx rcx, q0 678 | call64 [Sleep], rcx 679 | jmp yld_return 680 | yld_return: 681 | ; restore QBX state. 682 | pop qflags 683 | pop qsp 684 | pop qip 685 | pop q3 686 | pop q2 687 | pop q1 688 | pop q0 689 | popfq 690 | endinsn 691 | 692 | update_flags_advance: 693 | ; commit the current set of flags to the qflags register and 694 | ; proceed to the next insn. 695 | lahf 696 | mov qflags, rax 697 | jmp advance 698 | --------------------------------------------------------------------------------