Reputation:
I am developing a simple 16-bit Real Mode kernel in Assembly, as a DOS clone kernel. I am currently trying to read user input as a string by taking every character they type, and appending it to an array. The function should return the array (why I have yet to implement) as a string. However, my following code does not work. Why? Thanks in advance. I am using the NASM Assembler, if it makes a difference.
7 section .bss:
8 INPUT_STRING: resb 4
9 section .text:
....
39 scans:
40 mov ah, 0x00
41 .repeat:
42 int 16h
43 cmp al, 0
44 je .done
45 cmp al, 0x0D
46 ; jump straight to returning the array, implement later
47 mov [INPUT_STRING+al], 1
48 add al, 4
49 jmp .repeat
50 .done:
51 ret
Upvotes: 0
Views: 2643
Reputation: 16606
You can't easily dynamically grow your array in assembly, the INPUT_STRING: resb 4
reserves 4 bytes (max input is then 3 characters + 13 <CR>
char). Then add al,4
makes your pointer in al
to advance by 4 bytes, i.e. completely off the reserved memory after first iteration (not even mentioning that al
is modified by BIOS to return value in it, and that you need 16 bit register to store memory address offset in real mode, while al
is only 8 bits), you can write chars only into memory at addresses INPUT_STRING+0, INPUT_STRING+1, INPUT_STRING+2, INPUT_STRING+3
(unless you want to overwrite some memory which you may accidentally use for something else). That's general simple principle how fixed-size "arrays" may be implemented in ASM (you can of course use more complicated design if you wish, only your code is the limit, what you do with your CPU and memory): you reserve N*data_type_size bytes, and write there values at offsets +0*data_type_size, +1*data_type_size, 2*data_type_size ... in case of ASCII characters each character is 1 byte long, so the offsets of elements of "array" are simple 0, 1, 2, ...
Also in your code you have to re-set the AH
to zero every time ahead of int 16h
, because the interrupt will modify AH
with keyboard scancode. And you should check for maximum input size, if you have fixed-size input array.
Some simple very basic and crude example (proper command line input should also handle special keys like backspace, etc):
In the data the global fixed size buffer (256 bytes) is reserved like this:
INPUT_BUFFER_MAX_LEN equ 255
; reserver 255 bytes for input and +1 byte for nul terminator
INPUT_STRING: resb INPUT_BUFFER_MAX_LEN+1
And the code to store user input into it, checking for max length input.
...
scans:
mov di,INPUT_STRING ; pointer of input buffer
lea si,[di+INPUT_BUFFER_MAX_LEN] ; pointer beyond buffer
.repeat:
; check if input buffer is full
cmp di,si
jae .done ; full input buffer, end input
; wait for key press from user, using BIOS int 16h,ah=0
xor ah,ah ; ah = 0
int 0x16
; AL = ASCII char or zero for extended key, AH = scancode
test al,al
jz .done ; any extended key will end input
cmp al,13
je .done ; "Enter" key will end input (not stored in buffer)
; store the ASCII character to input buffer
mov [di],al ; expects ds = data segment
inc di ; make the pointer to point to next char
jmp .repeat ; read more chars
.done:
; store nul-terminator at the end of user input
mov [di],byte 0
ret
After ret
the memory at address INPUT_STRING
will contain bytes with user inputted ASCII characters. For example if the user will hit Abc123<enter>
, the memory at address INPUT_STRING
will look like this (bytes in hexadecimal): 41 62 63 31 32 33 00 ?? ?? whatever was there before ... ?? ??
, six ASCII characters and the null terminator at seventh (+6 offset) position. This would suffice as "C string" for common C functions like printf
and similar (it's same memory structure/logic, as the C language does use for it's "strings").
Upvotes: 0