How to read input from STDIN in x86_64 assembly? -
i trying learn x86_64 assembly , trying standard input output today , stumbled upon post learning assembly - echo program name how same reading input stdin (using syscall instruction)? if know input integer , want read register?
edit: @daniel kozar's answer below helped me understand how stdin , stdout stuff work syscall instruction on linux. attempted write small program, reads number console input , prints ascii character corresponding number. if type 65 input, should output. , new line character. if @ all, helps 1 else :-)
section .text global _start _start: mov rdi, 0x0 ; file descriptor = stdin = 0 lea rsi, [rsp+8] ; buffer = address store bytes read mov rdx, 0x2 ; number of bytes read mov rax, 0x0 ; syscall number reading stdin syscall ; make syscall xor rax, rax ; clear off rax mov rbx, [rsp+8] ; read first byte read rsp+8 stdin call rbp sub rbx, 0x30 ; since read character, obtained ascii value, subtract 0x30 number , rbx, 0xff ; ensures other last byte set 0 while last byte mov rax, rbx ; move value rax since want store final result in rax shl rbx, 0x1 ; need multiply 10 can add digits read multiplying number 2 , 8 , adding them up, multiply 2 here shl rax, 0x3 ; multiply 8 here add rax, rbx ; add 8 times multiplied value 2 times multiplied value 10 times multiplied value mov rbx, [rsp+9] ; read next byte (or digit) sub rbx, 0x30 ; again digit value ascii value of digit's character , rbx, 0xff ; clear higher bytes add rax, rbx ; add rax unit's place value mov [rsp+8], rax ; move entire byte rax mov rdi, 0x1 ; file descriptor = stdout lea rsi, [rsp+8] ; buffer = address write console mov rdx, 0x1 ; number of bytes write mov rax, 0x1 ; syscall number writing stdout syscall ; make syscall xor rax, rax ; clear off rax mov rax, 0xa ; move new line character rax mov [rsp+8], rax ; put on stack mov rdi, 0x1 ; file descriptor = stdout lea rsi, [rsp+8] ; buffer = address write console mov rdx, 0x1 ; number of bytes write mov rax, 0x1 ; syscall number writing stdout syscall ; make syscall mov rdi, 0 ; set exit status = 0 mov rax, 60 ; syscall number exit syscall ; make syscall
edit 2: here attempt read unsigned 32-bit decimal integer standard input, store integer computations , write std out.
section .text global _start _start: ;read stdin mov rdi, 0x0 ; file descriptor = stdin = 0 lea rsi, [rsp+8] ; buffer = address store bytes read mov rdx, 0xa ; number of bytes read mov rax, 0x0 ; syscall number reading stdin syscall ; make syscall ; ascii decimal conversion xor rax, rax ; clear off rax mov rbx, 0x0 ; initialize counter stores number of bytes in string representation of integer lea rsi, [rsp+8] ; address on stack first ascii byte of integer stored. rnext: mov rcx, [rsi] ; read byte on stack @ address represented rsi cmp rcx, 0xa ; check if newline character je return ; if done cmp rbx, 0xa ; or check if have read 10 bytes (the largest 32 bit number contains 10 digits, have process @ 10 bytes jg return ; if done sub rcx, 0x30 ; byte read, subtract 0x30/48 value ascii code. 0 == 0x30 in ascii, 1 == 0x31 in ascii , on. , rcx, 0xff ; clear off higher order bytes ensure there no interference mov rdx, rax ; need multiple 10 next byte goes unit's place , byte becomes ten's value. make copy shl rax, 0x3 ; multiply original 8 (shift left 3 multiply 8) shl rdx, 0x1 ; multiply copy 2 (shift left 1 multiply 2) add rax, rdx ; add these * 8 + * 2 * 10. add rax, rcx ; add digit @ units place original number add rsi, 1 ; advance memory address 1 read next byte inc rbx ; increment digit counter jmp rnext ; loop until have read digits or max reached. return: push rax ; push read number on stack ; write new line mov rax, 0xa ; move new line character rax mov [rsp+8], rax ; put on stack mov rdi, 0x1 ; file descriptor = stdout lea rsi, [rsp+8] ; buffer = address write console mov rdx, 0x1 ; number of bytes write mov rax, 0x1 ; syscall number writing stdout syscall ; make syscall ; convert decimal bytes xor rdx, rdx ; clear rdx stores obtains single digit of number convert ascii bytes mov r8, 0x0 ; initialize counter containing number of digits pop rax ; pop read number stack mov rbx, 0xa ; store divisor 10 decimals (base-10) in rbx. rbx divisor. wnext: div rbx ; divide number in rdx:rax rbx remainder in rdx add rdx, 0x30 ; add 0x30 ascii byte equivalent of remainder digit in number written display. push rdx ; push byte stack. because, individial digit bytes in reverse order. reverse order use stack xor rdx, rdx ; clear rdx preparing next division inc r8 ; increment digits counter cmp rax, 0x0 ; continue until number becomes 0 when there no more digits write console. jne wnext ; loop until there aren't more digits. popnext: cmp r8, 0x0 ; check if counter contains number of digits write 0 jle endw ; if there no more digits write mov rdx, 0x1 ; number of bytes write mov rsi, rsp ; buffer = address write console mov rdi, 0x1 ; file descriptor = stdout mov rax, 0x1 ; syscall number writing stdout syscall ; make syscall dec r8 ; decrement counter pop rbx ; pop current digit written display preparing stack pointer next digit. jmp popnext ; loop until counter contains number of digits goes down 0. endw: ; write new line xor rax, rax ; clear off rax mov rax, 0xa ; move new line character rax mov [rsp+9], rax ; put on stack mov rdi, 0x1 ; file descriptor = stdout lea rsi, [rsp+9] ; buffer = address write console mov rdx, 0x1 ; number of bytes write mov rax, 0x1 ; syscall number writing stdout syscall ; make syscall ; exit mov rdi, 0 ; set exit status = 0 mov rax, 60 ; syscall number exit syscall ; make syscall
first of : there no variables in assembly. there labels kind of data. data is, design, untyped - @ least in real assemblers, not hla (e.g. masm).
reading standard input achieved using system call read
. assume you've read post mentioned , know how call system calls in x64 linux. assuming you're using nasm (or resembles syntax), , want store input stdin
@ address buffer
, have reserved bufsize
bytes of memory, executing system call :
xor eax, eax ; rax <- 0 (write syscall number) xor edi, edi ; rdi <- 0 (stdin file descriptor) mov rsi, buffer ; rsi <- address of buffer mov edx, bufsize ; rdx <- size of buffer syscall ; execute
upon returning, rax
contain result of syscall. if want know more how works, please consult man 2 read
.
parsing integer in assembly language not simple, though. since read
gives plain binary data appears on standard input, need convert integer value yourself. keep in mind type on keyboard sent application ascii codes (or other encoding might using - i'm assuming ascii here). therefore, need convert data ascii-encoded decimal binary.
a function in c converting such structure normal unsigned int :
unsigned int parse_ascii_decimal(char *str,unsigned int strlen) { unsigned int ret = 0, mul = 1; int = strlen-1; while(i >= 0) { ret += (str[i] & 0xf) * mul; mul *= 10; --i; } return ret; }
converting assembly (and extending support signed numbers) left exercise reader. :)
last not least - write
syscall requires pass pointer buffer data that's supposed written given file descriptor. therefore, if want output newline, there no other way create buffer containing newline sequence.
Comments
Post a Comment