I'm writing an assembly (x64) program that uses 3 MACROS (mGetHandles, mWriteFile, mReadFile). I suppose to produce the following output (user input in boldface):
What is your name? John Smith
What is your address? 54321 Main St., La Mesa
Nice to meet you,
John Smith
54321 Main St., La Mesa
This is my code:
CONSOLE equ -11
KEYBOARD equ -10
CR equ 0dh
LF equ 0ah
extern GetStdHandle: PROTO
extern WriteFile: PROTO
extern ReadFile: PROTO
extrn ExitProcess: PROTO
.data
prompt1 byte 'What is your name? '
prompt2 byte 'What is your adress? '
hello byte 'Nice to meet you, ', CR, LF
myname byte 40 dup(' '), CR, LF, 0
myadress byte 40 dup(' ')
crlf byte CR, LF, 0 ;used to get a new line
.data?
stdin qword ? ;handle to console standard in
stdout qword ? ;handle to console standard out
numWrite qword ? ;number bytes actually written
numRead qword ? ;number bytes actually read
mGetStdHandle MACRO
;Get handle ID to console window
mov rcx, CONSOLE ;subsystem:console
call GetStdHandle ;handle in rax
mov stdout, rax ;save out handle
mov rcx, KEYBOARD ;keyboard code
call GetStdHandle ;handle in rax
mov stdin, rax ;save in handle
ENDM
mWriteFile MACRO
;Display message on console window
mov rcx, stdout ;parm1 = console handle
lea rdx, prompt1 ;parm2 = ascii message
mov r8, lengthof prompt1 ;# bytes to display (write)
lea r9, numWrite ;& to store #bytes written
call WriteFile ;display message
;Read ASCII from the keyboard
mov rcx, offset crlf ;cr, lf
mov rcx, stdin ;parm1 = keyboard handle
lea rdx, myname ;parm1 = ascii buffer
mov r8, lengthof myname ;# bytes of read
lea r9, numRead ;& to store #bytes actually read
call ReadFile ;get keystrokes
;Display message on console window
mov rcx, stdout ;parm1 = console handle
lea rdx, prompt2 ;parm2 = ascii message
mov r8, lengthof prompt2 ;# bytes to display (write)
lea r9, numWrite ;& to store #bytes written
call WriteFile ;display message
;Read ASCII from the keyboard
mov rcx, offset crlf ;cr, lf
mov rcx, stdin ;parm1 = keyboard handle
lea rdx, myadress ;parm1 = ascii buffer
mov r8, lengthof myadress ;# bytes of read
lea r9, numRead ;& to store #bytes actually read
call ReadFile ;get keystrokes
ENDM
mReadFile MACRO
mov rcx, stdout ;parm1 = console handle
lea rdx, hello ;parm2 = ascii message
mov r8, lengthof hello ;# bytes to display (write)
add r8, numRead ;& to store #bytes actually read
sub r8, 2
lea r9, numWrite ;& to store #bytes written
call WriteFile ;display message
mov rcx, stdout ;parm1 = console handle
lea rdx, myadress ;parm2 = ascii message
mov r8, lengthof myadress ;# bytes to display (write)
add r8, numRead ;& to store #bytes actually read
sub r8, 2
lea r9, numWrite ;& to store #bytes written
call WriteFile ;display message
ENDM
mBye MACRO retcode
mov rcx, retcode
callExitProcess
ENDM
.code
mainCRTStartup PROC
mGetStdHandle
mWriteFile
mReadFile
call ExitProcess
mainCRTStartup ENDP
END
The output I get is("___" represents empty space):
Nice to meet you,
John Smith
__________54321 Main St., La Mesa
Why is there space before "54321 Main St., La Mesa"?
Related
I'm trying to add arrA and arrB and store the values into arrC and print out.. once I run the code it takes me to infinite loop. How can I break the loop? Any suggestions greatly appreciated.
ExitProcess PROTO
WriteHex64 PROTO
.data
arrA BYTE 10h, 30h
arrB BYTE 0E0h, 40h
arrC BYTE 0, 0
string BYTE ", ", 00h
.code
main PROC
nop
mov rdi, OFFSET arrA
mov rsi, OFFSET arrB
mov rbx, OFFSET arrC
mov rdx, OFFSET string
mov rcx, LENGTHOF arrA
mov rax, 0
L1:
mov rax, [rdi]
mov rax, [rsi]
add rdi, TYPE arrA
add rsi, TYPE arrB
mov [rbx], rax
add rbx, TYPE arrC
call WriteHex64
call WriteString
loop L1
nop
mov ecx, 0
call ExitProcess
main ENDP
END
I am writing a NASM Assembly program on Windows to get the user to enter in two single digit numbers, add these together and then output the result. I am trying to use the Windows API for input and output.
Unfortunately, whilst I can get it to read in one number as soon as the program loops round to get the second the program ends rather than asking for the second value.
The output of the program shown below:
What is interesting is that if I input 1 then the value displayed is one larger so it is adding to something!
This holds for other single digits (2-9) entered as well.
I am pretty sure it is related to how I am using the ReadConsoleA function but I have hit a bit of a wall attempting to find a solution. I have installed gdb to debug the program and assembled it as follows:
nasm -f win64 -g -o task9.obj task9.asm
GoLink /console /entry _main task9.obj kernel32.dll
gdb task9
But I just get the following error:
"C:\Users\Administrator\Desktop/task9.exe": not in executable format: File format not recognized
I have since read that NASM doesn't output the debug information needed for the Win64 format but I am not 100% sure about that. I am fairly sure I have the 64-bit version of GDB installed:
My program is as follows:
extern ExitProcess ;windows API function to exit process
extern WriteConsoleA ;windows API function to write to the console window (ANSI version)
extern ReadConsoleA ;windows API function to read from the console window (ANSI version)
extern GetStdHandle ;windows API to get the for the console handle for input/output
section .data ;the .data section is where variables and constants are defined
STD_OUTPUT_HANDLE equ -11
STD_INPUT_HANDLE equ -10
digits db '0123456789' ;list of digits
input_message db 'Please enter your next number: '
length equ $-input_message
section .bss ;the .bss section is where space is reserved for additional variables
input_buffer: resb 2 ;reserve 64 bits for user input
char_written: resb 4
chars: resb 1 ;reversed for use with write operation
section .text ;the .text section is where the program code goes
global _main ;tells the machine which label to start program execution from
_num_to_str:
cmp rax, 0 ;compare value in rax to 0
jne .convert ;if not equal then jump to label
jmp .output
.convert:
;get next digit value
inc r15 ;increment the counter for next digit
mov rcx, 10
xor rdx, rdx ;clear previous remainder result
div rcx ;divide value in rax by value in rcx
;quotient (result) stored in rax
;remainder stored in rdx
push rdx ;store remainder on the stack
jmp _num_to_str
.output:
pop rdx ;get the last digit from the stack
;convert digit value to ascii character
mov r10, digits ;load the address of the digits into rsi
add r10, rdx ;get the character of the digits string to display
mov rdx, r10 ;digit to print
mov r8, 1 ;one byte to be output
call _print
;decide whether to loop
dec r15 ;reduce remaining digits (having printed one)
cmp r15, 0 ;are there digits left to print?
jne .output ;if not equal then jump to label output
ret
_print:
;get the output handle
mov rcx, STD_OUTPUT_HANDLE ;specifies that the output handle is required
call GetStdHandle ;returns value for handle to rax
mov rcx, rax
mov r9, char_written
call WriteConsoleA
ret
_read:
;get the input handle
mov rcx, STD_INPUT_HANDLE ;specifies that the input handle is required
call GetStdHandle
;get value from keyboard
mov rcx, rax ;place the handle for operation
mov rdx, input_buffer ;set name to receive input from keyboard
mov r8, 2 ;max number of characters to read
mov r9, chars ;stores the number of characters actually read
call ReadConsoleA
movzx r12, byte[input_buffer]
ret
_get_value:
mov rdx, input_message ;move the input message into rdx for function call
mov r8, length ;load the length of the message for function call
call _print
xor r8, r8
xor r9, r9
call _read
.end:
ret
_main:
mov r13, 0 ;counter for values input
mov r14, 0 ;total for calculation
.loop:
xor r12, r12
call _get_value ;get value from user
sub r12, '0' ;convert char to integer
add r14, r12 ;add value to total
;decide whether to loop for another character or not
inc r13
cmp r13, 2
jne .loop
;convert total to ASCII value
mov rax, r14 ;num_to_str expects total in rax
mov r15, 0 ;num_to_str uses r15 as a counter - must be initialised
call _num_to_str
;exit the program
mov rcx, 0 ;exit code
call ExitProcess
I would really appreciate any assistance you can offer either with resolving the issue or how to resolve the issue with gdb.
I found the following issues with your code:
Microsoft x86-64 convention mandates rsp be 16 byte aligned.
You must reserve space for the arguments on the stack, even if you pass them in registers.
Your chars variable needs 4 bytes not 1.
ReadConsole expects 5 arguments.
You should read 3 bytes because ReadConsole returns CR LF. Or you could just ignore leading whitespace.
Your _num_to_str is broken if the input is 0.
Based on Jester's suggestions this is the final program:
extern ExitProcess ;windows API function to exit process
extern WriteConsoleA ;windows API function to write to the console window (ANSI version)
extern ReadConsoleA ;windows API function to read from the console window (ANSI version)
extern GetStdHandle ;windows API to get the for the console handle for input/output
section .data ;the .data section is where variables and constants are defined
STD_OUTPUT_HANDLE equ -11
STD_INPUT_HANDLE equ -10
digits db '0123456789' ;list of digits
input_message db 'Please enter your next number: '
length equ $-input_message
NULL equ 0
section .bss ;the .bss section is where space is reserved for additional variables
input_buffer: resb 3 ;reserve 64 bits for user input
char_written: resb 4
chars: resb 4 ;reversed for use with write operation
section .text ;the .text section is where the program code goes
global _main ;tells the machine which label to start program execution from
_num_to_str:
sub rsp, 32
cmp rax, 0
jne .next_digit
push rax
inc r15
jmp .output
.next_digit:
cmp rax, 0 ;compare value in rax to 0
jne .convert ;if not equal then jump to label
jmp .output
.convert:
;get next digit value
inc r15 ;increment the counter for next digit
mov rcx, 10
xor rdx, rdx ;clear previous remainder result
div rcx ;divide value in rax by value in rcx
;quotient (result) stored in rax
;remainder stored in rdx
sub rsp, 8 ;add space on stack for value
push rdx ;store remainder on the stack
jmp .next_digit
.output:
pop rdx ;get the last digit from the stack
add rsp, 8 ;remove space from stack for popped value
;convert digit value to ascii character
mov r10, digits ;load the address of the digits into rsi
add r10, rdx ;get the character of the digits string to display
mov rdx, r10 ;digit to print
mov r8, 1 ;one byte to be output
call _print
;decide whether to loop
dec r15 ;reduce remaining digits (having printed one)
cmp r15, 0 ;are there digits left to print?
jne .output ;if not equal then jump to label output
add rsp, 32
ret
_print:
sub rsp, 40
;get the output handle
mov rcx, STD_OUTPUT_HANDLE ;specifies that the output handle is required
call GetStdHandle ;returns value for handle to rax
mov rcx, rax
mov r9, char_written
mov rax, qword 0 ;fifth argument
mov qword [rsp+0x20], rax
call WriteConsoleA
add rsp, 40
ret
_read:
sub rsp, 40
;get the input handle
mov rcx, STD_INPUT_HANDLE ;specifies that the input handle is required
call GetStdHandle
;get value from keyboard
mov rcx, rax ;place the handle for operation
xor rdx, rdx
mov rdx, input_buffer ;set name to receive input from keyboard
mov r8, 3 ;max number of characters to read
mov r9, chars ;stores the number of characters actually read
mov rax, qword 0 ;fifth argument
mov qword [rsp+0x20], rax
call ReadConsoleA
movzx r12, byte[input_buffer]
add rsp, 40
ret
_get_value:
sub rsp, 40
mov rdx, input_message ;move the input message into rdx for function call
mov r8, length ;load the length of the message for function call
call _print
call _read
.end:
add rsp, 40
ret
_main:
sub rsp, 40
mov r13, 0 ;counter for values input
mov r14, 0 ;total for calculation
.loop:
call _get_value ;get value from user
sub r12, '0' ;convert char to integer
add r14, r12 ;add value to total
;decide whether to loop for another character or not
inc r13
cmp r13, 2
jne .loop
;convert total to ASCII value
mov rax, r14 ;num_to_str expects total in rax
mov r15, 0 ;num_to_str uses r15 as a counter - must be initialised
call _num_to_str
;exit the program
mov rcx, 0 ;exit code
call ExitProcess
add rsp, 40
ret
As it turned out I was actually missing a 5th argument in the WriteConsole function as well.
I am attempting to convert an existing assembly program that I have so that it works on Windows. It should ask the user for their name and then output "Hello name". I pretty much have it working but there is a problem with getting the number of characters actually read from the ReadConsole call.
My program is:
extern ExitProcess ;windows API function to exit process
extern WriteConsoleA ;windows API function to write to the console window (ANSI version)
extern ReadConsoleA ;windows API function to read from the console window (ANSI version)
extern GetStdHandle ;windows API to get the for the console handle for input/output
section .data ;the .data section is where variables and constants are defined
hello db 'Hello ' ;db stands for 'define byte'
length equ $-hello ;get the length of the hello
;$ is the current memory location
;$-hello means the difference between the current location and
;where hello started
enter_name db 'Please enter your name: '
name_len equ $-enter_name
chars dd 0
section .bss ;the .bss section is where space is reserved for additional variables
name:
resb 30 ;reserve 30 bytes of space for the name
char_written:
resb 4
section .text ;the .text section is where the program code goes
global _main ;tells the machine which label to start program execution from
_main:
;get the output handle
mov rcx, -11 ;specifies that the output handle is required
call GetStdHandle ;returns value for handle to rax
;print message asking user for their name
mov rcx, rax
mov rdx, enter_name ;load the address of enter name message into rsi
mov r8, name_len ;move the length into the rdx register
mov r9, char_written
call WriteConsoleA
;get the input handle
mov rcx, -10 ;specifies that the input handle is required
call GetStdHandle
;get value from keyboard
mov rcx, rax ;place the handle for operation
mov rdx, name ;set name to receive input from keyboard
mov r8, 30 ;max number of characters to read
mov r9, chars ;stores the number of characters actually read
call ReadConsoleA
;print 'hello' + user's name message
;get the output handle
mov rcx, -11 ;specifies that the output handle is required
call GetStdHandle ;returns value for handle to rax
;print message asking user for their name
mov rcx, rax
mov rdx, hello ;load the address of enter name message into rsi
mov r8, length ;move the length into the rdx register
mov r9, char_written
call WriteConsoleA
mov rcx, -11 ;specifies that the output handle is required
call GetStdHandle ;returns value for handle to rax
;print name
mov rcx, rax
mov rdx, name ;load the address of enter name message into rsi
mov r8, chars ;move the length into the rdx register
mov r9, char_written
call WriteConsoleA
;exit the program
mov rcx, 0 ;exit code?
call ExitProcess
The way that I have read the Windows API documentation on ReadConsole is that it requires the following parameters:
Output Handle
Place to put the read data
A value for the number of characters to read
Place to put the actual number of characters read
I have defined a label name to receive the characters and chars to receive the actual number of characters.
The read call looks like this:
;get the input handle
mov rcx, -10 ;specifies that the input handle is required
call GetStdHandle
;get value from keyboard
mov rcx, rax ;place the handle for operation
mov rdx, name ;set name to receive input from keyboard
mov r8, 30 ;max number of characters to read
mov r9, chars ;stores the number of characters actually read
call ReadConsoleA
My assumption is that if the name entered is Anna then the value 4 or 5 (including new line character) should be stored in chars. This does not seem to be the case as when I attempt to output the name I get nothing with this code:
mov rcx, -11 ;specifies that the output handle is required
call GetStdHandle ;returns value for handle to rax
;print name
mov rcx, rax
mov rdx, name ;load the address of enter name message into rsi
mov r8, chars ;move the length into the rdx register
mov r9, char_written
call WriteConsoleA
But if I switch the chars label out for a value it works as expected i.e.
mov rcx, -11 ;specifies that the output handle is required
call GetStdHandle ;returns value for handle to rax
;print name
mov rcx, rax
mov rdx, name ;load the address of enter name message into rsi
mov r8, 4 ;move the length into the rdx register
mov r9, char_written
call WriteConsoleA
Obviously I am doing something wrong with this buffer - any assistance would be much appreciated.
I am trying to write a simple assembly program to add two numbers together. I want the user to be able to enter the values. The problem I am encountering is that when I display a string message and then read a value in, the next time the string is required the first x characters of the string have been overwritten by the data that was entered by the user.
My assumption is that this is related to the use of LEA to load the string into the register. I have been doing this because Macho64 complains if a regular MOV instruction is used in this situation (something to do with addressing space in 64-bits on the Mac).
My code is as follows:
section .data ;this is where constants go
input_message db 'Please enter your next number: '
length equ $-input_message
section .text ;declaring our .text segment
global _main ;telling where program execution should start
_main: ;this is where code starts getting executed
mov r8, 0
_loop_values:
call _get_value
call _write
inc r8 ;increment the loop counter
cmp r8, 2 ;compare loop counter to zero
jne _loop_values
call _exit
_get_value:
lea rcx, [rel input_message] ;move the input message into rcx for function call
mov rdx, length ;load the length of the message for function call
call _write
call _read
ret
_read:
mov rdx, 255 ;set buffer size for input
mov rdi, 0 ;stdout
mov rax, SYSCALL_READ
syscall
mov rdx, rax ;move the length from rax to rdx
dec rdx ;remove new line character from input length
mov rcx, rsi ;move the value input from rsi to rcx
ret
_write:
mov rsi, rcx ;load the output message
;mov rdx, rax
mov rax, SYSCALL_WRITE
syscall
ret
_exit:
mov rax, SYSCALL_EXIT
mov rdi, 0
syscall
The program loops twice as it should. The first time I get the following prompt:
Please enter your next number:
I would the enter something like 5 (followed by the return key)
The next prompt would be:
5
ease enter your next number:
Any assistance would be much appreciated.
I think all 64-bit code on Mac is required to be rip relative.
Absolute addresses are not supported. in this type of addressing you address your symbol relative to rip.
NASM documentation says:
default abs
mov eax,[foo] ; 32−bit absolute disp, sign−extended
mov eax,[a32 foo] ; 32−bit absolute disp, zero−extended
mov eax,[qword foo] ; 64−bit absolute disp
default rel
mov eax,[foo] ; 32−bit relative disp
mov eax,[a32 foo] ; d:o, address truncated to 32 bits(!)
mov eax,[qword foo] ; error
mov eax,[abs qword foo] ; 64−bit absolute disp
and you can also see this question.
I have a compiled assembly program and I'm asked to modify it by removing the last row seen in the cmd prompt. I'm new at assembly so I can't find a solution.
When you run it 5 rows appear, and I'm trying to delete the row below;
Press [q]uit [e]xecute [c]lear:
;Serhad Ali Turhan 040060390
.8086
.MODEL small
.STACK 256
.DATA
;~~~~~~ Declarations ~~~~~~~~
CR equ 13d
LF equ 10d
cPrompt DB 'Press [q]uit [e]xecute [c]lear: $'
cName DB 'SERHAD ALI TURHAN',CR,LF,'$'
cNum DB 'Electronical Engineering',CR,LF,'$'
cSch DB 'ITU Ayazaga Kampusu 34469 Maslak-ISTANBUL',CR,LF,'$'
vDW DB 0 ;Day of the Week
vMon DB 0 ;Month
vDM DB 0 ;Day of the month
cDW0 DB 'SUNDAY$ ' ;All days are 10 bytes
cDW1 DB 'MONDAY$ '
cDW2 DB 'TUESDAY$ '
cDW3 DB 'WEDNESDAY$'
cDW4 DB 'THURSDAY$ '
cDW5 DB 'FRIDAY$ '
cDW6 DB 'SATURDAY$ '
vI2S_I DW 0 ;2 bytes
vI2S_S DB ?,?,?,?,'$' ;4 bytes
cExcode DB 0
;~~~~~~~~ Main Program ~~~~~~~~
.CODE
MAIN PROC
mov ax,#data ; Initialize DS to address
mov ds,ax ; of data segment
call pClr
jmp pExecute
jmp pMenu ;
;~~~~~ Menu ~~~~~
pMenu:
lea dx,cPrompt ;
call puts ;
call getc ;AL has user selection
push ax ;Store it
call pNL ;
pop ax ;
cmp al,'q' ;al?=q
je lQuit ;Quit
cmp al,'c' ;al?=c
je lClr ;Clear screen
cmp al,'e' ;
je pExecute ;
jmp pMenu ;
lClr:
call pClr
jmp pMenu ;
lQuit:
call pQuit
;~~~~~~~~
pExecute:
call pInfo
call pClock
call pDate
call pNL
jmp pMenu ;
pInfo:
lea dx,cName ;
call puts ;Display Name
lea dx,cNum
call puts ;Display Department
lea dx,cSch
call puts ;Display School Address
ret
pClock:
mov ah,2ch ;get time
int 21h
push dx
push cx
mov al,ch ;ch->hour
call pDisp
mov dl,':'
call putc
pop ax ;cl->minute
call pDisp
mov dl,':'
call putc
pop ax ;dh->seconds
mov al,ah
call pDisp
call pNL
ret
pDate:
mov ah,2ah ;get date
int 21h
mov vDW,al ;Store day of week
mov vMon,dh ;Store month
mov vDM,dl ;Store day of month
mov vI2S_I,cx ;Year will be stored in
call pI2S ;vI2S_s as ASCII
mov al,vDM ;Print day of month
call pDisp
mov dl,'.'
call putc
mov al,vMon ;Print month
call pDisp
mov dl,'.'
call putc
lea dx,vI2S_S ;Print year
call puts
mov dl,'-'
call putc
call pDW ;Print day of week
ret
pDW:
mov al,vDW
mov bl,10 ;All days are 10 bytes
mul bl
mov ah,0
mov bx,ax
lea dx,cDW0[bx]
call puts
ret
pDisp:
xor ah,0 ;
aam
add ax,3030h ;
push ax
mov dl,ah ;
call putc
pop dx
call putc
ret
;vI2S_I=1000*vI2S_S[0]+100*vI2S_S[1]+10*vI2S_S[2]+vI2S_S[3]
pI2S: ;intToStr
mov cx,1000 ;
mov ax,vI2S_I ;
mov dx,0
div cx ;
add al,'0'
mov vI2S_S,al ;
mov cx,100 ;
mov ax,dx
mov dx,0
div cx
add al,'0'
mov vI2S_s[1],al
mov cx,10 ;
mov ax,dx
mov dx,0
div cx
add al,'0'
mov vI2S_s[2],al
add dl,'0'
mov vI2S_s[3],dl ;
ret
;~~~~~~~~ Screen I/O Functions ~~~~~~~~
getc:
mov ah,1h ;read character from keyboard
int 21h ;to al
ret ;
putc:
mov ah,2h ;display character
int 21h ;at dl
ret ;
puts:
mov ah,9h ;display string terminated by '$'
int 21h ;at the adress dx
ret ;
;~~ Clear screen ~~~~
pClr:
mov ax,03h ;
int 10h ;
ret ;
;~~ New Line ~~~~~~~
pNL:
mov dl,CR ;
call putc ;
mov dl,LF ;
call putc ;
ret
;~~~~~~~~~ Exit ~~~~~~~~~~~~~~~
;return to DOS
pQuit:
mov ah,4Ch ; DOS function: Exit program
mov al,cExcode ; Return exit code value
int 21h ; Call DOS. Terminate program
MAIN ENDP
END MAIN ; End of program / entry point
I'll offer some guiding questions:
Does the text that you're looking to prevent appear in the code?
Is it identified in some way?
Does that identifier exist elsewhere in the code?
Where does that usage take you?
There are only a handfull of instructions that you care about. These questions will help you find them, and hopefully understand which one to delete.