Dark Bit Factory & Gravity
PROGRAMMING => Other languages => ASM => Topic started 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 ;)
; 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 :)
-
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.
-
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.
-
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]?
-
inc byte [y1]
-
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
-
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.
-
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
-
You're setting DS then trying to read the values from y1 and x1, you need to get those values before you change DS.
-
Yes! That's it! Thanks a lot Stonemonkey :) Now, I just need to detect if the user is holding down a key.
-
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.
-
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.
-
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
-
I can't find any information on how to use subroutines, how do they work?
-
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
-
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?
-
My apologies, it should be 'call' not 'jsr'.
Jim
-
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.
-
Like I said, really sorry, Z80 and x86 is 'call', 68000 (Amiga, ST) is 'bsr' or 'jsr' ;D
Jim
-
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?
-
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.
-
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
-
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:
VSync: mov dx, 03DAh
in al,dx
test al,8
jz VSync
I have no idea why that works. What does "in" actually do?
-
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;
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 :)
-
"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 :)
-
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
-
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?
-
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.