News:

MASM32 SDK Description, downloads and other helpful links
MASM32.com New Forum Link
masmforum WebSite

A game in graphic mode

Started by amrac, December 28, 2009, 03:50:31 PM

Previous topic - Next topic

FORTRANS

Hi,

   If you can use the timer ticks (18.2 times a second) you
can read the value in the BIOS data segment and use it to
determine if the routine should exit.


; - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
BIOSSEG SEGMENT AT 40H  ; BIOS Data area
        ORG    6CH
ClockTic LABEL DWORD    ; 55 ms timer ticks since CPU reset
BIOSSEG ENDS

; - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
        MOV     AX,WORD PTR [ClockTic]
        MOV     [TestTic],AX
DoWork:
        ; Insert your routine here.
        MOV     AX,WORD PTR [ClockTic]
        CMP     [TestTic],AX    ; Have we used enough time to quit?
        JE      DoWork          ; No, continue.
        RET


   This gives about the same resolution you would get with
using the timer interrupt, but is a bit simpler.

   If shorter periods are required, you can try Int 15H, function 83H.
This gives about 1 millisecond resolution, if allowed by your computer.
On my computers, Windows 2000 and XP do not allow it.


        MOV     AX,8300H
        XOR     CX,CX           ; CX:DX = Microsecond count.
        MOV     DX,10000        ; 10 milliseconds
        PUSH    ES              ; ES:BX = user memory byte.
        PUSH    DS
        POP     ES
        MOV     BX,OFFSET TestTic
        INT     15H
        POP     ES
        MOV     [TestTic],0     ; Zero flag byte.
DoWork:
        ; Insert your routine here.
        CMP     [TestTic],0     ; Have we used enough time to quit?
        JE      DoWork          ; No, continue.
        RET


   If these will not work for you, you can program timer 2 to
generate a square wave of your choice of duration.  Then
have one routine run when the timer output is high and the
other when it is low.

Regards,

Steve N.

dedndave

amrac - i wonder if i could get you to explain, as well as you can, how you want the game to play
i don't understand what is supposed to happen with the keys
is there some other game that is similar ? - it kind of sounds like Centipede - lol
or - is there a game like it already written that we could try ?

also - are the comments in Portuguese ? (Brazil ?)
some of it i can understand because it's similar to Spanish  :P
i had 3 years of Latin in high school, but i don't remember very much - lol
i can use a translator
maybe that is the best way for you to convey how it is supposed to play

amrac

#17
Yes the comments are in portuguese. I think that there is an old game that is very similar. I have a block that stays on the bottom of the screen and deslocates itself to the right and to the left. The old game that I know had a ball bouncing and you had to touch the ball with the block when it touched the ground. In my game you have several balls falling, one at each time, and you have to touch the ball with the block to have ponctuation. One procedure makes the balls fall. The other procedure makes the block move . But you have to give a little time to each other and interrupt each procedure for a little time. The code that I left only had the balls falling. The code that I'm going to leave now has the procedure quadrado which draws the block to the right and to the left and the procedure pixeis which makes the balls fall. The procedures call each other but it isn't working. What is supposed to happen with the keys is to deslocate the block to the right and to the left. n for right, m for left ENTER to exit the game.


stack segment para stack
db 64 dup ('mystack' )
stack ends

mydata segment para 'data'
color db 02
lado dw 0
nivel dw 200
inicio dw 32h
coluna dw 96h
mydata ends

mycode segment para 'code'
myproc proc far
assume cs:mycode, ss:stack, ds:mydata

PUSH DS
SUB AX,AX
PUSH AX
MOV AX,MYDATA
MOV DS,AX

mov ah, 00h ; prepara para definir o mod grafico
mov al, 04  ;modo graf 320x200 color mode
int 10h ;invoca a int 10h da BIOS

mov ah,11 ;prepara configuração da palete de cores
mov bh,00 ;inicializa a cor de background
mov bl,01 ;background azul
    int 10h ;invoca a interrupção 10h da BIOS


mov ah,11 ;prepara configuração da palete de cores
mov bh,01 ;prepara conf do foreground
mov bl,00 ;verde,vermelho e amarelo
int 10h ;invoca a intrrupção 10h 10 da Bios

call rato
call quadrado ;procedimento que vai desenhar e apagar os quadrados



mov ah,00h ;prepara para definir o modo grafico
mov al,02h ;repor modo texto.80.25
int 10h ;invoca a interrupção 10h 10 da Bios
ret
myproc endp

pixeis proc near ;procedimento que vai continuamente fazer cair os pixeis
push ax
pix:call pixel ;procedimento que faz cair um pixel
mov ax,10
call MsDelay
;++++++++++++++++++++++++++++++++chama o procedimento quadrado++++++++++++++++++++++++++++++++++++++++++++++++
call quadrado

jmp pix
pop ax
pixeis endp


quadrado proc near
;desenhar o rectangulo
quad: mov dx,98h ;selecciona a linha 100
mov cx,inicio ;selecciona a coluna 158
call proc1

;++++++++++++++++++++++++++++++chama o procedimento pixeis+++++++++++++++++++++++++++++++++
call pixeis

here:
push dx
    mov ah,6  ;apenas espera
mov dl,0ffh
int 21h ;por outra tecla
pop dx
cmp al, 6eh ;se for igual a tecla n vai apagar quadrado
je apaga
cmp al,6dh ;se for igual a tecla m vai apagar quadrado
je apaga
cmp al,0dh ;se for ENTER sai do programa
je fim
push dx
    mov ah,6  ;apenas espera
mov dl,0ffh
int 21h ;por outra tecla
pop dx
cmp al, 0dh ;enquanto for diferente de ENTER não sai do programa
jne here

apaga:mov color,00
mov ax,10
call MsDelay
sub ah,ah
mov lado,ax
call proc1
mov color,02
cmp lado,6dh ;se foi escolhida a tecla para a direita vai ser desenhado o quadrado à direita
je inicio2
cmp lado,6eh ;se foi escolhida a tecla para a esquerda vai ser desenhado o quadrado à esquerda
je inicio1
inicio2:add inicio,10h
jmp quad
inicio1:sub inicio, 10h
jmp quad




fim:
    mov ah,1  ;apenas espera
int 21h ;por outra tecla

quadrado endp
proc1 proc near
mov bl,0h ;
mov bh,0h ;

um:
mov al,bl
cmp al,20 ;compara o valor que esta em AL é igual a  20
jg iniciofor2 ; salta se for igual a 20
call linha1
inc bl
jmp um

;linhas na vertical
iniciofor2:
mov bl,0h ;inicia for a zero
dois:
mov al,bl ;colocar o valor de bl em al
cmp al,30 ;compara se o valor que está no al é igual a 30
jg iniciofor3
call coluna1
inc bl
jmp dois

;linha horizontal
iniciofor3:
mov bl,0 ;coloca  o 0 no bl
tres:
mov al,bl
cmp al,20
jg iniciofor4 ; salta para inicio4 se for menor  20
call linha2 ; senão chama o procedimento linha esquerda, que é para desenhar as linhas
inc bl ;incrementa o bl
jmp tres ;salta para 3
;++++++++++++++++++++++++linha vertical em comprimentos ao anterior++++++++++++++++++++++++++++++++++++++++
iniciofor4:
mov bl,0h ;inicia  a zero
quatro:
mov al,bl
cmp al,30 ; número de vezes que faz
jg sair
call coluna2
inc bl
jmp quatro

sair:ret
cmp color,01
je muda
ret
proc1 endp ; acaba o procedimento proc1


muda proc near
mov color,02
jmp here
ret
muda endp
linha1 proc near
mov al,color ;cor dos pontos vermelho, configura
mov ah,12 ;conf int 10h para plot do pixel
inc dx ; vai incrementar o número de vezes que for chamado dentro do FOR
int 10h ;invoca a int 10h
ret
linha1 endp

linha2 proc near

mov al,color ;cor dos pontos vermelho, configura
mov ah,12 ;conf int 10h para plot do pixel
dec dx ; vai decrementar o número de vezes que for chamado dentro do FOR
int 10h ;invoca a int 10h
ret
linha2 endp


coluna2 proc near
mov al,color ;cor dos pontos vermelho, configura
mov ah,12 ;conf int 10h para plot do pixel
inc cx ; vai incrementar o número de vezes que for chamado dentro do FOR
int 10h ;invoca a int 10h
ret
coluna2 endp

coluna1 proc near
mov al,color ;cor dos pontos vermelho, configura
mov ah,12 ;conf int 10h para plot do pixel
dec cx ; vai decrementar o número de vezes que for chamado dentro do FOR
int 10h ;invoca a int 10h
ret
coluna1 endp
rato proc near    ;
mov ax,00
int 33h
mov ax,01
int 33h
ret
rato endp
pixel proc near
push ax
push bx
mov ah,2ch ;para ir buscar as centésimas de segundo
int 21h
sub dh,dh
mov ax,dx ;para fazer a multiplicação do valor por ah
mov cx,0ah
mul cx
;mov aleatorio, ax ;guarda os centesimos de segundo em aleatorio
;o resultado da multiplicação está em ax
;o resto fica em dx
;vai dividir ax por 2bch
mov bx,2bch
div bx
cmp dx,12ch ;verificar se o resto da divisao é maior q 300
jg maior
maior: mov coluna,12ch ; se o resto da divisao for maior que 300 retorna a coluna 300
mov coluna,dx ;poe o resto da divisao na coluna
pop bx
pop ax

mov dx,10h ;selecciona a linha 100
mov cx,coluna     ; seleciona a coluna


;MOV DX, 64h
MOV CX,180

Game:
PUSH CX
MOV AL, 02 ;pixel cor vermelha
MOV AH, 12
MOV CX, coluna
INT 10h

;MOV ah, 01
;INT 21h ;espera por uma tecla
mov     ax, nivel
        call    MsDelay

MOV AL, 00 ;pixel cor preto
MOV AH, 12
MOV CX, coluna ;considerando que temos 300 colunas
INT 10h

INC DX
POP CX
Loop Game
pixel endp
MsDelay proc near
; -----------------------------------------------------------
; This proc delays for the specified number of milliseconds.
; It does this by counting memory refresh requests, which
; for AT-class systems are generated by system timer 1,
; normally programmed by the BIOS to generate an output once
; every 18/1193182 = 15.09 microseconds. Each request toggles
; bit 4 of I/O port 61h. We are counting full cycles of the
; bit, with two toggles per cycle, so each cycle takes 30.18
; microseconds and we count 34 cycles for each millisecond.
;
; Call with the number of milliseconds in AX.
;
; Preserves all registers other than AX.
;
; Note that the delay will be much shorter when running
; under the NT versions of Windows.
; -----------------------------------------------------------
    push cx

    mov cx, ax            ; load msLoop count
  msLoop:
    push cx               ; preserve msLoop counter
    mov cx, 34            ; load repeat count
  wait1:
    in  al, 61h           ; read byte from port
    test al, 10h          ; test bit 4
    jz  wait1             ; jump if bit clear
  wait2:
    in  al, 61h           ; read byte from port
    test al, 10h          ; test bit 4
    jnz wait2             ; jump if bit not clear
    dec cx                ; decrement repeat count
    jnz wait1             ; jump if count not zero
    pop cx                ; recover msLoop counter
    dec cx                ; decrement msLoop count
    jnz msLoop            ; jump if count not zero

    pop cx
    ret
MsDelay endp


mycode ends
end


added code tags

dedndave

ohhh - maybe similar to BreakOut
give me some time to mess with it - i have just gotten busy, here - lol

amrac

Quote from: FORTRANS on January 05, 2010, 01:21:22 PM
Hi,

   If you can use the timer ticks (18.2 times a second) you
can read the value in the BIOS data segment and use it to
determine if the routine should exit.


; - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
BIOSSEG SEGMENT AT 40H  ; BIOS Data area
        ORG    6CH
ClockTic LABEL DWORD    ; 55 ms timer ticks since CPU reset
BIOSSEG ENDS

; - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
        MOV     AX,WORD PTR [ClockTic]
        MOV     [TestTic],AX
DoWork:
        ; Insert your routine here.
        MOV     AX,WORD PTR [ClockTic]
        CMP     [TestTic],AX    ; Have we used enough time to quit?
        JE      DoWork          ; No, continue.
        RET


   This gives about the same resolution you would get with
using the timer interrupt, but is a bit simpler.

   If shorter periods are required, you can try Int 15H, function 83H.
This gives about 1 millisecond resolution, if allowed by your computer.
On my computers, Windows 2000 and XP do not allow it.


        MOV     AX,8300H
        XOR     CX,CX           ; CX:DX = Microsecond count.
        MOV     DX,10000        ; 10 milliseconds
        PUSH    ES              ; ES:BX = user memory byte.
        PUSH    DS
        POP     ES
        MOV     BX,OFFSET TestTic
        INT     15H
        POP     ES
        MOV     [TestTic],0     ; Zero flag byte.
DoWork:
        ; Insert your routine here.
        CMP     [TestTic],0     ; Have we used enough time to quit?
        JE      DoWork          ; No, continue.
        RET


   If these will not work for you, you can program timer 2 to
generate a square wave of your choice of duration.  Then
have one routine run when the timer output is high and the
other when it is low.

Regards,

Steve N.


How do I initialize this segment in the assume directive? In the first example if I want to give the procedure a time of 55 milliseconds how do I initialize this time?

dedndave

#20
something like this...

BIOSSEG SEGMENT AT 0    ;absolute Data area
        ORG    46CH
ClockTic LABEL DWORD    ; 55 ms timer ticks since CPU reset
BIOSSEG ENDS
.
.
        .CODE
.
.
        ASSUME  DS:BIOSSEG

        push    ds
        xor     ax,ax
        mov     ds,ax
        MOV     AX,ClockTic
        pop     ds

        ASSUME  DS:DGROUP

        MOV     TestTic,AX
.
.
.

EDIT - fixed an address   :bg

dedndave

btw amrac - you should make your stack at least 512 bytes   :bg

here is a template you can use for 16-bit programs...

        .MODEL  SMALL
        .STACK  512

;------------------------------------------------------------------------
;libraries

;------------------------------------------------------------------------
;externals

;------------------------------------------------------------------------
;macros

;------------------------------------------------------------------------
;equates

;------------------------------------------------------------------------
;data

        .CONST

;constant data

        .DATA

;initialized data

Message db 'Hello World',13,10,36

        .DATA?

;uninitialized data

;------------------------------------------------------------------------
;code

        .CODE

_main   PROC    FAR

;initialize the data segment

        mov     ax,@DATA
        mov     ds,ax

;display the message

        mov     dx,offset Message
        mov     ah,9
        int     21h

;terminate program

        mov     ax,4C00h
        int     21h

_main   ENDP

;------------------------------------------------------------------------
;subroutine

;------------------------------------------------------------------------
;subroutine

;------------------------------------------------------------------------

        END     _main

dedndave

i cleaned up the file a bit and got it to assemble with no errors - now i am ready to have a look at it
i see a few PROCs with no RET   :bg
today, i have some things i need to do - tomorrow we will see if we can make things move

FORTRANS

Hi Dave,

   Typo?


        ASSUME  DS:BIOSSEG

        push    ds
        MOV     AX,40H  ; Not XOR
        mov     ds,ax
        MOV     AX,ClockTic
        pop     ds

        ASSUME  DS:DGROUP


Regards,

Steve N.

dedndave

it looks ok to me Steve
ohhhhhhhhh
you access it via 40h
i always access the BIOS data segment from segment 0000
it makes no diff in the address sizes (just add 400h)
but it saves a byte by using XOR AX,AX instead of MOV AX,40h
it also allows you to access the interrupt vector table without changing segments   :bg

amrac

Thanks for cleaning my file, that must have been a lot of work! About that other reply what does it mean:
ORG    46CH
Can't I just add the

ClockTic LABEL DWORD    in my data segment?

FORTRANS

Hi Dave,

   Okay I see what you are doing.  Oops.  The references
I have differentiate between the interrupt vector segment
at 0, and the BIOS data segment at 40H.  Should have
read a bit closer.  Good ole habits...


Hi amrac,

   No, it is a specific place in memory that is updated by
the BIOS on each timer interrupt.  It is at 40:6C or 0:46C,
which address the same piece of memory, using segment:offset.
You generally load the tick count into a register, and then
store it into a variable in your data segment.

Steve

dedndave

well - actually, you can place it in any segment
it is simply a label that has to have a specific offset
i don't know how well "SEGMENT AT" works with newer versions of masm, it wasn't great with older versions - lol
i think you can do it without a label at all like this.....

        push    ds
        xor     ax,ax
        mov     ds,ax
        mov     ax,ds:[46Ch]
        pop     ds

you shouldn't need an ASSUME directive, either

at any rate, the BIOS data segment is at 0040:0000
at offset 6Ch into that segment is the value for the clock tick (3 bytes i think)
0040:006C is the same physical address as 0000:046C
you can get it another way by using INT 1Ah (which is typically a pretty fast interrupt)

        mov     ah,0
        int     1Ah

;clock tick is in CX:DX, max value = 1800AFh

amrac

I'm sorry I must be really stupid. The thing is, I already have a data segment and I can´t have two data segments. So what are the necessary modifications that I have to make to make things work? I'm sorry to ask this again but I didn't really understand.

dedndave

well - the term "data segment" can be used in different context
in a broader definition, it might be any segment where you read or write data, including the video buffer
in a narrower definition, it is a specific section of an executable program
in this case, the BIOS code uses the area from 0040:0000 to 0040:00FF - that area is refered to as the "BIOS data segment"
a variety of low-level information is kept there
there are locations that hold the I/O addresses of serial and parallel ports
the keyboard buffer is in there, the amount of memory, and so on
a program can access the information, even though it is not really a data segment of that program

as for your program, you can have more than one data segment
it just isn't needed for most small programs because you do not have more than 64 Kb of data

the only stupid questions are the ones that aren't asked - lol