I actually want to print the content of the dx register with nasm. Thereby the content is a 16 bit hex digit such as 0x12AB.
Therefore I've first implemented a function which is able to print a string:
print_string:
pusha
mov ah, 0xe
print_character:
mov al, [bx]
inc bx
or al, al
jz print_done
int 0x10
jmp print_character
print_done:
popa
ret
You can use this function in this way:
mov bx, MSG
call print_string
MSG:
db 'Test',0
Now i want to have a function, which converts the hex to a string, so that print_string is capable to print it. I was thinking about something like that:
print_hex:
pusha
mov bx, HEX_OUT
; HEX_OUT is a kind of template string
; now i want to get the hex of dx into this template in order to be able to print it
; However I'm not sure how to manage this
call print_string
popa
ret
HEX_OUT:
db '0x0000', 0
Unfortunately I'm not sure how I get the hex from dx into bx, respectively the HEX_OUT. Can someone help me or does someone have an idea?
I want to use it at the end like this:
mov dx, 0x12AA
call print_hex
Thanks you already in advance!
UPDATE:
As mentioned I could achieve the separating and printing like this:
print_hex:
pusha
mov bx, PREFIX
call print_string
next_character:
mov bx, dx
and bx, 0xf000
shr bx, 4
add bh, 0x30
cmp bh, 0x39
jg add_7
print_character_hex:
mov al, bh
mov ah, 0x0e
int 0x10
shl dx, 4
or dx, dx
jnz next_character
popa
ret
add_7
add bh, 0x7
jmp print_character_hex
PREFIX:
db '0x', 0
I tried something like this to print it with my function and the buffer:
print_hex:
;Added this here
mov cx, HEX_OUT + 2
print_character_hex:
mov [cx], bh
Though I can't assemble this due to "invalid effective address". What do I need to do in order to accomplish this?
This adapts the accepted answer to include Kyle G.'s fix for the 0 issue.
; vim: :set ft=nasm:
[bits 16]
; subroutine to print a hex number.
;
; ```
; dx = the hexadecimal value to print
; ```
;
; Usage
;
; ```
; mov dx, 0x1fb6
; call print_hex
; ```
;
; used as an answer here: https://stackoverflow.com/a/27686875/7132678
;
print_hex:
; push all registers onto the stack
pusha
; use si to keep track of the current char in our template string
mov si, HEX_OUT + 2
; start a counter of how many nibbles we've processed, stop at 4
mov cx, 0
next_character:
; increment the counter for each nibble
inc cx
; isolate this nibble
mov bx, dx
and bx, 0xf000
shr bx, 4
; add 0x30 to get the ASCII digit value
add bh, 0x30
; If our hex digit was > 9, it'll be > 0x39, so add 7 to get
; ASCII letters
cmp bh, 0x39
jg add_7
add_character_hex:
; put the current nibble into our string template
mov [si], bh
; increment our template string's char position
inc si
; shift dx by 4 to start on the next nibble (to the right)
shl dx, 4
; exit if we've processed all 4 nibbles, else process the next
; nibble
cmp cx, 4
jnz next_character
jmp _done
_done:
; copy the current nibble's ASCII value to a char in our template
; string
mov bx, HEX_OUT
; print our template string
call print_string
; pop all arguments
popa
; return from subroutine
ret
add_7:
; add 7 to our current nibble's ASCII value, in order to get letters
add bh, 0x7
; add the current nibble's ASCII
jmp add_character_hex
; our global template string. We'll replace the zero digits here with the
; actual nibble values from the hex input.
HEX_OUT:
db '0x0000', 0
Also, worth noting that this is an exercise from https://www.cs.bham.ac.uk/~exr/lectures/opsys/10_11/lectures/os-dev.pdf, section 3.5.1, question 5, page 23.
All right, I was able to manage it. Thank you for your help! Here is the working code:
print_hex:
pusha
mov si, HEX_OUT + 2
next_character:
mov bx, dx
and bx, 0xf000
shr bx, 4
add bh, 0x30
cmp bh, 0x39
jg add_7
add_character_hex:
mov al, bh
mov [si], bh
inc si
shl dx, 4
or dx, dx
jnz next_character
mov bx, HEX_OUT
call print_string
popa
ret
add_7:
add bh, 0x7
jmp add_character_hex
HEX_OUT:
db '0x0000', 0
You can use this code to print a hex number
mov cx,12
mov dx,1f34h
st:
push dx
shr dx,cl
and dl,15
print:
add dl, 0x30
cmp dl, 0x39
jle go
add dl, 0x7
go:
mov ah, 0x2
int 0x21
sub cl,4
pop dx
cmp cl,0
jl end_code
jmp st
end_code:
Related
This is my first attempt in 64-bit assembly under Linux. I am using FASM.
I am converting a 64-bit register hex value to string. It is working fine until it reaches the final digit. I can't figure out exactly what's wrong with my code. Maybe there is something about 64-programming that I don't know or with the syscall (I am a linux noob as well)
format ELF64 executable 3
entry start
segment readable executable
start:
mov rax,3c5677h ;final '7' is not displayed
push rax
call REG
;call line
xor edi,edi ;exit
mov eax,60
syscall
;----------------------------------
REG:push rbp ;stack frame setup
mov rbp,rsp
sub rsp,8 ;space for local char
mov rax,[rbp+16];arg
lea r9,[rsp-8] ;local char
mov rcx,16 ;divisor
mov rsi,16 ;16 hex digits for register
.begin: ;get the digit
xor rdx,rdx ;by division
div rcx ;of 16
push rdx ;from back to front
dec rsi
test rsi,rsi
jz .disp
jmp .begin
.disp: ;convert and display digit
inc rsi
pop rax ;In reverse order
add rax,30h ;convert digit to string
cmp rax,39h ;if alpha
jbe .normal
add rax,7 ;add 7
.normal:
mov [r9],rax ;copy the value
push rsi ;save RSI for syscall
mov rsi,r9 ;address of char
mov edx,1 ;size
mov edi,1 ;stdout
mov eax,1 ;sys_write
syscall
pop rsi ;restore RSI for index
cmp rsi,16
je .done
jmp .disp
.done:
add rsp,8 ;stack balancing
pop rbp
ret
Thanks in advance for your help.
I believe the problem printing the last digit comes from how you load r9. If on entry, rsp was 100. You subtract 8 (rsp = 92), then load r9 with rsp - 8 (r9 = 84). Presumably you meant r9 to 100, so try changing that to:
lea r9, [rsp+8]
For a more efficient solution, how about something more like this (assumes value in rbx):
mov r9, 16 ; How many digits to print
mov rsi, rsp ; memory to write digits to
sub rsp, 8 ; protect our stack
mov edx, 1 ; size is always 1
mov edi, 1 ; stdout is always 1
.disp:
rol rbx, 4 ; Get the next nibble
mov cl, bl ; copy it to scratch
and cl, 15 ; mask out extra bits
add cl, 0x30 ; Convert to char
cmp cl, 0x39 ; if alpha
jbe .normal
add cl, 7 ; Adjust for letters
.normal:
mov [rsi], cl ; copy the value
mov eax, 1 ; sys_write
syscall ; overwrites rcx, rax, r11
dec r9 ; Finished a digit
jnz .disp ; Are we done?
add rsp, 8 ; Done with the memory
I have a problem with a TASM based assembly code. I would like to convert a string in the "string" pointer into a decimal number and then print it as a hexadecimal one. However the code prints only the first 2 characters from the right correctly. What may be wrong with this?
concd SEGMENT
ASSUME cs: concd
ORG 100h
main:
mov ax, 0
mov bx, offset string
mov cx, 0
mov dx, 0
mov si, 0
jump:
mov cx, [bx + si]
cmp cx, 0h ;checking if we reached the end of the string
jz exit
mov dx, ax
mov cx, 9
mult:
add ax, dx ;loop for multiplying by 10
loop mult
mov cx, [bx + si]
sub cx, 30h
add ax, cx
inc si
jmp jump
exit:
call hex
mov ah, 04Ch
mov al, 0
int 21h
hex PROC
mov dx, ax
mov cl, 12
jump2:
mov ax, dx
shr ax, cl
and ax, 15
cmp ax, 9
jng t
add ax, 7h
t: add ax, 30h
call putc
sub cl, 4
jnc jump2
RET
hex ENDP
putc PROC
mov ah, 0Eh
int 10h
RET
putc ENDP
string DB "1234", 0h
concd ENDS
END main
I made some small changes, and now it works.
I commented with "phase 1" the changes that it actually did get a character abd did something, and with "phase 2" the changes that corrected the conversion.
Now it's OP's time to figure out what was wrong.
concd SEGMENT
ASSUME cs: concd
ORG 100h
main:
mov ax, cs ;; added - phase 1
mov ds, ax ;; added - phase 1
mov ax, 0
mov bx, offset string
mov cx, 0
mov dx, 0
mov si, 0
jump:
mov ch, 0 ;; Added - phase 2
mov cl, [bx + si] ;; added - phase 2
;mov cx, [bx + si] ;; removed - phase 2
cmp cx, 0h ;checking if we reached the end of the string
jz exit
mov dx, ax
mov cx, 9
mult: ; ax contains the number
;; removed the next 2 lines - phase 2
;add ax, dx ;loop for multiplying by 10
;loop mult
;; added the next 2 lines - phase 2
mov cl, 10
mul cl ; multiply by 10
mov cx, [bx + si]
sub cx, 30h
add ax, cx
inc si
jmp jump
exit:
call hex
mov ah, 04Ch
mov al, 0
int 21h
hex PROC
mov dx, ax
mov cl, 12
jump2:
mov ax, dx
shr ax, cl
and ax, 15
cmp ax, 9
jng t
add ax, 7h
t: add ax, 30h
call putc
sub cl, 4
jnc jump2
RET
hex ENDP
putc PROC
mov ah, 0Eh
int 10h
RET
putc ENDP
string DB "1234", 0h
concd ENDS
END main
I am trying to get an input from the keyboard, and then output it to the screen.
My code;
BITS 16 ;Set code generation to 16 bit mode
ORG 0x0100;
SECTION .text;
MAIN:
mov SI, MyArray
call GetString
call Putln
call PutString
jmp Exit;
GetString:
call Getch ; get the character stored in DL
cmp dl, 0dh ; if Enter is pressed Exit the subroutine
je Return
; call Putch ;*commeted out to see if putsring works* ; output the character on screen
stosb ; store the character in al to [di] and increment di by 1
jmp GetString ; loop back to GetString
Return:
mov al, 0 ; terminate array with a 0
stosb
ret
PutString:
cld
lodsb ; load the character in [si] to al and increment si by 1
cmp al, 0
jz Return2
mov dl, al
call Putch
jmp PutString ; loop back to PutString
Return2:
Ret
Getch:
push di
mov ah, 7 ; keyboard input subprogram without echo
int 21h ; read the character into al
mov dl, al
pop di
RET ; return
Putch:
push di
mov ah, 2h ; display subprogram
INT 21H ;read the characters from al
pop di
RET ; Return
Putln: ;new line
mov ah, 2
mov dl, 0DH ;Carriage return
int 21h
mov dl, 0AH ;Line feed
int 21H
RET ; return
Exit:
MOV AH,04Ch ; Select exit function
MOV AL,00 ; Return 0
INT 21h ; Call the interrupt to exit
SECTION .bss
MyArray resb 256
However I cannot get PutString to work properly. It is printing the same ASCII characters no matter what is typed on the keyboard.
Any help would be appreciated!
I don't see you initializing DI anywhere. It should probably be set to point to MyArray just like SI, otherwise your STOSB will just write to some random location.
These are the only changes you need to make:
...
;;;; mov SI, MyArray
mov DI, MyArray ;;;; stosb uses DI
call GetString
call Putln
mov SI, MyArray ;;;; lodsb uses SI
...
I don't know much of assembly language but I tried making this palindrome and it's quite hard. First I have to enter a string then show its original and reversed string then show if its a palindrome or not.
I already figured out how to show the reverse string by pushing and popping it through a loop from what I have read in another forum and figured it out
now the only problem for me is to compare the reverse string and original string to check if its a palindrome or not.
call clearscreen
mov dh, 0
mov dl, 0
call cursor
mov ah, 09h
mov dx, offset str1
int 21h
palin db 40 dup(?)
mov ah, 0ah
mov palin, 40
mov dx, offset palin
int 21h
mov dh, 1
mov dl, 0
call cursor
mov ah, 09h
mov dx, offset str2
int 21h
mov si, 2
forward:
mov al, palin + si
cmp al, 13
je palindrome
mov ah, 02h
mov dl, al
push ax 'push the letter to reverse
int 21h
inc si
jmp forward
palindrome:
mov dh, 2
mov dl, 0
call cursor
mov ah, 09h
mov dx, offset str3
int 21h
mov cx, 40 'pop each letter through a loop to show its reverse
reverse:
mov ah, 02h
pop ax
mov dl, al
int 21h
loop reverse
int 20h
clearscreen:
mov ax, 0600h
mov bh, 0Eh
mov cx, 0
mov dx, 8025
int 10h
ret
cursor:
mov ah, 02h
mov bh, 0
int 10h
ret
str1: db "Enter A String : $"
str2: db "Forward : $"
str3: db "Backward : $"
str4: db "Its a Palindrome! $"
str5: db "Not a Palindrome!$"
You have a string the user typed in, what you need to do is compare the first byte with the last byte, do the same for the 2nd one and the 2nd to last one. Keep doing this for the whole string. You also need the length of the string. To make life easier, you should convert the string to all UPPER case letters or all lowercase letters to make comparison easier.
It is unfortunate they are still teaching 16bit DOS code. This is a sample with the word defined in the data section. You will have to modify it to receive input and work on that string
.data
pal db "racecar"
pal_len equ $ - pal - 1
szYes db "yes$"
szNo db "no$"
.code
start:
mov ax,#data
mov ds,ax
call IsPalindrome
mov ah,4ch
int 21h
IsPalindrome:
lea si, pal
lea di, pal
add di, pal_len
mov cx, 0
CheckIt:
mov al, byte ptr [si]
mov dl, byte ptr [di]
cmp al, dl
jne No
inc si
dec di
inc cx
cmp cx, pal_len
jne CheckIt
mov ah,9
lea dx,szYes
int 21h
ret
No:
mov ah,9
lea dx,szNo
int 21h
ret
end start
For completeness and to bring us into the 21st century, 32bit NASM code:
section .data
fmt db "%s", 0
szPal db "RACECAR"
Pal_len equ $ - szPal - 1
szYes db "Yes", 10, 0
szNo db "No", 10, 0
extern printf, exit
global _start
section .text
_start:
call IsPalindrome
call exit
IsPalindrome:
mov ecx, 0
mov ebx, Pal_len
mov esi, szPal
.CheckIt:
mov al, byte [esi + ecx]
mov dl, byte [esi + ebx]
cmp al, dl
jne .No
inc ecx
dec ebx
jns .CheckIt
push szYes
push fmt
call printf
add esp, 4 * 2
mov eax, 1
jmp Done
.No:
push szNo
push fmt
call printf
add esp, 4 * 2
xor eax, eax
Done:
ret
I have writen this little experiement bootstrap that has a getline and print_string "functions". The boot stuff is taken from MikeOS tutorial but the rest I have writen myself. I compile this with NASM and run it in QEMU.
So the actual question: I've declared this variable curInpLn on line 6. What ever the user types is saved on that variable and then after enter is hit it is displayed to the user with some additional messages. What I'd like to do is to clear the contents of curInpLn each time the getline function is called but for some reason I can't manage to do that. I'm quite the beginner with Assmebly at the moment.
You can compile the code to bin format and then create a floppy image of it with: "dd status=noxfer conv=notrunc if=FILENAME.bin of=FILENAME.flp" and run it in qemu with: "qemu -fda FILENAME.flp"
BITS 16
jmp start
welcomeSTR: db 'Welcome!',0
promptSTR: db 'Please prompt something: ',0
responseSTR: db 'You prompted: ',0
curInpLn: times 80 db 0 ;this is a variable to hold the input 'command'
curCharCnt: dw 0
curLnNum: dw 1
start:
mov ax, 07C0h ; Set up 4K stack space after this bootloader
add ax, 288 ; (4096 + 512) / 16 bytes per paragraph
mov ss, ax
mov sp, 4096
mov ax, 07C0h ; Set data segment to where we're loaded
mov ds, ax
call clear_screen
lea bx, [welcomeSTR] ; Put string position into SI
call print_string
call new_line
.waitCMD:
lea bx, [promptSTR]
call print_string
call getLine ; Call our string-printing routine
jmp .waitCMD
getLine:
cld
mov cx, 80 ;number of loops for loopne
mov di, 0 ;offset to bx
lea bx, [curInpLn] ;the address of our string
.gtlLoop:
mov ah, 00h ;This is an bios interrupt to
int 16h ;wait for a keypress and save it to al
cmp al, 08h ;see if backspace was pressed
je .gtlRemChar ;if so, jump
mov [bx+di], al ;effective address of our curInpLn string
inc di ;is saved in bx, di is an offset where we will
;insert our char in al
cmp al, 0Dh ;see if character typed is car-return (enter)
je .gtlDone ;if so, jump
mov ah, 0Eh ;bios interrupt to show the char in al
int 10h
.gtlCont:
loopne .gtlLoop ;loopne loops until cx is zero
jmp .gtlDone
.gtlRemChar:
;mov [bx][di-1], 0 ;this needs to be solved. NASM gives error on this.
dec di
jmp .gtlCont
.gtlDone:
call new_line
lea bx, [responseSTR]
call print_string
mov [curCharCnt], di ;save the amount of chars entered to a var
lea bx, [curInpLn]
call print_string
call new_line
ret
print_string: ; Routine: output string in SI to screen
mov si, bx
mov ah, 0Eh ; int 10h 'print char' function
.repeat:
lodsb ; Get character from string
cmp al, 0
je .done ; If char is zero, end of string
int 10h ; Otherwise, print it
jmp .repeat
.done:
ret
new_line:
mov ax, [curLnNum]
inc ax
mov [curLnNum], ax
mov ah, 02h
mov dl, 0
mov dh, [curLnNum]
int 10h
ret
clear_screen:
push ax
mov ax, 3
int 10h
pop ax
ret
times 510-($-$$) db 0 ; Pad remainder of boot sector with 0s
dw 0xAA55 ; The standard PC boot signature
I haven't written code in Assembly for 20 years (!), but it looks like you need to use the 'stosw' instruction (or 'stosb'). STOSB loads the value held in AL to the byte pointed to by ES:DI, whereas STOSSW loads the value held in AX to the word pointed to by ES:DI. The instruction automatically advances the pointer. As your variable curInpLn is 80 bytes long, you can clear it with 40 iterations of STOSW. Something like
xor ax, ax ; ax = 0
mov es, ds ; point es to our data segment
mov di, offset curInpLn ; point di at the variable
mov cx, 40 ; how many repetitions
rep stosw ; zap the variable
This method is probably the quickest method of clearing the variable as it doesn't require the CPU to retrieve any instructions from the pre-fetch queue. In fact, it allows the pre-fetch queue to fill up, thus allowing any following instructions to execute as quickly as possible.