How to make string input in Assembly language? - user-input

Please, does anybody know how to code string input in assembly language? I'm using int 21 to display and input characters.

You can use function 0Ah to read buffered input. Given a string buffer in ds:dx it reads a string of up to length 255. The buffer layout is:
Byte 0 String length (0-255)
Byte 1 Bytes read (0-255, filled by DOS on return)
Bytes 2-..Length+2 (The character string including newline as read by DOS).
An example of a small COM file that reads a string and then echos it back to the user:
org 0x100
start:
push cs
pop ds ; COM file, ds = cs
mov ah, 0x0A ; Function 0Ah Buffered input
mov dx, string_buf ; ds:dx points to string buffer
int 0x21
movzx si, byte [string_buf+1] ; get number of chars read
mov dx, string_buf + 2 ; start of actual string
add si, dx ; si points to string + number of chars read
mov byte [si], '$' ; Terminate string
mov ah, 0x09 ; Function 09h Print character string
int 0x21 ; ds:dx points to string
; Exit
mov ax, 0x4c00
int 0x21
string_buf:
db 255 ; size of buffer in characters
db 0 ; filled by DOS with actual size
times 255 db 0 ; actual string
Note that it will overwrite the input line (and it thus might not look the program is doing anything!)
Alternatively you can use function 01h and read the characters yourself in a loop. Something like this (note it will overflow later buffers if more than 255 characters are entered):
org 0x100
start:
push cs
pop ax
mov ds, ax
mov es, ax; make sure ds = es = cs
mov di, string ; es:di points to string
cld ; clear direction flag (so stosb incremements rather than decrements)
read_loop:
mov ah, 0x01 ; Function 01h Read character from stdin with echo
int 0x21
cmp al, 0x0D ; character is carriage return?
je read_done ; yes? exit the loop
stosb ; store the character at es:di and increment di
jmp read_loop ; loop again
read_done:
mov al, '$'
stosb ; 'Make sure the string is '$' terminated
mov dx, string ; ds:dx points to string
mov ah, 0x09 ; Function 09h Print character string
int 0x21
; Exit
mov ax, 0x4c00
int 0x21
string:
times 255 db 0 ; reserve room for 255 characters

A string is just a series of characters, so you can use your int 21 code inside of a loop to get a string, one character at a time. Create a label in the data segment to hold your string, and each time you read a character, copy it to that label (incrementing an offset each time so your characters get stored sequentially). Stop looping when a certain character is read (perhaps enter).
Doing all this manually is tedious (think about how backspace will work) but you can do it. Alternatively, you can link against stdio, stdlib, etc. and call library functions to do much of the work for you.

Related

how can i print text in 16 bit real mode (graphical)?

i'm trying to write a boot sector that displays a small message on boot, but running the following on QEMU produces malformed text and any string with more than 5 characters doesn't show at all.
here's the code i assembled with NASM to a raw .bin file
[bits 16]
[org 0x7c00]
start:
xor ax,ax
mov ds,ax
mov es,ax
mov bx,0x8000
mov ax,0x13
int 0x10
mov ah,02
int 0x10
mov ah,0x02
mov bh,0x00
mov dh,0x12
mov dl,0x03
int 0x10
mov si , welcome
welcome db "hello",13,0
call RainbowPrint
RainbowPrint:
mov bl,1
mov ah, 0x0E
.repeat_next_char:
lodsb
cmp al, 0
je .done_print
add bl,6
int 0x10
jmp .repeat_next_char
.done_print:
ret
times (510 - ($ - $$)) db 0x00
dw 0xAA55
EDIT : here's an image showing the program running in qemu
You seem to overlapping your char_stream
welcome db "hello", 13, 0
I reviewed your code and came up with the following that has a clear display of your specific char_stream
[bits 16]
[org 0x7c00]
start:
xor ax,ax
mov ds,ax
mov es,ax
mov bx,0x8000
mov ax,0x13
int 0x10
mov ah,02
int 0x10
mov ah,0x02
mov bh,0x00
mov dh,0x12
mov dl,0x03
int 0x10
mov si , welcome
; Your [welcome] stream was here before...
call RainbowPrint
;***********
; Don't mind this area... just a simple key detection and reboot method...
xor ax, ax
int 0x16
xor ax, ax
int 0x19
;***********
; Move your stream away from operatable code areas:
welcome db "Hello, World!", 0x0D, 0x0A, 0x00
RainbowPrint:
mov bl,1
mov ah, 0x0E
.repeat_next_char:
lodsb
cmp al, 0
je .done_print
add bl,6
int 0x10
jmp .repeat_next_char
.done_print:
ret
times (510 - ($ - $$)) db 0x00
dw 0xAA55
The prefered text stream

Is it the code, or is it the OS..?

MY CODE IS
.model small
.data
arr db 83h, 12h, 0F0h, 0Bh, 89h
cnt db 05h
.code
mov ax, #data
mov ds, ax
mov si, offset arr
mov cl, cnt
sub bl, bl
sub dl, dl
back:
mov al, [si]
and al, 80h
jz skip
inc bl
skip:
inc dl
inc si
dec cl
jnz back
mov ah, 4cH
int 21H
end
THE ERRORS ARE AS FOLLOWS
1.asm:1: error: attempt to define a local label before any non-local labels
1.asm:1: error: parser: instruction expected
1.asm:2: error: attempt to define a local label before any non-local labels
1.asm:9: error: comma, colon, decorator or end of line expected after operand

why did not fill with zeros

Allocated array for 10000 bits = 1250 bytes(10000/8):
mov edi, 1250
call malloc
tested the pointer:
cmp rax, 0
jz .error ; error handling at label down the code
memory was allocated:
(gdb) p/x $rax
$3 = 0x6030c0
attempted to fill that allocated memory with zeros:
mov rdi, rax
xor esi, esi
mov edx, 1250 ; 10000 bits
call memset
checked first byte:
(gdb) p/x $rax
$2 = 0x6030c0
(gdb) x/xg $rax + 0
0x6030c0: 0x0000000000000000
checked last byte(0 - first byte, 1249 - last byte)
(gdb) p/x $rax + 1249
$3 = 0x6035a1
(gdb) x/xg $rax + 1249
0x6035a1: 0x6100000000000000
SOLVED QUESTION
Should have typed x/1c $rax + 1249
You interpreted memory as a 64 bit integer, but you forgot that endianness of intel is little endian. So bytes were reversed.
0x6100000000000000 is the value that the CPU reads when de-serializing the memory at this address. Since it's little endian, the 0x61 byte is last in memory (not very convenient to dump memory in this format, unless you have a big endian architecture)
Use x /10bx $rax + 1249 you'll see that it's zero at the correct location. The rest is garbage (happens to be zero for a while, then garbage)
0x00 0x00 0x00 0x00 0x00 0x00 0x61

Read a sector to address zero

%include "init.inc"
[org 0x0]
[bits 16]
jmp 0x07C0:start_boot
start_boot:
mov ax, cs
mov ds, ax
mov es, ax
load_setup:
mov ax, SETUP_SEG
mov es, ax
xor bx, bx
mov ah, 2 ; copy data to es:bx from disk.
mov al, 1 ; read a sector.
mov ch, 0 ; cylinder 0
mov cl, 2 ; read data since sector 2.
mov dh, 0 ; Head = 0
mov dl, 0 ; Drive = 0
int 0x13 ; BIOS call.
jc load_setup
lea si, [msg_load_setup]
call print
jmp $
print:
print_beg:
mov ax, 0xB800
mov es, ax
xor di, di
print_msg:
mov al, byte [si]
mov byte [es:di], al
or al, al
jz print_end
inc di
mov byte [es:di], BG_TEXT_COLOR
inc di
inc si
jmp print_msg
print_end:
ret
msg_load_setup db "Loading setup.bin was completed." , 0
times 510-($-$$) db 0
dw 0xAA55
I want to load setup.bin to memory address zero. So, I input 0 value to es register (SETUP_SEG = 0). bx, too. But it didn't work. then, I have a question about this issue. My test is below.
SETUP_SEG's value
0x0000 : fail
0x0010 : success
0x0020 : fail
0x0030 : fail
0x0040 : fail
0x0050 : success
I can't understand why this situation was happened. All test was carried out on VMware. Does anyone have an idea ?
I'm not sure if this is your problem, but your trying to load setup.bin in the Real Mode IVT (Interrupt Vector Table). The IVT contains the location of each interrupt, so I'm assuming that your boatloader is overwriting them when it loads setup.bin into memory! Interrupts can be sneaky and tricky, since they can be called even if you didn't call them. Any interrupt vector you overwrote will likely cause undefined behavior when called, which will cause some problems.
I suggest setting SETUP_SEG to a higher number like 0x2000 or 0x3000, but the lowest you could safely go is 0x07E0. The Osdev Wiki and Wikipedia have some helpful information on conventional memory and memory mapping.
I hope this helps!

Assembly printing system date

I have a problem with printing system date because of cx register size. How can it be solved without any big changes?
title casadatum
zas segment stack
db 256 dup(?)
zas ends
strsize EQU 64
dat segment
print db 'Current System Date is : $'
date db 'dd:mm:rrrr$'
nl db 10,13,'$'
dat ends
code segment
assume cs:code, ss:zas, ds:dat
get_date proc
mov ah,2ah
int 21h
mov al,dl
call convert
mov [bx],ax
mov al,dh
call convert
mov [bx+3],ax
mov al,cx
call convert
mov [bx+6],ax
ret
endp
convert proc
push dx
mov ah,0
mov dl,10
div dl
or ax, 3030h
pop dx
ret
endp
start:
mov ax, seg dat
mov ds,ax
LEA BX, date
CALL GET_date
lea dx,print
mov ah,09h
int 21h
lea dx,date
mov ah,09h
int 21h
koniec:
mov ah, 4ch
int 21h
code ends
end start
Replace this part of your code
mov al,cx
call convert
mov [bx+6],ax
with these instructions
mov al,100
xchg ax,cx
div cl
mov ch,ah
call convert
mov [bx+6],ax
mov al,ch
call convert
mov [bx+8],ax