Clearing out a string variable - string

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.

Related

print out skewed text in x86, but can't see why the 'monitored command dumped core' timeout occurs

I am trying to write skewed Text on Terminal via assembly to get my skills going.
Example [] = spaces :
H
[]E
[][]L
[][][] L
[][][][] O
But I seem to run into a timeout, because I am trying to move a character between registers / get the character via memory address - like getting a char at an index. But it won't really work. I tried working with allocating a byte to increment in the .bss section before, but that lead to a different error which I couldn't work around (relocation truncated to fit: R_X86_64_8 against `.bss')
Maybe there is a better approach to handle this, and if you could help me get on the right path I would be so so happy. To get more context, here is the 'problematic' snippet of my latest code.
inc rcx ; increase pointer of rcx
space_loop:
push rdi ;save destination register
cmp byte [rcx], 0 ;end of string found
je eos ; jump to exit subroutine
mov rax, 4; SYS Write
mov rbx, 0 ; STDOUT
cmp rsp,[rbp] ; compare spaceIndex to String Index
je print_char
mov rdi, space ;print space
mov rdx,1 ; length = 1
syscall
inc byte [rsp] ; increase spaceIndex
jnz space_loop
print_char:
;print out char
mov rax, 4
mov rbx, 0
mov rcx,[rcx] ; <- this line breaks it dont know how to access index here
mov rdx, 1
syscall
call new_Line ; make line break
inc rcx ; increase rcx counter
inc byte [rbp] ; increase index counter
mov byte [rsp], 0 ;reset space counter
jmp space_loop; next Line

Getting string input and displaying input with DOS interrupts MASM

In MASM, I created a buffer variable to hold the user string input from keyboard. I am stuck on how to hold the string input into that buffer variable. I don't have any libraries linked like the irvine ones and want to do this with DOS interrupts. So far I have something along the lines of
.model small
.stack 100h
.data
buff db 25 dup(0), 10, 13
lbuff EQU ($ - buff) ; bytes in a string
.code
main:
mov ax, #data
mov ds, ax
mov ah, 0Ah ; doesn't work
mov buff, ah ; doesn't seem right
int 21h
mov ax, 4000h ; display to screen
mov bx, 1
mov cx, lbuff
mov dx, OFFSET buff
int 21h
mov ah, 4ch
int 21h
end main
I assume using 0Ah is correct as it is for reading array of input of buffered characters.
I made some changes to your code. First, the "buff" variable needs the three level format (max number of characters allowed, another byte for the number of characteres entered, and the buffer itself) because that's what service 0AH requires. To use service 0AH I added "offset buff" (as Wolfgang said). Here it is:
.model small
.stack 100h
.data
buff db 26 ;MAX NUMBER OF CHARACTERS ALLOWED (25).
db ? ;NUMBER OF CHARACTERS ENTERED BY USER.
db 26 dup(0) ;CHARACTERS ENTERED BY USER.
.code
main:
mov ax, #data
mov ds, ax
;CAPTURE STRING FROM KEYBOARD.
mov ah, 0Ah ;SERVICE TO CAPTURE STRING FROM KEYBOARD.
mov dx, offset buff
int 21h
;CHANGE CHR(13) BY '$'.
mov si, offset buff + 1 ;NUMBER OF CHARACTERS ENTERED.
mov cl, [ si ] ;MOVE LENGTH TO CL.
mov ch, 0 ;CLEAR CH TO USE CX.
inc cx ;TO REACH CHR(13).
add si, cx ;NOW SI POINTS TO CHR(13).
mov al, '$'
mov [ si ], al ;REPLACE CHR(13) BY '$'.
;DISPLAY STRING.
mov ah, 9 ;SERVICE TO DISPLAY STRING.
mov dx, offset buff + 2 ;MUST END WITH '$'.
int 21h
mov ah, 4ch
int 21h
end main
When 0AH captures the string from keyboard, it ends with ENTER (character 13), that's why, if you want to capture 25 characters, you must specify 26.
To know how many characters the user entered (length), access the second byte (offset buff + 1). The ENTER is not included, so, if user types 8 characters and ENTER, this second byte will contain the number 8, not 9.
The entered characters start at offset buff + 2, and they end when character 13 appears. We use this to add the length to buff+2 + 1 to replace chr(13) by '$'. Now we can display the string.
This is my code,maybe can help you.
;Input String Copy output
dataarea segment
BUFFER db 81
db ?
STRING DB 81 DUP(?)
STR1 DB 10,13,'$'
dataarea ends
extra segment
MESS1 DB 'After Copy',10,13,'$'
MESS2 DB 81 DUP(?)
extra ends
code segment
main proc far
assume cs:code,ds:dataarea,es:extra
start:
push ds
sub ax,ax
push ax
mov ax,dataarea
mov ds,ax
mov ax,extra
mov es,ax
lea dx,BUFFER
mov ah,0ah
int 21h
lea si,STRING
lea di,MESS2
mov ch,0
mov cl,BUFFER+1
cld
rep movsb
mov al,'$'
mov es:[di],al
lea dx,STR1 ;to next line
mov ah,09h
int 21h
push es
pop ds
lea dx,MESS1 ;output:after copy
mov ah,09h
int 21h
lea dx,MESS2
mov ah,09h
int 21h
ret
main endp
code ends
end start
And the result is:
c:\demo.exe
Hello World!
After Copy
Hello World!
You may follow this code :
; Problem : input array from user
.MODEL SMALL
.STACK
.DATA
ARR DB 10 DUB (?)
.CODE
MAIN PROC
MOV AX, #DATA
MOV DS, AX
XOR BX, BX
MOV CX, 5
FOR:
MOV AH, 1
INT 21H
MOV ARR[BX], AL
INC BX
LOOP FOR
XOR BX, BX
MOV CX, 5
PRINT:
MOV AX, ARR[BX] ;point to the current index
MOV AH, 2 ;output
MOV DL, AX
INT 21H
INC BX ;move pointer to the next element
LOOP PRINT ;loop until done
MAIN ENDP
;try this one, it takes a 10 character string input from user and displays it after in this manner, "Hello *10character string input"
.MODEL TINY
.CODE
.286
ORG 100h
START:
MOV DX, OFFSET BUFFER
MOV AH, 0ah
INT 21h
JMP PRINT
BUFFER DB 10,?, 10 dup(' ')
PRINT:
MOV AH, 02
MOV DL, 0ah
INT 21h
MOV AH, 9
MOV DX, OFFSET M1
INT 21h
XOR BX, BX
MOV BL, BUFFER[1]
MOV BUFFER [BX+2], '$'
MOV DX, OFFSET BUFFER +2
MOV AH, 9
INT 21h
M1: db 'Hello $'
END START
END

Buffers do no behave properly

I am miserable at assembly programming and ask for your help. What I do not understand is why do I lose the characters of a string from my buffer even though there is plenty of room in it?
My readBuffer size is 32 and writeBuffer size is 64, what my program does is add a space after a period and makes the first letter capital.
EXAMPLE:
Good.morning.people hehehe in the jungle the mighty jungle //this is the data
Good. Morning. People hehehe in the jungle the mighty jungle //this is the output
WHAT MY PROGRAM DOES:
Good.morning.people hehehe in the jungle the mighty jungle //this is the data
Good. Morning. People hehehe in e jungle the mighty jungle //this is the output
As you can see I end up with 'e' instead of 'the'
Here is the my code which does the editing:
Read:
MOV bx, DataHandle
CALL ReadBuffer
CMP ax, 0 ;ax = how many symbols did I read
JE closeWrite ;if 0 then close write file
;Editing the string
MOV cx, ax ;CX FOR LOOPING
MOV si, offset rBuff ;read buffer = si
MOV di, offset wBuff ;write buffer = di
Do_It:
MOV dl, [si]
CMP dl, '.' ;comparing to period
JNE Keep_Going ;if not period put it in di (write buffer)
MOV dh, [si+1] ;put the char after the period in dh
CMP dh, ' ' ;check if space is after it
JNE If_capital ;if not I check if it is a capital letter
MOV dh, [si+2] ;if there was a space I add the char after the space
CMP dh, 'a' ;checking if it is a capital letter
JB Keep_Going
CMP dh, 'z'
JA Keep_Going
SUB byte ptr [si+2], 32 ;turn it into a capital letter
Keep_Going:
MOV [di], dl ;put the char into writebuff
INC si
INC di
LOOP Do_It
JMP Finishin
If_capital:
CMP dh, 'a'
JB Add_Space
CMP dh, 'z'
JA Add_Space
SUB byte ptr [si+1], 32
Add_Space:
MOV [di], '.'
INC di
MOV [di], ' '
INC di
INC si
JMP Do_It
;Write the result
Finishin:
MOV cx, ax
CALL checkOnScreen ;print it to command line
MOV bx, WriteHandle
CALL writeBuffer ;print it
CMP ax, rBuffSize ;compare the size to 32
JE Read ;if it was 32 then read again
You write only 32 bytes of wBuff. This is after the first run: "Good. Morning. People hehehe in ". Since the line is longer, the rest is behind the boarder of 32 byte. Then you reset the pointers to rBuff and wBuff and read the next block: "e jungle the mighty jungle". That block is attached to the written line.
Consider that the block to be written has a variable length. You can calculate the length by subtracting the offset of wBuff from DI (sub di, OFFSET wBuff) after changing the JMP Do_It to LOOP Do_It.
I can't show a working example since I can't interpret relevant parts of your code (CMP ax, rBuffSize, JE Read ???).
Thanks for input guys, I managed to do it myself this time. The problem was
Finishin:
MOV cx, ax
CALL checkOnScreen
MOV bx, WriteHandle
CALL writeBuffer
CMP ax, rBuffSize
JE Read
New code:
Finishin:
MOV cx, strLength2 ;length of writebuffer
CALL checkOnScreen
MOV bx, WriteHandle
CALL writeBuffer
CMP ax, rBuffSize ;ax was messed up so I changed it to strLength1 or readBuffer
JE Read

Nasm Print String in TSR program

I am trying to print string in my program. When i put my instructions
mov ah,9h
mov dx,poruka
int 21h
before call _inst_09 it prints well. But when i print inside my code like shown below it prints weird stuff and characters.
This is picture of working program. String is printed well
This is picture of program not working when i print my string inside the code
This is my TSR code
org 100h
NULL equ 000h
ESC equ 001h
KBD equ 060h
EOI equ 020h
Master_8259 equ 020h
zelena equ 02h
main:
mov ah,9h
mov dx,poruka
int 21h
call _inst_09
_inst_09:
cli
xor ax, ax
mov es, ax
;mov ax, [stari_int09_off]
mov bx, [es:09h*4]
mov [stari_int09_off], bx
mov [es:60h*4], bx ; U int60h ubacujemo off od int9h
mov bx, [es:09h*4+2]
mov [stari_int09_seg], bx
mov [es:60h*4+2], bx ; U int60h ubacujemo seg od int9h
mov dx, tastatura
mov [es:09h*4], dx
mov ax, cs
mov [es:09h*4+2], ax
sti
mov ax,3100h
mov dx,500
int 21h
ret
tastatura:
push ax
in al, KBD
mov [kbdata], al
cmp byte[kbdata],20h
je .lup
cmp byte[kbdata],ESC
je .krj
mov al, EOI
out Master_8259, al
pop ax
int 60h ; Vracamo stari interupt 9h
iret
.lup:
mov ax,0b800h ;dont forget 0 before b
mov es,ax
mov bx,word[video]
mov ah, 02h
int 1ah
mov al,dh
mov byte [es:100+bx],al ;also dont forget the byte thing
;inc byte[video]
;inc byte[video]
mov al, EOI
out Master_8259, al
pop ax
iret
.krj:
mov ah,9h
mov dx,poruka
int 21h
ret
stari_int09_seg: dw 0
stari_int09_off: dw 0
kbdata: db 0
key: db 0
video: dw 100
poruka: db 'Poruka.$'
%include "ekran.asm"
Here is the deal with code. This is a TSR program, so it terminates and stays resident when i run it. When i press esc button it should print my string on the screen(it jumps on .krj label) but it will show something like you see on the 2nd picture. When i put my instructions before call _inst09 like i said before it will print my string lik in the 1st picture.
I think that somehow my string address has changed and that is why it is not working but i cannot figure it out.
I would appreciate if you could give me a direct answer regarding to my code.
With je .krj you jump to a routine that calls a DOS service for printing. You cannot use DOS services from within an interrupt handler because DOS might have been occupied when the interrupt was triggered.
Simplest solution here is to output to the screen using the BIOS teletype function 0Eh. Why not even write to the screen at segment 0B800h yourself since you're doing it already somewhere else?
If we jump to .krj, then we also have to terminate the ISR with sending an EOI, popping ax and end with iret. The last "ret" instruction is wrong.

8086 ASM - Output String to screen

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
...

Resources