I am trying to sort an array by using functions that finds the smallest number in an array, and the other swaps two variables. But for some reason the array doesn't change and stays the same.
I thing I have a problem with the stack but I can't find it.
This is my code: sorry its long and not organized. I just started assembly.
`
org 100h
jmp start
array db 1,9,3,6,3 **;should get 1,2,3,6,9**
min_in_array dw ?
start:
lea si, array
push 5
push si
call sortArray
pop bx
pop bx
mov ah, 0
int 16h
ret
;doesn't work
PROC sortArray
push bp
mov bp, sp
mov dx, 0 ;the index
mov cx, [bp+6];size
mov di, [bp+4];array
loop_arr:
add di, dx
push di
call findMin
pop di
sub di, dx
add di, min_in_array
push dx
push di
call swap
pop di
pop dx
sub di, min_in_array
inc dx
mov ax, [di]
loop loop_arr
mov sp, bp
pop bp
ret
ENDP sortArray
;works
PROC findMin
push bp
mov bp, sp
sub sp, 4
mov cx, 0 ;init the counter
mov di, [bp+4]
mov al, [bp-2] ;initialising the min save var
mov al, [di]
mov bx, [bp-4] ;the index to save
mov bx, 0
run:
cmp al, [di]
ja change_min
cmp cx, 4 ;check if cx is lower than the size of the array
inc cx ;+1
inc di ;move forward in the array
jb run ;check again
jmp fin ;finished - cx = size
change_min:
mov al, [di] ;change the min
mov bx, cx ;assign the index
inc di
cmp cx, 4
je fin
inc cx
jmp run
fin:
mov sp, bp
pop bp
mov cx, 0
mov min_in_array, bx
ret
ENDP findMin
;function works
PROC swap
;creates fram
push bp
mov bp,sp
sub sp,2 ;make space for local temp
mov bx, [bp+6]
mov cx, [bp+4]
;swaping using the temp varaiable
mov [bp-2], bx
mov bx, cx
mov cx, [bp-2]
;close frame
mov sp, bp
pop bp
ret
ENDP swap
`
I have a problem with the stack but I can't find it.
Your use of the stack is actually fine: prologue, epilogue, cleaning.
But there's one additional thing that the stack is good at, and that's preserving register values. Your sortArray procedure depends on the CX register, but you destroy its value in the findMin and swap procedures!
You say findMin works, but I assure you it doesn't. Other than not preserving CX and containing a lot of unused code, findMin always processes 5 elements eventhough the supplied address moved up with each invokation, meaning you are processing garbage that follows the original array in memory. Moreover the result that you store in the min_in_array variable is an offset in the unsorted partition of the array, but after returning, sortArray will use this same value as an offset in the original array. Can't work that way...
You say swap works, but I assure you it doesn't. What you supply to this procedure is an offset in the original array and an (wrongly calculated) address within the original array. You fetch these arguments from the stack and only swap these in registers, nothing more. You never read/write in the array, so there's no swapping taking place.
See if next code points you in the right direction:
add di, dx
push cx ; Number of elements in the unsorted partition
push di ; Address of the start of the unsorted partition
call findMin ; -> AX (BL CX SI)
pop di
pop cx
push ax ; Address of the Min from the unsorted partition
push di ; Address of the start of the unsorted partition
call swap
...
; IN () OUT (ax) MOD (bl,cx,si)
PROC findMin
push bp
mov bp, sp
mov cx, [bp + 6]
mov si, [bp + 4]
min:
mov bl, [si]
mov ax, si
run:
dec cx
jz done
inc si
cmp bl, [si]
jb run
jmp min ; Go change min
done:
pop bp
ret
ENDP findMin
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.
Welcome.
I could't get values from 2d array. I am trying and trying and it doesn't work. I use NASM 16 DOS
I want to have blue letter E on red backgroung. I only get square random colours.
'1' in array is blue,'4' is red. It could be spinned, but it doesn't matter.
I have no idea what is wrong. I'm trying to do this a couple od days.
Here is my 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 si, T34 ; my array
mov word [Y_Start], 10 ; Y start line count values in register
mov word [X_Start], 20 ; X start line
mov word [Y], 10 ; Y start line
mov word [X], 20 ; X start line
mov dx, 4 ; colour (red)
mov cx, 8 ; loop top counter
top:
add word [Y], 1
push cx ;loop top counter =
mov cx, 8 ;inside loop counter
inside:
add word [X], 1
;////////////////////////////////////////////// count colour
push ax ; remember value before procedure
push bx
push cx
push dx
mov ax, word [X]
mov cx,8
mul cx ;X*8
add ax, word[Y] ; X*8 +Y
mov cx,4
mul cx ; (X*8+Y)*4
add ax, si ; tab0 + (X*8+Y)*4
pop dx
mov dx, ax; colour
pop cx
pop bx
pop ax
;///////////////////////////////////////////
mov ax, word [Y]
mov bx, word [X]
push ax ; remember value before procedure
push bx
push cx
call putpixel
pop cx ; go back to old values
pop bx
pop ax
loop inside
mov bx, word [X_Start] ; move to X start
mov word [X], bx ;
pop cx ;loop top counter
loop top
;;;;;;;;;;;;;;;;;;;;;;;;;
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], dx ; store color/pixel
ret
;;;;;;;;;;;;;;;;;;;;;;
X_Start DW 0 ; start letter position
Y_Start DW 0
X DW 0 ;current letter position
Y DW 0
T34 DB 1,1,1,1,1,1,1,1
DB 1,4,4,4,4,4,4,4
DB 1,4,4,4,4,4,4,4
DB 1,4,4,4,4,4,4,4
DB 1,1,1,1,1,1,1,1
DB 1,4,4,4,4,4,4,4
DB 1,4,4,4,4,4,4,4
DB 1,1,1,1,1,1,1,1
I didn't catch your approach, but I can show you how I would do it:
segment .code
..start:
mov ax, data ; Initialize DS (needed for .exe-program)
mov ds, ax
mov ax, 0A000h ; Segment to video memory
mov es, ax
mov ax, 13h
int 10h ; Switch to 320x200 mode
mov si, T34 ; DS:SI = letter
mov bx, [X_Start]
mov ax, [Y_Start]
call vector_to_memory
mov di, ax ; ES:DI = video memory
call paint
; once more
add word [X_Start], 8 ; That's one small step for a man
mov si, T34 ; DS:SI = letter
mov bx, [X_Start]
mov ax, [Y_Start]
call vector_to_memory
mov di, ax ; ES:DI = video memory
call paint
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
paint: ; Args: DS:SI = letter, ES:DI: video memory
mov ax, 8
.l1:
mov cx, 8 ; repetition count for REP
rep movsb ; CX times (one row): [DS:SI] => [ES:DI], SI++, DI++
add di, (320-8) ; next row
sub ax, 1 ; no row left?
jnz .l1 ; no: once more
ret ; yes: paint is done, return
vector_to_memory: ; Args: BX = X, AX = Y
push dx ; mul changes dx too
mov cx, 320 ; video mode width
mul cx ; DX:AX = AX * CX
add ax, bx ; left indentation
pop dx
ret ; Return: AX = offset in memory
segment .data
T34:
DB 1,1,1,1,1,1,1,1
DB 1,4,4,4,4,4,4,4
DB 1,4,4,4,4,4,4,4
DB 1,4,4,4,4,4,4,4
DB 1,1,1,1,1,1,1,1
DB 1,4,4,4,4,4,4,4
DB 1,4,4,4,4,4,4,4
DB 1,1,1,1,1,1,1,1
X_Start DW 10 ; start letter position
Y_Start DW 20
segment stack stack
resb 0x1000
HTH
You're not loading the pixel values correctly:
add ax, si ; tab0 + (X*8+Y)*4
mov dx, ax; colour <-- Here you're just copying the _address_, you're
not loading the value stored at that address.
Those two instructions should be changed to:
add si,ax
mov dl,[si]
Note the use of dl since you're dealing with bytes. I also switched the add around because [ax] wouldn't be a valid effective address. This means that you'll have to reload si with the address of the array on every iteration of the loop.
The line mov [es:di], dx in putpixel should also be changed to mov [es:di], dl.
Some of your multiplications could be simplified: to multiply a register by 4 you can do shl reg,2, and to multiply by 8 you can do shl reg,3.
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