I am trying to make a 8086 program in which I input an array of string and then the program prints them. But the program prints only the 1st characters of input strings and jumbled text.
This is the code where I attempted to do it.
data segment
; Definicija podataka
poruka1 DB "Input array length: $"
strN DB " "
N DW 0
poruka2 DB "Input string: $"
strM DB " "
niz1 DB 16 dup(?)
ends
; Deficija stek segmenta
stek segment stack
dw 128 dup(0)
ends
; Ucitavanje znaka bez prikaza i cuvanja
keypress macro
push ax
mov ah, 08
int 21h
pop ax
endm
; Isis stringa na ekran
writeString macro s
push ax
push dx
mov dx, offset s
mov ah, 09
int 21h
pop dx
pop ax
endm
; Kraj programa
krajPrograma macro
mov ax, 4c02h
int 21h
endm
code segment
; Novi red
novired proc
push ax
push bx
push cx
push dx
mov ah,03
mov bh,0
int 10h
inc dh
mov dl,0
mov ah,02
int 10h
pop dx
pop cx
pop bx
pop ax
ret
novired endp
; Ucitavanje stringa sa tastature
; Adresa stringa je parametar na steku
readString proc
push ax
push bx
push cx
push dx
push si
mov bp, sp
mov dx, [bp+12]
mov bx, dx
mov ax, [bp+14]
mov byte [bx] ,al
mov ah, 0Ah
int 21h
mov si, dx
mov cl, [si+1]
mov ch, 0
kopiraj:
mov al, [si+2]
mov [si], al
inc si
loop kopiraj
mov [si], '$'
pop si
pop dx
pop cx
pop bx
pop ax
ret 4
readString endp
; Konvertuje string u broj
strtoint proc
push ax
push bx
push cx
push dx
push si
mov bp, sp
mov bx, [bp+14]
mov ax, 0
mov cx, 0
mov si, 10
petlja1:
mov cl, [bx]
cmp cl, '$'
je kraj1
mul si
sub cx, 48
add ax, cx
inc bx
jmp petlja1
kraj1:
mov bx, [bp+12]
mov [bx], ax
pop si
pop dx
pop cx
pop bx
pop ax
ret 4
strtoint endp
; Konvertuje broj u string
inttostr proc
push ax
push bx
push cx
push dx
push si
mov bp, sp
mov ax, [bp+14]
mov dl, '$'
push dx
mov si, 10
petlja2:
mov dx, 0
div si
add dx, 48
push dx
cmp ax, 0
jne petlja2
mov bx, [bp+12]
petlja2a:
pop dx
mov [bx], dl
inc bx
cmp dl, '$'
jne petlja2a
pop si
pop dx
pop cx
pop bx
pop ax
ret 4
inttostr endp
start:
; postavljanje segmentnih registara
ASSUME cs: code, ss:stek
mov ax, data
mov ds, ax
; Mesto za kod studenata
writeString poruka1
; Unos broja elemenata
push 3
push offset strN
call readString
; Pretvaranje strN u broj
push offset strN
push offset N
call strtoint
call novired
writeString strN
; Unos elemenata niza
mov cx, N
mov di, 0
mov si, 0
unos:
call novired
writeString poruka2
push 6
push offset strM
call readString
mov al, strM
mov niz1[di], al
add di, 2
add si, 1
loop unos
call novired
mov cx, N
mov di, 0
mov si, 0
ispis:
writeString niz1[di]
add di, 2
add si, 1
loop ispis
krajPrograma
ends
end start
I input an array of string and then the program prints them. But the program prints only the 1st characters of input strings ...
What else could the program print if the 1st character is all that you store in memory?
mov al, strM
mov niz1[di], al
add di, 2
... and jumbled text.
With add di, 2 you leave an undetermined byte between any two of these 1st characters. That's not going to look nice. Make that byte the DOS string terminator $.
Your writeString macro does not load an address but a word from memory. That's not going to work. The invokation writeString niz1[di] gets expanded into mov dx, offset niz1[di]. What you need here is LEA instead of MOV.
Try the following:
....
unos:
call novired
writeString poruka2
push 6
push offset strM
call readString
mov al, strM
MOV AH, "$"
mov niz1[di], AX
add di, 2
loop unos
call novired
mov cx, N
mov di, 0
ispis:
LEA DX, niz1[di]
MOV AH, 09h
INT 21h
add di, 2
loop ispis
...
I'm programming the game "15 puzzle game" in x86 using tasm asm/dosbox. I want to "save" or "preserve" my array called 'a' that is being declared in my data segment, so that after I swap the bytes around using upArrow, downArrow, etc... and then I press 'n' for new game, I can set my array 'a' back to it's original state as it was declared in my data segment. Here is my code:
.MODEL TINY
.386
.DATA
PROMPT DB "Enter q to quit, arrow keys to move, n to restart (new game) HAPPY HOLIDAYS!! :)$"
;this is board preset A
a DB 201,205,205,205,205,203,205,205,205,205,203,205,205,205,205,203,205,205,205,205,203,205,205,205,205,203,205,205,205,205,187,
DB 186,255,48,50,255,186,255,48,49,255,186,255,48,51,255,186,255,48,52,255,186,255,48,53,255,186,255,48,54,255,186,
DB 204,205,205,205,205,206,205,205,205,205,206,205,205,205,205,206,205,205,205,205,206,205,205,205,205,206,205,205,205,205,185,
DB 186,255,48,55,255,186,255,48,56,255,186,255,48,57,255,186,255,49,48,255,186,255,49,49,255,186,255,49,50,255,186,
DB 204,205,205,205,205,206,205,205,205,205,206,205,205,205,205,206,205,205,205,205,206,205,205,205,205,206,205,205,205,205,185,
DB 186,255,49,51,255,186,255,49,52,255,186,255,49,53,255,186,255,49,54,255,186,255,49,55,255,186,255,255,255,255,186,
DB 204,205,205,205,205,206,205,205,205,205,206,205,205,205,205,206,205,205,205,205,206,205,205,205,205,206,205,205,205,205,185,
DB 186,255,49,56,255,186,255,49,57,255,186,255,50,48,255,186,255,50,49,255,186,255,50,50,255,186,255,50,51,255,186,
DB 200,205,205,205,205,202,205,205,205,205,202,205,205,205,205,202,205,205,205,205,202,205,205,205,205,202,205,205,205,205,188,"$"
col DB 0
row DB 0
board DB 0
.CODE
org 100h
MAIN PROC
;initialize display step 0
MOV AH, 00h
MOV AL, 03h
INT 10h
MOV AX, 0B800h
MOV ES, AX
XOR di,di
;initialize configuration and dislay prompt step 1
newGame:
call initConfig
;display board step 2
;wait for key step 3
game:
;print board
call printBoard1
;get cursor x y
call SetCursor
MOV AH, 0
INT 16H
MOV BL, AL
;up arrow
CMP AH, 48h
jz upArrow
;down arrow
CMP AH, 50h
jz downArrow
;right arrow
CMP AH, 4dh
jz rightArrow
;left arrow
CMP AH, 4bh
jz leftArrow
;lowercase q
CMP AL, 71h
jz EXIT
;uppercase q
CMP AL, 51h
jz EXIT
;lowercase n
CMP AL, 6eh
jz newGame
;uppercase n
CMP AL, 4eh
jz newGame
jmp game
MAIN ENDP
EXIT:
MOV AH, 4CH ; return control to DOS
INT 21H
SetCursor:
mov dl, col
mov dh, row
mov bh, 0
mov ah, 02h
int 10h
ret
getStrlen:
lodsb
cmp al, 24h
je strlen_end
inc bx
jmp getStrlen
strlen_end:
ret
loadAndDisplayStr:
lodsb
stosw
dec cx
jne loadAndDisplayStr
ret
printBoard1:
MOV board, 1
xor dx,dx
xor ax,ax
mov cx, 9
myloop1:
push cx
lea si, a
add si, dx
mov cx, 31
mov di, ax
push ax
mov ah, 4
call loadAndDisplayStr
pop ax
add ax, 160
add dx, 32
pop cx
dec cx
jnz myloop1
ret
upArrow:
xor si, si
xor di, di
lea bx, a
mov ax, 32
sub row, 2
mul row
add al, col
mov di, ax
mov ax, 32
add row, 2
mul row
add al, col
mov si, ax
mov dx, [bx+si]
xchg dx, [bx+di]
xchg [bx+si], dx
sub row, 2
jmp game
downArrow:
xor si, si
xor di, di
lea bx, a
mov ax, 32
add row, 2
mul row
add al, col
mov di, ax
mov ax, 32
sub row, 2
mul row
add al, col
mov si, ax
mov dx, [bx+si]
xchg dx, [bx+di]
xchg [bx+si], dx
add row, 2
jmp game
leftArrow:
xor si, si
xor di, di
lea bx, a
mov ax, 32
sub col, 5
mul row
add al, col
mov di, ax
mov ax, 32
add col, 5
mul row
add al, col
mov si, ax
mov dx, [bx+si]
xchg dx, [bx+di]
xchg [bx+si], dx
sub col, 5
jmp game
rightArrow:
xor si, si
xor di, di
lea bx, a
mov ax, 32
add col, 5
mul row
add al, col
mov di, ax
mov ax, 32
sub col, 5
mul row
add al, col
mov si, ax
mov dx, [bx+si]
xchg dx, [bx+di]
xchg [bx+si], dx
add col, 5
jmp game
initConfig:
;put the cursor at 00
mov col, 27
mov row, 5
;gets strlen of prompt and stores it in BX
lea si, PROMPT
xor bx, bx
call getStrlen
;display prompt
lea si, PROMPT
mov cx, bx
mov ah, 2
mov di, 5a0h
call loadAndDisplayStr
ret
END MAIN
The way I see it is there are 2 ways of doing this. One way would be to create an empty array, and every time the user makes a move (up/down/left/right) I can push this to a stack, and then when the user hits 'n' for new game, I just pop through that stack and reverse all the moves.
Another alternative would be to create 2 identical boards (arrays/string) 'a' and 'b', and then the user would manipulate board 'a' till they decide to hit 'n' for a new game, and then I would some how set 'a' = 'b'
There's no magic way, you just have to copy the array like you would in C.
I'd suggest having one master copy of the data that you keep clean / untouched, never ever writing into it.
Allocate uninitialized space for the working game state somewhere, e.g. on the stack or in the BSS1.
At the start of every game (including the first one), copy the preset whole array to the scratch space for the current game. (e.g. with rep movsb or rep movsw after setting CX, SI, and DI. ES and DS segment regs are already equal segment because of .model tiny. i.e. implement a memcpy however you like.)
So you don't need to write the initializer data twice or anything like that, and only one copy of it needs to be in the file.
And no you don't need any exchanging, just a block copy. Get the assembler to calculate the size of the whole thing by putting a label at the end, or put datasize = $ - a right after the end of a to get the size in bytes.
Footnote 1: See #MichaelPetch's comment for details on how to declare a label in the .data? segment so you can use normal label/symbol syntax. Like gamestate datasize dup(?), where datasize is an = or equ constant you got the assembler to calculate earlier from the size of the preset.
I think .data? is just space past the end of where DOS loads your .com file, and you can use space from there out to near the end of a 64k segment where the program-loader put your stack. (Unlike the .bss under a modern OS is not zeroed to start with. That's fine for this, you want to write the space you use before you read/modify it anyway.)
#DavidWohlferd also suggests you could re-read part of the executable from disk, with DOS file open/read system calls. That could be the start of a save-game mechanism. In its simplest form, if the first n bytes of your program are all read-only (code and constant data) apart from this game-state array, you could read the first n bytes of the raw .com file into memory at ds:100h (or cs or es, it's all the same in a tiny memory model). Overwriting instructions with themselves (like a jmp rel16 over the data) is fine.
How could I do something like this in assembly? DOS 16bit graphic mode
int start_x=1, start_y=1;
for(int i=0; i<8; i++){
for(int j=0; j<8; j++){
if(T34[i][j]==1) put_colour_pixel(start_x+i, start_y+j);
else put_black_pixel(start_x+i, start_y+j);
}
}
:edit:
So my loops pretty work. How to connect it to table with 0 and 1.
mov ax, 10 ; Y start line
mov bx, 20 ; X start line
mov dl, 4 ; colour (red)
mov cx, 5 ; loop top counter
top:
add ax, 1
push cx ;loop top counter
mov cx, 10
inside:
add bx, 1
push ax
push bx
push cx
call putpixel
pop cx
pop bx
pop ax
loop inside
mov bx, 20 ;next line X go to start X
pop cx ;loop top counter
loop top
/////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////
My new code:
segment .data
segment .code
..start:
mov ax, 13h
int 10h ; switch to 320x200 mode
mov ax, 0a000h ; The offset to video memory
mov es, ax ; We load it to ES through AX,
; because immediate operation
; is not allowed on ES
;;;;;;;;;;;;;;;;;;;;;;
mov di, T34
mov si, 8
;------------------------------------
P1: mov bp, 8
;----------------
P2: cmp BYTE[di], 1
jnz short NOHIT
NOHIT: ; increase the x position
push ax
push bx
push cx
mov ax,si ;Y
mov bx,bp ;X
mov dl, 1 ; here I should take '0' or '1' from table
call putpixel
pop cx
pop bx
pop ax
inc di ; increase offset address of array
dec bp
jnz P2
;-------------
; increase the y position + substract 8 from x position
dec si
jnz P1
;------------------------------------
;;;;;;;;;;;;;;;;;;;;;;;;;
xor ah, ah
int 16h ; keyboard (wait for key)
mov ax, 3
int 10h ; go to text mode
mov ax, 4c00h
int 21h ; return to DOS, exit code 0
;;;;;;;;;;;;;;;;;;;;;
putpixel:
push dx ; mul changes dx too
mov cx, 320
mul cx ; multiply Y (ax) by 320 (one row)
add ax, bx ; and add X (bx) (result= dx:ax)
mov di, ax
pop dx
mov [es:di], dl ; store color/pixel
ret
;;;;;;;;;;;;;;;;;;;;;;
T34 DB 1,1,1,1,1,1,1,1
DB 1,0,0,0,0,0,0,0
DB 1,0,0,0,0,0,0,0
DB 1,0,0,0,0,0,0,0
DB 1,1,1,1,1,1,1,1
DB 1,0,0,0,0,0,0,0
DB 1,0,0,0,0,0,0,0
DB 1,1,1,1,1,1,1,1
I have changed order and now it is drawing square in left corner (as I expected). Did I change order rigth?
I suppose that letter will be spinned, but it is not problem for me. I can fix it later.
Now I should go to '0' or '1' in table and set color. Which register have "0" or "1"?
So mostly I have problem with colour.
I have try this, but I have error. I have try set colour black (0) or blue (1)
push dx
mov ax, bp ; X*8
mov cx,8
mul cx
add ax, si ; X*8 +Y
add ax, di ; tab0 + X*8+Y
mov dl, ax; here is error, here I set the colour
pop dx
So I don't know how to fix it :( I have try different possibilities and northing work.
mov di, T34
mov si, 8
;------------------------------------
P1: mov bp, 8
;----------------
P2: cmp BYTE[di], 1
jnz short NOHIT
; save used REGISTER
; load register with the X,Y position and color
call putpixel
; get back the saved Register
NOHIT: ; increase the x position
inc di ; increase offset address of array
dec bp ; decrease counter and set zeroflag if register is zero
jnz P2 ; jump if zeroflag is not set
;-------------
; increase the y position + substract 8 from x position
dec si
jnz P1
;------------------------------------
It is also possible to start the counters for the loops with 0 for to increase the counter, but then we need a compare instruction for to leave the loop, if we want to run the loop from 0 to 8. The offset addresses inside of the array are pointed independent with no relationship to the counters.
With a little modifying (put an additional jump instruction in the code and a second call) it is also possible to set a black pixel if the byte of the array is 0. But i think this simple form of a nested loop is easier to understand in the first time.
Edit: Now it is compatible for NASM.
Edit2: Maybe it is more simple to store the x,y coordinates into a known ram location for modifying and reloading inside of a loop, if many parameters are in use.
Our ram location:
Color DB ? ; define byte
X_Pos DW ? ; define word
Y_Pos DW ?
Examples for to show how to access a memory location (default segment is DS):
mov [X_Pos], ax
mov [X_Pos], bx
mov [Color], dl
mov ax, [X_Pos]
mov bx, [Y_Pos]
mov dl, [Color]
mov WORD[X_Pos], 10
inc WORD[X_Pos]
sub WORD[X_Pos], 8
mov BYTE[Color], 4
inc BYTE[Color]
Edit3:
mov dl, ax; here is error, here I set the colour
It is not possible to mix an 8 bit register with a 16 Bit register.
Note:
AX = AL+AH
DX = DL+DH
Existing instruction are:
mov dl, al
mov dl, ah
mov dx, ax
movzx dl, ax
hi i made program that will rotate two symbols - \ and / but i dont know how to set cx counter in nested loops can someone give me advice or help me ?
here is the code of that part
program:
mov ah, 0fh ; function - get video mode
int 10h
push ax ; save number of columns
push bx ; save page number
mov ah, 0 ; function - set video mode
int 10h
mov al, 0003h ;set video mode
int 10h
mov cx,10d ; Outer loop counter how many symbols rotate
mov bx,50d ; this is for delay loop
OuterLoop:
push cx
mov ah,02h
mov bh, 0 ;cursor set
mov dh, 2
mov dl, 10
int 10h
mov AH,0Ah
mov al,"/" symbol /
mov bh,0
mov cx,1
int 10h
mov cx,bx
call Delay ;delay loop
sub bx,15d
mov ah,02h
mov bh, 0
mov dh, 2 cursor set
mov dl, 10
int 10h
mov AH,0Ah
mov al,"\" ;symbol \
mov bh,0
mov cx,1
int 10h
mov cx,bx
call Delay ; another delay
sub bx,10
pop cx ; Restore current CX
loop OuterLoop
jmp START ; and after end it should jump to start where is menu with choices
it should work like this
write /
delay for example 10 sec
write \
delay 8 sec
and jump to beginning and loop
thanks for advices
this is my delay procedure
Delay PROC NEAR ;
push ds ;
push si ;
push ax ;
xor ax, ax ;AX = 0
mov ds, ax ;DS = 0
mov si, 046Ch ;
t1: mov ax, [si] ;
t2: cmp ax, [si] ;
je t2 ;
loop t1 ;
pop ax ;
pop si ;
pop ds ;
ret ;
Delay ENDP ;
i am still working on this app but it is not working all i need is this steps to be made
program : start of loop
\
delay delay 100 times
/
delay delay 80 times
loop
and after every loop to decrease delay like this 100 times 80,60,40 and so on but i dont know where to put push and pop cx because my delay procedure is working with cx. i just set cx for example to 100 and it makes delay and so on.
I see two problems:
First, bx is initialized here
mov bx,50d ; this is for delay loop
but then it gets over-written by
mov bh, 0
bh is bits 15:8 of bx
Second, in the Delay procedure, what modifies the location at [si] so that the je t2 branch falls thru? As it stands that is an infinite loop:
xor ax, ax ;AX = 0
mov ds, ax ;DS = 0
mov si, 046Ch ;
t1: mov ax, [si] ;
t2: cmp ax, [si] ;
je t2 ;
loop t1 ;
Update:
I found that address 0x46c is a BIOS address that contains time information.
http://www.osdata.com/system/physical/lowmem.htm
So the memory you are watching in that loop is a location updated by BIOS with a counter of timer ticks (counts every 54.9 milliseconds), see
http://code.google.com/p/xtideuniversalbios/source/browse/trunk/Assembly_Library/Src/Time/TimerTicks.asm?spec=svn131&r=131
So to answer your question, before calling your Delay procedure you should load CX with the count of BIOS ticks you wish to delay (time in milliseconds divided by 54.9).
For to drawing my own cursor i use the timer interrupt for to become a more steady delay on different performant CPUs.
Cursor_Speed = 7
;-------------DATA--------------------------------------
CURFLAG DB 0, 0, 0, 0
ALTVEC DD 0
;-------------CODE--------------------------------------
cli
xor ax, ax
mov es, ax
mov ebx, DWORD PTR es:[8*4] ; Get the old Vector (Offset/Segment)
mov DWORD PTR[ALTVEC], ebx ; save it
mov cs:DWORD PTR[OLDVEC], ebx
mov es:[8*4], OFFSET NEWVEC ; Set the new IRQ-Vector
mov es:[(8*4)+2], cs
mov al, 36h ; Set 18,2 Hertz(Standard)
out 43h, al
xor al, al
out 40h, al ; low
out 40h, al ; high
sti
;---------------------------------------------------
......
;---------------------------------------------------
NEWVEC: inc BYTE PTR[CURFLAG+1]
cmp BYTE PTR[CURFLAG+1], Cursor_Speed
jb short NOHIT
mov BYTE PTR[CURFLAG+1], 0
xor BYTE PTR[CURFLAG], 1
NOHIT: DB 0EAh ; jmp far
OLDVEC DD 0
;---------------------------------------------------
....
Alternativly we can wait of the vsinc with polling port 3DAh for to become a little bit lesser precice delay.
Dirk