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.
Related
I try to change the string to the other one, but the output I get is broken.
How do I change string to the other one so it doesn't do this:
Intended output:
in rect
under line
out circ
Also, I do not know the purpose of "offset" in the code so it also can be the problem.
Also, I get some warnings when trying to assemble it and I don't know they mean:
The code:
.model small
.data
X db 0
Y db 0
var0 db 0
rect db "out rect",13,10,"$"
rect_plus db "in rect",13,10,"$"
line db "under line",13,10,"$"
line_plus db "above line",13,10,"$"
circ db "out circ",13,10,"$"
circ_plus db "in circ",13,10,"$"
.code
main:
mov ax, #data
mov ds, ax
jmp input
input:
...
jmp rect_check1
rect_check1:
...
jb rect_check2
jmp rect_print
rect_check2:
...
jb rect_change
jmp rect_print
rect_change:
lea bx, rect; <============== SOMETHING WRONG HERE
mov [bx], offset rect_plus; <=========================== OR HERE
jmp rect_print
rect_print:
mov dx, offset rect; <=========================== OR HERE
mov ah, 09h
int 21h
jmp line_check
line_check:
...
jb line_change
jmp line_print
line_change:
lea bx, line; <=========================== OR HERE
mov [bx], offset line_plus; <=========================== OR HERE
jmp line_print
line_print:
mov dx, offset line; <=========================== OR HERE
mov ah, 09h
int 21h
jmp circ_check
circ_check:
...
jb circ_change
jmp circ_print
circ_change:
lea bx, circ; <=========================== OR HERE
mov [bx], offset circ_plus; <=========================== OR HERE
jmp circ_print
circ_print:
mov dx, offset circ; <=========================== OR HERE
mov ah, 09h
int 21h
jmp quit
quit:
mov ax, 4c00h
int 21h
end main
rect_change:
lea bx, rect; <============== SOMETHING WRONG HERE
mov [bx], offset rect_plus; <=========================== OR HERE
jmp rect_print
Your logic is wrong here.
lea bx, rect loads the offset of string "out rect",13,10,"$" to register bx. The offset in bx is then 0003h. The next instruction tries to overwrite one or two bytes of memory at ds:bx (the letters "o" or "ou") with offset of another string "in rect",13,10,"$" (with binary number 000Eh) which makes no sense. As you didn't specify how many bytes you're going to overwrite, TASM gives you the Warning.
Restructuralize your spaghetti code and make your subprocedures callable (ending with ret), for instance
line_print PROC ; Display the $-terminated string addressed with DS:DX.
mov ah, 09h
int 21h
ret
line_print ENDP
Then, whenever you want to display a string, load its offset to dx and call the subprocedure:
mov dx,offset rect
call line_print
; check the rectangle
mov dx,offset rect_plus
call line_print
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
I am trying to write a bootloader which is just a program that sets its own keybord interrupt handler and handles keyboard presses.
However I am lost.
This one does not respond to keyboard presses by writing to video RAM.
org 7c00h ; BOOT PROGRAM
mov ax,03h
int 10h ; 80x25 video mode
mov ax,1112h;
int 10h ; 80x50 video mode
cli ; Zakaz preruseni
mov word[ds:20h],keyboard_handler ; nastaveni INT9 na moji funkci
mov word[ds:22h],0h
sti
jmp $
keyboard_handler:
cli
push ax
push dx
push bx
in al,60h
mov dx,0b800h
mov es,dx
mov dx,0000h
mov ds,dx
mov dl,10h
div dl; al+ah
add ax,3030h
mov dl,ah
cmp al,3ah
jl not_add
add al,7h
not_add cmp dl,3ah
jl not_add2
add dl,7h
not_add2 mov ah,21h
mov dh,21h
mov bx,[ds:vidoff]
mov word[es:bx],ax
add bx,2h
mov word[es:bx],dx
add bx,2h
mov word[ds:vidoff],bx
;finalizing code
mov al,20h
out 20h,al ;odhlaseni preruseni
;mov word ax,[ds:22h]
;mov cs,ax
;mov ds,ax
;mov ss,ax
;mov es,ax
pop bx
pop dx
pop ax
sti
iret
vidoff dw 0FF0h
times 510-($-$$) db 0
dw 0AA55h
And this one is messy, but it keeps writing to video RAM eventhough no key was pressed. If I press a key, however, at least it responds to my keypress by taking my scancode and writing to video RAM accordingly. Then it again continues as if some key was pressed continously.
org 7c00h ; BOOT PROGRAM
;++++++ Nastaveni video mode 80x50 a INT9(co reaguje na klavesnici pomoci preruseni IRQ1)
mov ax,03h
int 10h ; 80x25 video mode
mov ax,1112h;
int 10h ; 80x50 video mode
mov dx,0b800h
mov es,dx
mov dx,0h
mov ds,dx
mov di,0FF0h
mov bx,0h
mov ds,bx
cli ; Zakaz preruseni
mov word[ds:20h],keyboard_handler ; nastaveni INT9 na moji funkci
mov word[ds:22h],0h
sti
; Povoleni preruseni
jmp $
keyboard_handler:
push es
push ax
mov ax,0h
in al,60h
mov ch,10h
div ch
mov byte[prvni_cislice],al
mov byte[druha_cislice],ah
mov ch,al
mov cl,0dbh
mov word[barva_cislice],cx
mov bl,ah
mov bh,0dbh
cmp cx,bx
jne normal
xor ch,0fh
mov bh,ch
normal mov word[barva_podkladu],bx
mov word[es:di],bx
add di,2h
mov al,20h
out 20h,al ;odhlaseni preruseni
pop ax
pop es
iret
;hexacislice
prvni_cislice db 0h
druha_cislice db 0h
;hotove znaky
barva_podkladu dw 0h
barva_cislice dw 0h
offset_radku dw 0h
PRNGseed dw 0h
times 510-($-$$) db 0
dw 0AA55h
What I am doing wrong? Am I forgetting something? In some other code I saw somebody adding out,61h or something like that, I dont know what that does though.
The biggest problem with these programs is that you are NOT setting up the keyboard interrupt! Instead you are messing with the system timer interrupt.
mov word[ds:20h],keyboard_handler ; nastaveni INT9 na moji funkci
mov word[ds:22h],0h
Better use this :
mov word[ds:24h],keyboard_handler
mov word[ds:26h],0h
The rest of the code you've written poses a real challenge to people that do not speak your native language. Please use English-only names for labels and variables.
Also be generous with comments in your code. They make reading a lot easier and thus increase your chance of getting an answer.
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 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.