MS-DOS Debug command what key is pressed - dos

Hello I'm trying to create a command/code on MS-DOS Debug to see what key I press on the keyboard. The code I have only works for one keypress but it doesn't work when I press different keys.
I want it to work with 2 or more different keys with individual output msgs depending on which key was pressed i.e hex 54 you pressed shift-F1
Mov ah, 0
Int 16
Mov bh, ah
Mov bl, al
Cmp ah, 3b
Jne again
Mov ah, 9
Mov dx, [msg address]
Int 21
Int 20
Db ‘You pressed F1 key$’

If it is not crucial here, you can constantly read input from 0x60h port and see what it gives. Value below 128 is a key press, when above 127 is a key release. You don't use the interrupt then.
Try to take input from 0x60h port first in higher level language, I advise QBASIC. Then you will be able to quickly write a solution in asm.
Don't forget to make possibility to exit your program, for example by Esc key, which pressed gives value 1, or you will need to reboot your system to take control over system back.

Related

x86 assembler pushf causes program to exit

I think my real problem is I don't completely understand the stack frame mechanism so I am looking to understand why the following code causes the program execution to resume at the end of the application.
This code is called from a C function which is several call levels deep and the pushf causes program execution to revert back several levels through the stack and completely exit the program.
Since my work around works as expected I would like to know why using the pushf instruction appears to be (I assume) corrupting the stack.
In the routines I usually setup and clean up the stack with :
sub rsp, 28h
...
add rsp, 28h
However I noticed that this is only necessary when the assembly code calls a C function.
So I tried removing this from both routines but it made no difference. SaveFlagsCmb is an assembly function but could easily be a macro.
The code represents an emulated 6809 CPU Rora (Rotate Right Register A).
PUBLIC Rora_I_A ; Op 46 - Rotate Right through Carry A reg
Rora_I_A PROC
sub rsp, 28h
; Restore Flags
mov cx, word ptr [x86flags]
push cx
popf
; Rotate Right the byte and save the FLAGS
rcr byte ptr [q_s+AREG], 1
; rcr only affects Carry. Save the Carry first in dx then
; add 0 to result to trigger Zero and Sign/Neg flags
pushf ; this causes jump to end of program ????
pop dx ; this line never reached
and dx, CF ; Save only Carry Flag
add [q_s+AREG], 0 ; trigger NZ flags
mov rcx, NF+ZF+CF ; Flag Mask NZ
Call SaveFlagsCmb ; NZ from the add and CF saved in dx
add rsp, 28h
ret
Rora_I_A ENDP
However if I use this code it works as expected:
PUBLIC Rora_I_A ; Op 46 - Rotate Right through Carry A reg
Rora_I_A PROC
; sub rsp, 28h ; works with or without this!!!
; Restore Flags
mov ah, byte ptr [x86flags+LSB]
sahf
; Rotate Right the byte and save the FLAGS
rcr byte ptr [q_s+AREG], 1
; rcr only affects Carry. Save the Carry first in dx then
; add 0 to result to trigger Zero and Sign/Neg flags
lahf
mov dl, ah
and dx, CF ; Save only Carry Flag
add [q_s+AREG], 0 ; trigger NZ flags
mov rcx, NF+ZF+CF ; Flag Mask NZ
Call SaveFlagsCmb ; NZ from the add and CF saved in dx
; add rsp, 28h ; works with or without this!!!
ret
Rora_I_A ENDP
Your reported behaviour doesn't really make sense. Mostly this answer is just providing some background not a real answer, and a suggestion not to use pushf/popf in the first place for performance reasons.
Make sure your debugging tools work properly and aren't being fooled by something into falsely showing a "jump" to somewhere. (And jump where exactly?)
There's little reason to mess around with 16-bit operand size, but that's probably not your problem.
In Visual Studio / MASM, apparently (according to OP's comment) pushf assembles as pushfw, 66 9C which pushes 2 bytes. Presumably popf also assembles as popfw, only popping 2 bytes into FLAGS instead of the normal 8 bytes into RFLAGS. Other assemblers are different.1
So your code should work. Unless you're accidentally setting some other bit in FLAGS that breaks execution? There are bits in EFLAGS/RFLAGS other than condition codes, including the single-step TF Trap Flag: debug exception after every instruction.
We know you're in 64-bit mode, not 32-bit compat mode, otherwise rsp wouldn't be a valid register. And running 64-bit machine code in 32-bit mode wouldn't explain your observations either.
I'm not sure how that would explain pushf being a jump to anywhere. pushf itself can't fault or jump, and if popf set TF then the instruction after popf would have caused a debug exception.
Are you sure you're assembling 64-bit machine code and running it in 64-bit mode? The only thing that would be different if a CPU decoded your code in 32-bit mode should be the REX prefix on sub rsp, 28h, and the RIP-relative addressing mode on [x86flags] decoding as absolute (which would presumably fault). So I don't think that could explain what you're seeing.
Are you sure you're single-stepping by instructions (not source lines or C statements) with a debugger to test this?
Use a debugger to look at the machine code as you single-step. This seem really weird.
Anyway, it seems like a very low-performance idea to use pushf / popf at all, and also to be using 16-bit operand-size creating false dependencies.
e.g. you can set x86 CF with movzx ecx, word ptr [x86flags] / bt ecx, CF.
You can capture the output CF with setc cl
Also, if you're going to do multiple things to the byte from the guest memory, load it into an x86 register. A memory-destination RCR and a memory-destination ADD are unnecessarily slow vs. load / rcr / ... / test reg,reg / store.
LAHF/SAHF may be useful, but you can also do without them too for many cases. popf is quite slow (https://agner.org/optimize/) and it forces a round trip through memory. However, there is one condition-code outside the low 8 in x86 FLAGS: OF (signed overflow). asm-source compatibility with 8080 is still hurting x86 in 2019 :(
You can restore OF from a 0/1 integer with add al, 127: if AL was originally 1, it will overflow to 0x80, otherwise it won't. You can then restore the rest of the condition codes with SAHF. You can extract OF with seto al. Or you can just use pushf/popf.
; sub rsp, 28h ; works with or without this!!!
Yes of course. You have a leaf function that doesn't use any stack space.
You only need to reserve another 40 bytes (align the stack + 32 bytes of shadow space) if you were going to make another function call from this function.
Footnote 1: pushf/popf in other assemblers:
In NASM, pushf/popf default to the same width as other push/pop instructions: 8 bytes in 64-bit mode. You get the normal encoding without an operand-size prefix. (https://www.felixcloutier.com/x86/pushf:pushfd:pushfq)
Like for integer registers, both 16 and 64-bit operand-size for pushf/popf are encodeable in 64-bit mode, but 32-bit operand size isn't.
In NASM, your code would be broken because push cx / popf would push 2 bytes and pop 8, popping 6 bytes of your return address into RFLAGS.
But apparently MASM isn't like that. Probably a good idea to use explicit operand-size specifiers anyway, like pushfw and popfw if you use it at all, to make sure you get the 66 9C encoding, not just 9C pushfq.
Or better, use pushfq and pop rcx like a normal person: only write to 8 or 16-bit partial registers when you need to, and keep the stack qword-aligned. (16-byte alignment before call, 8-byte alignment always.)
I believe this is a bug in Visual Studio. I'm using 2022, so it's an issue that's been around for a while.
I don't know exactly what is triggering it, however stepping over one specific pushf in my code had the same symptoms, albeit with the code actually working.
Putting a breakpoint on the line after the pushf did break, and allowed further debugging of my app. Adding a push ax, pop ax before the pushf also seemed to fix the issue. So it must be a Visual Studio issue.
At this point I think MASM and debugging in Visual Studio has pretty much been abandoned. Any suggestions for alternatives for developing dlls on Windows would be appreciated!

LOOP is done only one time when single stepping in Turbo Debugger

The code must output 'ccb',but output only 'c', LOOP is done only one time, i have calibrated in TD, but why LOOP is done only one time?
I THINK THAT I MUST TO DECREMENT STRING_LENGTH, SO I WROTE
DEC STRING_LENGTH
BUT IT NOT WORK, SO I WROTE LIKE THAT
MOV SP,STRING_LENGTH
DEC SP
MOV STRING_LENGTH,SP
I KNOW WHAT ARE YOU THINKING RIGHT NOW, THAT IS SO INCORRECT, YOU ARE RIGHT)))
I CAN USE C++, BUT I WANT TO DO IT ONLY IN ASSEMBLY,
DOSSEG
.MODEL SMALL
.STACK 200H
.DATA
STRING DB 'cScbd$'
STRING_LENGTH EQU $-STRING
STRING1 DB STRING_LENGTH DUP (?) , '$'
.CODE
MOV AX,#DATA
MOV DS,AX
XOR SI,SI
XOR DI,DI
MOV CX,STRING_LENGTH
S:
MOV BL,STRING[DI]
AND STRING[DI],01111100B
CMP STRING[DI],01100000B
JNE L1
MOV AL,BL
MOV STRING1[SI],AL
ADD SI,2
L1:
ADD DI,2
LOOP S
MOV DL,STRING1
MOV AH,9
INT 21H
MOV AH,4CH
INT 21H
END
In Turbo Debugger (TD.EXE) the F8 "F8 step" will execute the loop completely, until the cx becomes zero (you can even create infinite loop by updating cx back to some value, preventing it from reaching the 1 -> 0 step).
To get "single-step" out of the loop instruction, use the F7 "F7 trace" - that will cause the cx to go from 6 to 5, and the code pointer will follow the jump back on the start of the loop.
About some other issues of your code:
MOV SP,STRING_LENGTH
DEC SP
MOV STRING_LENGTH,SP
sp is not general purpose register, don't use it for calculation like this. Whenever some instruction does use stack implicitly (push, pop, call, ret, ...), the values are being written and read in memory area addressed by the ss:sp register pair, so by manipulating the sp value you are modifying the current "stack".
Also in 16 bit x86 real mode all the interrupts (keyboard, timer, ...), when they occur, the current state of flag register and code address is stored into stack, before giving the control to the interrupt handler code, which usually will push additional values to the stack, so whatever is in memory on addresses below current ss:sp is not safe in 16 bit x86 real mode, and the memory content keeps "randomly" changing there by all the interrupts being executed meanwhile (the TD.EXE itself does use part of this stack memory after every single step).
For arithmetic use other registers, not sp. Once you will know enough about "stack", you will understand what kind of sp manipulation is common and why (like sub sp,40 at beginning of function which needs additional "local" memory space), and how to restore stack back into expected state.
One more thing about that:
MOV SP,STRING_LENGTH
DEC SP
MOV STRING_LENGTH,SP
The STRING_LENGTH is defined by EQU, which makes it compile time constant, and only compile time. It's not "variable" (memory allocation), contrary to the things like someLabel dw 1345, which cause the assembler to emit two bytes with values 0100_0001B, 0000_0101B (when read as 16 bit word in little-endian way, that's value 1345 encoded), and the first byte address has symbolic name someLabel, which can be used in further instructions, like dec word ptr [someLabel] to decrement that value in memory from 1345 to 1344 during runtime.
But EQU is different, it assigns the symbol STRING_LENGTH final value, like 14.
So your code can be read as:
mov sp,14 ; makes almost sense, (practically destroys stack setup)
dec sp ; still valid
mov 14,sp ; doesn't make any sense, constant can't be destination for MOV

Why call and ret functions work like this in assembly?

So, I was messing around with assembly, and stumbled upon weird thing. Before writing anything, I set registers to these values:
AX 0000, BX 0000, SP 00FD.
Lets say, that we write simple code, which increases registers AX and BX:
A100
INC AX
CALL 0200
A200
INC BX
RETN
After writing it, I tried to see how registers change when executing commands, one by one, using T command. First time, it increases AX, moves to 0200, increases BX and returns. Here is the weird bit: when it returns, it executes command:
ADD [BX+SI],AX and then calls 0200 again.
When it calls 0200 again, it repeats itself, but now, when it returns, it returns to CALL 0100 command (not to CALL 0200) and increases AX again and so on. Why is this happening?
I have image of full output, maybe this can help understand my question better?:
http://s18.postimg.org/wt6eracg9/Untitled.png
Based on your screenshots, your code seems to be this (filled with nop's, disassembled with udcli):
echo 40 e8 fc 00 01 00 e8 f7 00 e8 f4 ff x{1..244} 43 c3 | sed 's/x[0-9]*\>/90/g' | udcli -o 0x100 -x -16
0000000000000100 40 inc ax
0000000000000101 e8fc00 call word 0x200
0000000000000104 0100 add [bx+si], ax
0000000000000106 e8f700 call word 0x200
0000000000000109 e8f4ff call word 0x100
000000000000010c *** never reached ***
0000000000000200 43 inc bx
0000000000000201 c3 ret
The code flow is the following, line by line:
0000000000000100 40 inc ax
ax gets incremented.
0000000000000101 e8fc00 call word 0x200
Return address 0x104 gets pushed to the stack, ip (instruction pointer) is set to 0x200.
0000000000000200 43 inc bx
bx gets incremented.
0000000000000201 c3 ret
Near return, that is, ip is popped from stack. New ip will be 0x104.
0000000000000104 0100 add [bx+si], ax
The value of ax is added to the word value at [bx+si].
0000000000000106 e8f700 call word 0x200
Return address 0x109 gets pushed to the stack, ip (instruction pointer) is set to 0x200.
0000000000000200 43 inc bx
bx gets incremented.
0000000000000201 c3 ret
Near return, that is, ip is popped from stack. New ip will be 0x109.
0000000000000109 e8f4ff call word 0x100
Return address 0x10c gets pushed to the stack, ip (instruction pointer) is set to 0x100. So this is actually an infinite recursive function and will run out of stack.
So your problem is that you don't define the code after CALL 0200. There happens to be 01 00 (add [bx+si], ax) and after return it will be executed, and after it other undefined instructions.
My advice: download any decent assembler (NASM, YASM, FASM...) ASAP and don't ruin your life trying to write assembly code with DEBUG. Trying to write assembly programs with DEBUG is an attempt destined to fail.
On possible reason is that you don't have some exit, you didn't use DOS's 0x4c service, and the program get bound the code and started to execute random commands or the execution flow hit some ret instruction where no call is used, it then will behave unexpectedly.

Patch an executable file

In short
What would be the easiest way to make sure that the high word of ecx contains 0 by replacing following instruction in the exe file?
004044be 0fbf4dfc movsx ecx,word ptr [ebp-4]
Instead of containing FFFF9298 after executing adress 004044be I'd like ecx to contain 00009298.
What have I tried
I have tried replacing movsx with a simple movby replacing the opcode 0fbf in the executable with 8b0c but that didn't work out as planned (using the debugger to verify the change, I'm sure it was at the the right place)
Some background
I have a rather old program that for one reason or another AV's when I try to print to my HP printer. Everything works fine when I print to CutePDF so the current workaround is to generate a PDF file and print the PDF.
Getting my feet wet with WinDbg I tried to find the reason of why this was happening.
While this is not the root cause of my problem, it seems that ecx at some point get's a negative value which is used to allocate memory, ultimately resulting in an exception.
I could try to find why a negative value is returned but during my debugging session, I noticed that zeroing out the high word in ecx did the job (aka printed the file).
So instead of containing FFFF9298 after executing adress 004044be I'd like ecx to contain 00009298.
004044ac 0fbf05e8025100 movsx eax,word ptr [Encore32!SystemsPerPageDlogProc+0x10e4a1 (005102e8)]
004044b3 05416f0000 add eax,6F41h
004044b8 668945fc mov word ptr [ebp-4],ax
004044bc 6a40 push 40h
004044be 0fbf4dfc movsx ecx,word ptr [ebp-4] --> replace with ?
004044c2 51 push ecx
004044c3 8b55f8 mov edx,dword ptr [ebp-8]
004044c6 52 push edx
Use movzx, it does exactly what you need.
The opcode (for 16->32 version) is 0F B7 /r so just patching BF to B7 should do it.
movsx mean "move with sign extend". That means that the top bit of src is copied to all high bits of dest. You don't want that.
movzx fills top bits with 0.
movzx ecx,word ptr [ebp-4] is what you need. Using 32-bit code as in your example it can be encoded as 0F B7 4D FC.

How can I view what is happening in the lower parts of the EAX register in VS2008?

I am doing some assembly homework and thought the best way to get my head around what is going on is to watch what happens in the registers as the program runs. In Visual Studio You can view the registers but I am doing an operation that only changes things in the lower 8-bits of one of my registers so im not sure what I should be looking at. Can anyone help me out?
Here's the question and the code I'm running:
What will be the hexadecimal value of the destination operand after each of the following instructions execute in sequence?
TITLE MASM Template (main.asm)
INCLUDE Irvine32.inc
.data var1 SBYTE -4, -2, 3, 1
.code main PROC
call Clrscr
mov al, var1 mov ah, [var1+3]
exit main ENDP
END main
Im pretty sure the answer is -4 after the first statement and 1 after the second statement but I want to see it in the registers.
and the register window i have to look as in VS:
The ah and al registers are just aliases for the lower two bytes of eax, and so you can just monitor the eax entry in the register window. In your example, ah is 0x36 and al is 0x65. You might also need mov al, [var1] to get the value at that address, but I am not sure about that.

Resources