Dark Bit Factory & Gravity

PROGRAMMING => Other languages => ASM => Topic started by: Phoenix on September 22, 2007

Title: Pong in Assembly
Post by: Phoenix on September 22, 2007
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 ;)
Code: [Select]
; 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 :)
Title: Re: Pong in Assembly
Post by: Stonemonkey on September 22, 2007
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.

Title: Re: Pong in Assembly
Post by: Stonemonkey on September 22, 2007
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.
Title: Re: Pong in Assembly
Post by: Phoenix on September 22, 2007
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]?
Title: Re: Pong in Assembly
Post by: Stonemonkey on September 22, 2007
inc byte [y1]
Title: Re: Pong in Assembly
Post by: Phoenix on September 22, 2007
The dot still flashes that way :-\

Edit: Updated code, with screen clearing:
Code: [Select]
; 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
Title: Re: Pong in Assembly
Post by: Stonemonkey on September 22, 2007
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.
Title: Re: Pong in Assembly
Post by: Phoenix on September 22, 2007
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.
Code: [Select]
; 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
Title: Re: Pong in Assembly
Post by: Stonemonkey on September 22, 2007
You're setting DS then trying to read the values from y1 and x1, you need to get those values before you change DS.
Title: Re: Pong in Assembly
Post by: Phoenix on September 22, 2007
Yes! That's it! Thanks a lot Stonemonkey :) Now, I just need to detect if the user is holding down a key.
Title: Re: Pong in Assembly
Post by: rain_storm on September 23, 2007
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.
Title: Re: Pong in Assembly
Post by: Rbz on September 23, 2007
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.
Title: Re: Pong in Assembly
Post by: Jim on September 23, 2007
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
Title: Re: Pong in Assembly
Post by: Phoenix on September 23, 2007
I can't find any information on how to use subroutines, how do they work?
Title: Re: Pong in Assembly
Post by: Jim on September 23, 2007
Well, instead of this
Code: [Select]
main:
  jmp draw_it
done_draw_it:
  jmp main
draw_it:
 ..whatever
  jmp done_draw_it
you could write
Code: [Select]
main:
  jsr draw_it
  jmp main
draw_it:
 ..whatever
 ret

That way you can re-use draw_it from anywhere.

Jim
Title: Re: Pong in Assembly
Post by: Phoenix on September 23, 2007
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?
Title: Re: Pong in Assembly
Post by: Jim on September 23, 2007
My apologies, it should be 'call' not 'jsr'.

Jim
Title: Re: Pong in Assembly
Post by: Stonemonkey on September 23, 2007
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.
Title: Re: Pong in Assembly
Post by: Jim on September 23, 2007
Like I said, really sorry, Z80 and x86 is 'call', 68000 (Amiga, ST) is 'bsr' or 'jsr' ;D

Jim
Title: Re: Pong in Assembly
Post by: Phoenix on September 23, 2007
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:
Code: [Select]
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?
Title: Re: Pong in Assembly
Post by: Stonemonkey on September 23, 2007
I think that because the code for ESC is 1, if you decrement it the zero flag wil be set so you then test that to see if ESC has been pressed.
Title: Re: Pong in Assembly
Post by: Jim on September 23, 2007
http://flint.cs.yale.edu/cs421/papers/art-of-asm/pdf/APNDXC.PDF (http://flint.cs.yale.edu/cs421/papers/art-of-asm/pdf/APNDXC.PDF)
Absolutely, Esc is 1 so subtracting 1 will set the z flag and there you go.

You can use in,60h if you like but I think using a DOS call might be better.
Ralf Brown http://www.ctyme.com/rbrown.htm (http://www.ctyme.com/rbrown.htm) has a web page which no DOS programmer can ignore. 15 years ago, that guy was a hero for building this list.  I wonder what he does these days?

Jim
Title: Re: Pong in Assembly
Post by: Phoenix on September 23, 2007
I decided to use the "in al, 60h" method; it works, and that's good enough for me ;) I'm slowly progressing with this thing now, and I found some v sync code by Rbraz:
Code: [Select]
VSync: mov dx, 03DAh
       in al,dx
       test al,8
       jz VSync
I have no idea why that works. What does "in" actually do?
Title: Re: Pong in Assembly
Post by: Shockwave on September 23, 2007
in is a command that samples input from various ports on the computer, the operand on the left is the byte location that you want to store the incoming data and the right operand is the port number.

You could use in like this too;

Code: [Select]
in al,60h
dec al
jnz main

To check if escape had been pressed for instance. There are loads of ports that you can look at to do different stuff :)
Title: Re: Pong in Assembly
Post by: Rbz on September 23, 2007
"IN reads a byte, word or doubleword from the specified I/O port, and stores it in the given destination register. The port number may be specified as an immediate value if it is between 0 and 255, and otherwise must be stored in DX."

Have fun :)
Title: Re: Pong in Assembly
Post by: rain_storm on September 23, 2007
Reading input from port 60h is useful when you are optimising for size since it saves one byte compared with int 16h. This meathod is useful in tiny code (256 byte demos) but size is not an issue with this program so as Jim said you should just read the keys via int 16h. Unless you want to experiment with reading ports in which case I say go right ahead
Title: Re: Pong in Assembly
Post by: Phoenix on September 25, 2007
Oddly enough, everything is rendered just fine with one exception. Everything that's drawn at the top ~30 pixels is invisible or flashing heavily. Does anyone know what the problem might be? Should I post the source?
Title: Re: Pong in Assembly
Post by: rain_storm on September 25, 2007
That could be due to a number of reasons

The segment register may not be set to 0A000h when writing to video memory
The offset may be incorrect
Your monitor might not be vertically centered for 320x200 8bpp resolutions

Post what you have so far and we can eliminate some possibilites if that doesnt work try setting your monitors vertical-center / horizontal-center whilst in mode 13h. I often have to do this for my monitor it  seems its only mode 13h & FreeBasic programs that are an issue for me.