• Welcome to Dark Bit Factory & Gravity.
 

Pong in Assembly

Started by Phoenix, September 22, 2007

Previous topic - Next topic

0 Members and 0 Guests are viewing this topic.

Phoenix

I'm attempting to create pong, in FASM. Maybe it seems quite pointless, but I want to brush my assembly skills. So far, I have been progressing quite well, until I wanted to add movement. When the I press the up key, y1 is increased, but the paddle doesn't move. Instead, a colored dot appears at the top of the screen. This dot also comes if I change y1 to, well any variable I have defined. My guess is that I'm accessing the video memory at some place where I shouldn't be doing it, but I'm unsure where. Good news though, is that nowadays I comment my code ;)
; ASM Pong
; By Phoenix September 22, 2007
org 100h                       ; COM

mov al, 13h                    ; Set up 13h resolution (320x200 256 colors)
int 10h                        ; Change to the resolution
mov ax, 0a000h                 ; 0a000h = Video memory start
mov ds, ax                     ; Move ds to the 0a000h

Render:     mov cx, 30         ; Reset the height counter
            mov bx, 0
            jmp DrawPaddle     ; Draw the first rectangle

Update:     mov bh, 0
            mov ah, 00         ; Prepare for key input
            int 16h            ; Check for input, stores it in ah
            cmp ah, 48h        ; Was it the 'up' key?
            je MoveLeft        ; If so, move!
            cmp ah, 01         ; Was it escape?
            jne Render         ; If not, then just continue to playing

mov ax, 4c00h                  ; Prepare for exit
int 21h                        ; Exit

DrawPaddle: mov ax, 320        ; Start of with the screen width in the coordinate formula
            mov bx, y1         ; Set up the next parameter in the multiplication; y value
            add bx, cx         ; Add the current vertical position to the multiplication
            mul bx             ; Calculate!
            add ax, x1         ; Add the x position to the final product
            mov si, ax         ; Set the drawing position, screen coordinates described as ScreenWidth*y+x
            mov al, 255        ; Color 255 in the default palette (White)
            mov [si], al       ; Poke the pixel into memory

            loop DrawPaddle    ; Loop until cx (rectangle height counter) is 0
            jmp Update         ; Then go back to the main loop

MoveLeft:   inc [y1]
            jmp Render         ; Go back to the main loop


; Variables
x1 db 10                       ; Player 1's x
x2 db 160                      ; Player 2's y
y1 db 100                      ; Player 1's x
y2 db 100                      ; Player 2's y
rectLoop db 0

scoretext db "              Score: $" ; The text that goes before the score, centered using spaces
score1 db "0$"                        ; Player 1's score
dash db "-$"                          ; Dash separating scores
score2 db "0$"                        ; Player 2's score


Why am I experiencing this weird behavior? How do I fix it?

Many many thanks in advance,
Martin :)

Stonemonkey

#1
You're variables are bytes but instructions like:

mov bx, y1

will load 2 bytes into the bx reg. And I had to change the colour, i tried 25 instead of 255.

can't seem to get it working with the x position and there's still a pixel that changes colour near the top.


Stonemonkey

Aha, the flashing pixel is because it's addressing that byte of screen memory instead of the y1 variable and when you press the up arrow it's incrementing the value in that.

Phoenix

#3
Aha, that's the problem. I'll change the code and see if I can get it working. Thanks :)

Edit: But how am I supposed to increase y1 if I can't type inc [y1]?

Stonemonkey


Phoenix

The dot still flashes that way :-\

Edit: Updated code, with screen clearing:

; ASM Pong
; By Phoenix September 22, 2007
org 100h                       ; COM

mov al, 13h                    ; Set up 13h resolution (320x200 256 colors)
int 10h                        ; Change to the resolution
mov ax, 0a000h                 ; 0a000h = Video memory start
mov ds, ax                     ; Move ds to the 0a000h

Main:       mov cx, 65535      ; Start the counter at the end of the screen
            mov al, 0          ; Set the color to black
            jmp ClearScreen    ; Clear the screen
Render:     mov cx, 30         ; Reset the height counter
            mov bx, 0
            jmp DrawPaddle     ; Draw the first rectangle
Update:     mov bh, 0
            mov ah, 00         ; Prepare for key input
            int 16h            ; Check for input, stores it in ah
            cmp ah, 48h        ; Was it the 'up' key?
            je MoveUp          ; If so, move!
            cmp ah, 01         ; Was it escape?
            jne Main           ; If not, then just continue to playing

mov ax, 4c00h                  ; Prepare for exit
int 21h                        ; Exit

DrawPaddle: mov ax, 320        ; Start of with the screen width in the coordinate formula
            mov bl, [y1]       ; Set up the next parameter in the multiplication; y value
            add bx, cx         ; Add the current vertical position to the multiplication
            mul bx             ; Calculate!
            add al, [x1]       ; Add the x position to the final product
            mov si, ax         ; Set the drawing position, screen coordinates described as ScreenWidth*y+x
            mov al, 255        ; Color 255 in the default palette (White)
            mov [si], al       ; Poke the pixel into memory

            loop DrawPaddle    ; Loop until cx (rectangle height counter) is 0
            jmp Update         ; Then go back to the main loop

MoveUp:     inc byte [y1]
            jmp Main           ; Go back to the main loop

ClearScreen: mov si, cx        ; Set the position to clear
             mov [si], al      ; Clear that pixel
             loop ClearScreen  ; Go to the next pixel
             jmp Render        ; Return to the main loop

; Variables
x1 db 10                       ; Player 1's x
x2 db 160                      ; Player 2's y
y1 db 100                      ; Player 1's x
y2 db 100                      ; Player 2's y

scoretext db "              Score: $" ; The text that goes before the score, centered using spaces
score1 db "0$"                        ; Player 1's score
dash db "-$"                          ; Dash separating scores
score2 db "0$"                        ; Player 2's score

Stonemonkey

Yup, since you've changed the data segment register it's looking in the wrong place for your variables. I'm not sure what the best way to deal with that is but what I've done is this when drawing the paddle:

first set up the registers and do the calculations so the registers hold the data to draw the paddle
push the DS
load the offset for the screen memory into DS
draw the paddle
pop the original value back into DS

And not to load the offset at the start of the code.

Phoenix

The dot isn't showing up now :) But the paddle won't react when I push the up key, I must be doing something wrong.

; ASM Pong
; By Phoenix September 22, 2007
org 100h                       ; COM

mov al, 13h                    ; Set up 13h resolution (320x200 256 colors)
int 10h                        ; Change to the resolution

Main:       push ds            ; Store the data segment's position on the stack, so we can get it back later
            mov ax, 0a000h     ; 0a000h = Video memory start
            mov ds, ax         ; Move ds to the 0a000h
            mov cx, 65535      ; Start the counter at the end of the screen, so we can loop through all pixels
            mov al, 0          ; Set the color to black
            jmp ClearScreen    ; Clear the screen
Render:     mov cx, 30         ; Reset the height counter, making the paddle 30px high
            jmp DrawPaddle     ; Draw the first rectangle
Update:     pop ds             ; Return the old data segment to non-video memory
            mov ah, 0          ; Prepare for key input, 0=read a key
            int 16h            ; Check for input, stores it in ah
            cmp ah, 48h        ; Was it the 'up' key?
            je MoveUp          ; If so, move!
            cmp ah, 01         ; Was it escape?
            jne Main           ; If not, then just continue to playing

mov ax, 4c00h                  ; Prepare for exit
int 21h                        ; Exit

DrawPaddle: mov ax, 320        ; Start of with the screen width in the coordinate formula
            mov bl, [y1]       ; Set up the next parameter in the multiplication; y value
            add bx, cx         ; Add the current vertical position to the multiplication
            mul bx             ; Calculate!
            add al, [x1]       ; Add the x position to the final product
            mov si, ax         ; Set the drawing position, screen coordinates described as ScreenWidth*y+x
            mov al, 255        ; Color 255 in the default palette (White)
            mov [si], al       ; Poke the pixel into memory

            loop DrawPaddle    ; Loop until cx (rectangle height counter) is 0
            jmp Update         ; Then go back to the main loop

MoveUp:     inc byte [y1]
            jmp Main           ; Go back to the main loop

ClearScreen: mov si, cx        ; Set the position to clear
             mov [si], al      ; Clear that pixel
             loop ClearScreen  ; Go to the next pixel
             jmp Render        ; Return to the main loop

; Variables
x1 db 10                       ; Player 1's x
x2 db 160                      ; Player 2's y
y1 db 100                      ; Player 1's x
y2 db 100                      ; Player 2's y

scoretext db "              Score: $" ; The text that goes before the score, centered using spaces
score1 db "0$"                        ; Player 1's score
dash db "-$"                          ; Dash separating scores
score2 db "0$"                        ; Player 2's score

Stonemonkey

You're setting DS then trying to read the values from y1 and x1, you need to get those values before you change DS.

Phoenix

Yes! That's it! Thanks a lot Stonemonkey :) Now, I just need to detect if the user is holding down a key.

rain_storm

I tried runnin this but it wont render for me I'm just getting a black screen but it still escapes normally which is a bit confusing.
But from looking at the code I dont think you should be juggling DS like that you should use ES to write to the video buffer (have look at how stosb works) and that will free up DS for reading values.

Challenge Trophies Won:

Rbz

It's a nice exercise coding those stuff, you will learn alot!

My suggestion:

- Use another segment for video address, for example "es"
- You need to setup your own vga palette, 255 it's not white color here for example.
Challenge Trophies Won:

Jim

#12
Much better off, as Rbraz says, to use 'es' segment to point at the screen.  Right now, your variables are being stored in the first few pixels of the screen!

Second, 320x200 means that bytes (db) aren't going to be big enough to hold all the x and y positions.  Much better to make them words (dw).  I can't believe fasm lets you write mov bx,[ x] when x is only a byte without warning you that you've done something silly.

Third, try using suboutines instead of jumping around.  ie. use 'jsr' to call the functions instead of 'jmp' and use 'ret' to go back to the instruction after the 'jsr'.  Define proper interfaces to these functions, for example always use ax to return a result, always use si to pass an address, cx for a count.  'Push' and 'pop' any other registers you use, to avoid corrupting things in the main loop.  Document these parameters.  It might not be important for this little program, but if you ever do anything more complex you won't regret it.

Keep at it! ;D

Jim
<edit>ez-code got the better of my example
Challenge Trophies Won:

Phoenix

I can't find any information on how to use subroutines, how do they work?

Jim

Well, instead of this

main:
  jmp draw_it
done_draw_it:
  jmp main
draw_it:
..whatever
  jmp done_draw_it

you could write

main:
  jsr draw_it
  jmp main
draw_it:
..whatever
ret


That way you can re-use draw_it from anywhere.

Jim
Challenge Trophies Won:

Phoenix

#15
FASM doesn't like that, and says that jsr is an illegal instruction. Also, jsr doesn't exist in the FASM manual. Are you using another assembler?

Jim

My apologies, it should be 'call' not 'jsr'.

Jim
Challenge Trophies Won:

Stonemonkey

Quote
I dont think you should be juggling DS like that you should use ES to write to the video buffer

I thought there'd be a better solution to it.


JSR doesn't work in fasm, CALL can be used like that though.

EDIT: oops, was reading and didn't realise there was another page in the thread.

Jim

Like I said, really sorry, Z80 and x86 is 'call', 68000 (Amiga, ST) is 'bsr' or 'jsr' ;D

Jim
Challenge Trophies Won:

Phoenix

That works, and my code looks a little better now :) However, I'm having a little problem with keyboard input. I found this code here on DBF:

in al,60h ; keyboard check
dec al   ; check for esc key

But I don't understand how that's supposed to check if escape is pushed. Is it possible to use this method for other keys as well?