After the initial dive into the Pong problem and with a new understanding from the experiments done a tough choice had to be made. And with that I started over. My first goal with this new start was just to get a bouncing ball
The Code for the bouncing ball iteration no longer exists but it worked moving the ball in a two step process, the first step said yes the ball should move, the second said which way it should move. This was accomplished by first splitting the movement into the X and Y and then either incriminating or decrementing depending on which surface the ball last bounced off of. The first version of this as quite basic and did not clear the old ball position and only worked at a 45 degree angle resulting in a diamond being drawn on the screen but it was a start.
The screen not being cleared was beginning to bother me and so I implemented a very simple solution, first I copied the screen clear code from the etch-a-sketch program and had it happen on every game loop.
clear: lda table_low ; clear the screen
sta POINTER
lda table_high
sta POINTER_H
ldy #$00
tya
c_loop: sta (POINTER),y
iny
bne c_loop
inc POINTER_H
ldx POINTER_H
cpx #$06
bne c_loop
second I added a delay so that the ball was drawn for longer than it was not making it so that it was less likely to blink out of existence. This had a few issues but I finally had a single ball bouncing around a screen.
Finally The game was coming together and only needed a few more elements to make it functional, the first and most important being the paddle, for this I added a new collision procedure which checked to see if the ball was on a pixel which counted as being on the paddle and if so to make it bounce. I also made it into more of a game by changing the bottom collision to game over and making it so that the x and y velocity got randomized whenever the paddle was hit. Here is the code for that itteration
; zero-page variable locations
define ROW $20 ; current row
define COL $21 ; current column
define DELTAX $30 ; current Delta X
define DELTAY $31 ; current Delta Y
define BOUNCEX $35 ; checks if X has bounced
define BOUNCEY $36 ; checks if Y has bounced
define VELX $38
define VELY $39
define POINTER $10 ; ptr start of row
define POINTER_H $11
define PADDLEL $40
define PADDLER $41
; constants
define DOT $01 ; dot colour
define PADDLE $07 ; black colour
ldy #$00 ; put help text on screen
print: lda help,y
beq setup
sta $f000,y
iny
bne print
setup: lda #$0f ; set initial ROW,COL
sta ROW
lda #$00
sta COL
lda #$20
sta VELX
lda #$20
sta VELY
lda #$0C
sta PADDLEL
lda #$14
sta PADDLER
draw: lda ROW ; ensure ROW is in range 031
and #$1f
sta ROW
lda COL ; ensure COL is in range 031
and #$1f
sta COL
ldy ROW ; load POINTER with start-of-row
lda table_low,y
sta POINTER
lda table_high,y
sta POINTER_H
ldy COL ; store CURSOR at POINTER plus COL
lda #DOT
sta (POINTER),y
drawPaddle:
ldy #$1f ; load POINTER with start-of-row
lda table_low,y
sta POINTER
lda table_high,y
sta POINTER_H
ldy PADDLEL ; store CURSOR at POINTER plus COL
lda #PADDLE
paddleLoop:
sta (POINTER),y
iny
cpy PADDLER
bne paddleLoop
colidR: lda COL
cmp #$1F
bne colidL
sta BOUNCEY
colidL: lda COL
cmp #$00
bne colidD
sta BOUNCEY
colidD: lda ROW
cmp #$1F
bne colidU
CLC
jmp gameover
colidU: lda ROW
cmp #$00
bne colidP
sta BOUNCEX
colidP: CLC
lda ROW
cmp #$1E
bne ballX
lda COL
cmp PADDLEL
bcc ballX
cmp PADDLER
bcs ballX
sta BOUNCEX
lda $fe ;randomize vel when hitting paddle
cmp #$80 ;ensure vel isn't too high
bcc velx
adc #$81
velx:
sta VELX
lda $fe
sta VELY
ballX: lda VELX
adc DELTAX
sta DELTAX
bcc ballY
CLC
lda BOUNCEX
cmp #$00
bne decROW
incROW: inc ROW
CLC
bcc ballY
decROW: dec ROW
ballY:
lda VELY
adc DELTAY
sta DELTAY
bcc getkey
CLC
lda BOUNCEY
cmp #$00
bne decCOL
incCOL: inc COL
CLC
bcc getkey
decCOL: dec COL
getkey: lda $ff ; get a keystroke
ldx #$00 ; clear out the key buffer
stx $ff
cmp #$83 ; check key == LEFT
bne checkR
ldy PADDLEL
cpy #$00
beq checkR
dec PADDLEL
dec PADDLER
jmp delaya
checkR: cmp #$81 ; check key == RIGHT
bne delaya
ldy PADDLER
cpy #$20
beq delaya
inc PADDLEL
inc PADDLER
delaya: ldy #$00 ; Delay processor so that ball doesn't flash at top of screen
ldx #$00
delay: iny
cpy #$FF
bne delay
ldy #$00
inx
cpx #$06
bne delay
clear: lda table_low ; clear the screen
sta POINTER
lda table_high
sta POINTER_H
ldy #$00
tya
c_loop: sta (POINTER),y
iny
bne c_loop
inc POINTER_H
ldx POINTER_H
cpx #$06
bne c_loop
done: clc ; repeat
jmp draw
gameover:
brk
; these two tables contain the high and low bytes
; of the addresses of the start of each row
table_high:
dcb $02,$02,$02,$02,$02,$02,$02,$02
dcb $03,$03,$03,$03,$03,$03,$03,$03
dcb $04,$04,$04,$04,$04,$04,$04,$04
dcb $05,$05,$05,$05,$05,$05,$05,$05,
table_low:
dcb $00,$20,$40,$60,$80,$a0,$c0,$e0
dcb $00,$20,$40,$60,$80,$a0,$c0,$e0
dcb $00,$20,$40,$60,$80,$a0,$c0,$e0
dcb $00,$20,$40,$60,$80,$a0,$c0,$e0
; help message on character screen
help:
dcb "A","r","r","o","w",32,"k","e","y","s"
dcb 32,"d","r","a","w",32,"/",32,"'","C","'"
dcb 32,"k","e","y",32,"c","l","e","a","r","s"
dcb 00
As you can probably tell there is still some left over fragments from the etch-a-sketch which need to be removed but all and all this code will make a working game of pong with the 6502. It draws and moves a ball, receives keyboard input and move the paddle. This is not where I decided to stop with this program though.
; zero-page variable locations
define ROW $20 ; current row
define COL $21 ; current column
define DELTAX $30 ; current Delta X
define DELTAY $31 ; current Delta Y
define BOUNCEX $35 ; checks if X has bounced
define BOUNCEY $36 ; checks if Y has bounced
define VELX $38
define VELY $39
define POINTER $10 ; ptr start of row
define POINTER_H $11
define PADDLEL $40
define PADDLER $41
define SCORE $24
define HIT $23
; constants
define DOT $01 ; dot colour
define PADDLE $07 ; black colour
ldy #$00 ; put help text on screen
print: lda help,y
beq setup
sta $f000,y
iny
bne print
setup: lda #$0f ; set initial ROW,COL
sta ROW
lda #$00
sta COL
lda #$20
sta VELX
lda #$20
sta VELY
lda #$0B
sta PADDLEL
lda #$15
sta PADDLER
lda #$00
sta SCORE
draw: lda ROW ; ensure ROW is in range 031
and #$1f
sta ROW
lda COL ; ensure COL is in range 031
and #$1f
sta COL
ldy ROW ; load POINTER with start-of-row
lda table_low,y
sta POINTER
lda table_high,y
sta POINTER_H
ldy COL ; store CURSOR at POINTER plus COL
lda #DOT
sta (POINTER),y
drawPaddle:
ldy #$1f ; load POINTER with start-of-row
lda table_low,y
sta POINTER
lda table_high,y
sta POINTER_H
ldy PADDLEL ; store CURSOR at POINTER plus COL
lda #PADDLE
paddleLoop:
sta (POINTER),y
iny
cpy PADDLER
bne paddleLoop
colidR: lda COL
cmp #$1F
bne colidL
sta BOUNCEY
colidL: lda COL
cmp #$00
bne colidD
sta BOUNCEY
colidD: lda ROW
cmp #$1F
bne colidU
CLC
jmp gameover
colidU: lda ROW
cmp #$00
bne colidP
sta BOUNCEX
colidP: CLC
lda ROW
cmp #$1E
bne incScore
lda COL
cmp PADDLEL
bcc incScore
cmp PADDLER
bcs incScore
sta BOUNCEX
inc HIT
lda $fe ;randomize vel when hitting paddle
cmp #$80 ;ensure vel isn't too high
bcc velx
adc #$81
velx:
sta VELX
lda $fe
sta VELY
incScore:
CLC
lda ROW
cmp #$1D
bne delaya
lda HIT
cmp #$00
beq delaya
lda #$00
sta HIT
SED
CLC
lda SCORE
adc #$01
sta SCORE
CLD
delaya: ldy #$00 ; Delay processor to slow down game
ldx #$00
delay: iny
cpy #$FF
bne delay
ldy #$00
inx
cpx #$08
bne delay
ballX: lda VELX
adc DELTAX
sta DELTAX
bcc ballY
ldy ROW ; load POINTER with start-of-row
lda table_low,y
sta POINTER
lda table_high,y
sta POINTER_H
ldy COL ; store CURSOR at POINTER plus COL
lda #00
sta (POINTER),y
CLC
lda BOUNCEX
cmp #$00
bne decROW
incROW: inc ROW
CLC
bcc ballY
decROW: dec ROW
ballY:
lda VELY
adc DELTAY
sta DELTAY
bcc getkey
ldy ROW ; load POINTER with start-of-row
lda table_low,y
sta POINTER
lda table_high,y
sta POINTER_H
ldy COL ; store CURSOR at POINTER plus COL
lda #00
sta (POINTER),y
CLC
lda BOUNCEY
cmp #$00
bne decCOL
incCOL: inc COL
CLC
bcc getkey
decCOL: dec COL
getkey: lda $ff ; get a keystroke
ldx #$00 ; clear out the key buffer
stx $ff
cmp #$83 ; check key == LEFT
bne checkR
ldy PADDLEL
cpy #$00
beq checkR
ldy #$1f ; load POINTER with start-of-row
lda table_low,y
sta POINTER
lda table_high,y
sta POINTER_H
ldy PADDLER ; store CURSOR at POINTER plus COL
dey
lda #00
sta (POINTER),y
dec PADDLEL
dec PADDLER
jmp done
checkR: cmp #$81 ; check key == RIGHT
bne done
ldy PADDLER
cpy #$20
beq done
ldy #$1f ; load POINTER with start-of-row
lda table_low,y
sta POINTER
lda table_high,y
sta POINTER_H
ldy PADDLEL ; store CURSOR at POINTER plus COL
lda #00
sta (POINTER),y
inc PADDLEL
inc PADDLER
done:
ldy #$0
scorePrint:
lda score,y
beq scoreNum
sta $f0F0,y
iny
bne scorePrint
scoreNum:
lda SCORE
and #$F0
LSR
LSR
LSR
LSR
TAY
lda number,y
sta $f0f8
lda SCORE
and #$0F
TAY
lda number,y
sta $f0f9
lda SCORE
clc ; repeat
jmp draw
gameover:
brk
clear: lda table_low ; clear the screen
sta POINTER
lda table_high
sta POINTER_H
ldy #$00
tya
c_loop: sta (POINTER),y
iny
bne c_loop
inc POINTER_H
ldx POINTER_H
cpx #$06
bne c_loop
jmp setup
; these two tables contain the high and low bytes
; of the addresses of the start of each row
table_high:
dcb $02,$02,$02,$02,$02,$02,$02,$02
dcb $03,$03,$03,$03,$03,$03,$03,$03
dcb $04,$04,$04,$04,$04,$04,$04,$04
dcb $05,$05,$05,$05,$05,$05,$05,$05,
table_low:
dcb $00,$20,$40,$60,$80,$a0,$c0,$e0
dcb $00,$20,$40,$60,$80,$a0,$c0,$e0
dcb $00,$20,$40,$60,$80,$a0,$c0,$e0
dcb $00,$20,$40,$60,$80,$a0,$c0,$e0
; help message on character screen
help:
dcb "A","r","r","o","w",32,"k","e","y","s"
dcb 32,"C","o","n","t","r","o","l",32,"p","a","d"
dcb "d","l","e"
dcb 00
score:
dcb "S","C","O","R","E",":",32
dcb 00
number:
dcb "0","1","2","3","4","5","6","7"
dcb "8","9","A","B","C","D","E","F"
dcb 00
This is where I decided to cut off coding for this task with quite a few tweeks and improvments to the code. The First big change is the removing of the flicker, I did this by only removing the ball when it moves and only removing one pixel from the paddle when it mves. Next I added a score feature which was an interesting task to tackel. Firstly I had to find a way to incriment the score which was accomplished by checking first to see in the paddle had been hit and second to see if the ball was off the paddle and if both those things were true the score could be incrimented. Secondly I chose to make the score decimal instead of Hex since that is the number system most people are used to. Luckily through so research I was able to find out about decimal mode on the 6502, which allows numbers to be stored in a byte as two decimal digits taking up 4 bits per digit. thus allowing the score to be properly relayed to the player. All together this made the pong app both easier on the eyes and more enjoyable since progress was tacked.
The process of building this app has furthered my understanding of assembly programming quite a bit. I feel that in gerneral in order to get a grasp for many of the concepts they just have to be played with. Some of the odd quirks and how the computer actually works with the bits is something which is difficult to learn without experienceing it and I feel this task accomplished that.
Friday, January 31, 2020
Thursday, January 30, 2020
Lab 3 - Pong pt.1
For this Lab we have begun to build off of our knowledge in 6502 assembly in order to make a more robust program. We were given five options in tasks to do and with very little extra help had to figure out how to achieve an effective result. The five options were to create a bouncing graphic (think dvd logo), to create a numeric display which displayed two digits, to create the game pong, to create a kaleidoscope where one quadrant is mirrored in the other three, lastly and most challenging to draw a line between to points that can be moved around in real time.
Our group chose to work on Pong since it seemed like an enjoyable app to create and at least a couple of us had a bit of a grasp on how they wanted to tackle the problem. We started off by looking at some example code that was provided for us for a fairly unrelated program but it allowed us to get some good ideas for how to create out code (Link to Example Code). This code specifically helped us with Three things.
; zero-page variable locations
define DOTROW $20 ; current row
define DOTCOL $21 ; current column
define DOTDELTAX $30 ; current Delta X
define DOTDELTAY $31 ; current Delta Y
define POINTER $10 ; ptr: start of row
define POINTER_H $11
; constants
define DOT $01 ; dot colour
define CURSOR $04 ; black colour
ldy #$00 ; put help text on screen
print: lda help,y
beq setup
sta $f000,y
iny
bne print
setup: lda #$0f ; set initial ROW,COL
sta DOTROW
lda #$02
sta DOTCOL
lda #$20 ;set angle to 45
sta DOTDELTAX
sta DOTDELTAY
game: lda DOTROW ; ensure ROW is in range 0:31
and #$1f
sta DOTROW
lda DOTCOL ; ensure COL is in range 0:31
and #$1f
sta DOTCOL
ldy DOTROW ; load POINTER with start-of-row
lda table_low,y
sta POINTER
lda table_high,y
sta POINTER_H
pha ; save A
lda #DOT ; set current position to DOT
sta (POINTER),y
pla ; restore A
DotMovA:lda DOTCOL
inc DOTCOL
lda DOTCOL
DotMovB:lda DOTROW
inc DOTROW
lda DOTROW
done: clc ; repeat
bcc game
; these two tables contain the high and low bytes
; of the addresses of the start of each row
table_high:
dcb $02,$02,$02,$02,$02,$02,$02,$02
dcb $03,$03,$03,$03,$03,$03,$03,$03
dcb $04,$04,$04,$04,$04,$04,$04,$04
dcb $05,$05,$05,$05,$05,$05,$05,$05,
table_low:
dcb $00,$20,$40,$60,$80,$a0,$c0,$e0
dcb $00,$20,$40,$60,$80,$a0,$c0,$e0
dcb $00,$20,$40,$60,$80,$a0,$c0,$e0
dcb $00,$20,$40,$60,$80,$a0,$c0,$e0
; help message on character screen
help:
dcb "A","r","r","o","w",32,"k","e","y","s"
dcb 32,"d","r","a","w",32,"/",32,"'","C","'"
dcb 32,"k","e","y",32,"c","l","e","a","r","s"
dcb 00
The code above is greatly unaltered from the etch-a-sketch code. All it does is use that code to draw a line across the screen continuously but It allowed us to learn quite a lot about how to get a ball moving across the screen since it practically is that just without the previous position being removed thus a line is drawn. So from that base it is quite simple to begin to work out how a ball will move properly such as in pong which will be talked about in the next Blog.
Our group chose to work on Pong since it seemed like an enjoyable app to create and at least a couple of us had a bit of a grasp on how they wanted to tackle the problem. We started off by looking at some example code that was provided for us for a fairly unrelated program but it allowed us to get some good ideas for how to create out code (Link to Example Code). This code specifically helped us with Three things.
- How to turn a screen made of pages into coordinates
- How to use those coordinates to draw on the screen
- How to take keyboard input
; zero-page variable locations
define DOTROW $20 ; current row
define DOTCOL $21 ; current column
define DOTDELTAX $30 ; current Delta X
define DOTDELTAY $31 ; current Delta Y
define POINTER $10 ; ptr: start of row
define POINTER_H $11
; constants
define DOT $01 ; dot colour
define CURSOR $04 ; black colour
ldy #$00 ; put help text on screen
print: lda help,y
beq setup
sta $f000,y
iny
bne print
setup: lda #$0f ; set initial ROW,COL
sta DOTROW
lda #$02
sta DOTCOL
lda #$20 ;set angle to 45
sta DOTDELTAX
sta DOTDELTAY
game: lda DOTROW ; ensure ROW is in range 0:31
and #$1f
sta DOTROW
lda DOTCOL ; ensure COL is in range 0:31
and #$1f
sta DOTCOL
ldy DOTROW ; load POINTER with start-of-row
lda table_low,y
sta POINTER
lda table_high,y
sta POINTER_H
pha ; save A
lda #DOT ; set current position to DOT
sta (POINTER),y
pla ; restore A
DotMovA:lda DOTCOL
inc DOTCOL
lda DOTCOL
DotMovB:lda DOTROW
inc DOTROW
lda DOTROW
done: clc ; repeat
bcc game
; these two tables contain the high and low bytes
; of the addresses of the start of each row
table_high:
dcb $02,$02,$02,$02,$02,$02,$02,$02
dcb $03,$03,$03,$03,$03,$03,$03,$03
dcb $04,$04,$04,$04,$04,$04,$04,$04
dcb $05,$05,$05,$05,$05,$05,$05,$05,
table_low:
dcb $00,$20,$40,$60,$80,$a0,$c0,$e0
dcb $00,$20,$40,$60,$80,$a0,$c0,$e0
dcb $00,$20,$40,$60,$80,$a0,$c0,$e0
dcb $00,$20,$40,$60,$80,$a0,$c0,$e0
; help message on character screen
help:
dcb "A","r","r","o","w",32,"k","e","y","s"
dcb 32,"d","r","a","w",32,"/",32,"'","C","'"
dcb 32,"k","e","y",32,"c","l","e","a","r","s"
dcb 00
The code above is greatly unaltered from the etch-a-sketch code. All it does is use that code to draw a line across the screen continuously but It allowed us to learn quite a lot about how to get a ball moving across the screen since it practically is that just without the previous position being removed thus a line is drawn. So from that base it is quite simple to begin to work out how a ball will move properly such as in pong which will be talked about in the next Blog.
Sunday, January 26, 2020
Lab 2 - 6502 Experiments
The next few entries on this blog are going to be detailing our look into learning assembly code on the 6502 processor.
we were provided the following code
we were provided the following code
lda #$00 ; set a pointer at $40 to point to $0200
sta $40
lda #$02
sta $41
lda #$07 ; colour
ldy #$00 ; set index to 0
loop: sta ($40),y ; set pixel
iny ; increment index
bne loop ; continue until done the page
inc $41 ; increment the page
ldx $41 ; get the page
cpx #$06 ; compare with 6
bne loop ; continue until done all pages
This code will Fill the page with the colour Yellow by looping through the each address of a
page and setting it to yellow, and then incriminating the page until the screen is filled.
When we insert the command tya into the code at the start of the loop the screen fills with
strips of colour. This is because that command will transfer the value of y into a, this value
will loop every 16 colours since there are only 16 colour values, and the screen is 32 pixels so
it loops perfectly and lines up. this is also why the colours repeat.
Adding in the lsr command now will shift the bits in the colour to the right, and as such
remove the least significant digit. This results in an effective division by 2 and so the colours
appear twice as thick. Adding more will result in further division and as such further thickening
instead using asl we will multiply by two instead this reduces the unique values which the
colours can be but they remain 1 pixel thick.
Next we will see what happens when we add more iny. This will result in an interesting
change in which the y values skips ahead 5 times each loop. this will miss the esacpe value
and overflow and will continue doing so until the page is filled in an interesting grainy way.
The final experiment which was done in this lab was to see if we could get 4 lines drawn
across the edges of the screen
lda #$00 ; set a pointer at $40 to point to $0200
sta $40
lda #$02
sta $41
lda #$05 ; colour
ldy #$00 ; set index to 0
loopa:
sta ($40),y ; set pixel
iny ; increment index
cpy #$20 ; compare with 32
bne loopa ; continue until done the page
ldy #$00
loops:
CLC
lda #$07
sta ($40),y ; set pixel
TYA
adc #$1f
TAY
lda #$04
sta ($40),y ; set pixel
iny
cpy #$00
bne loops
inc $41 ; increment the page
ldx $41 ; get the page
cpx #$06 ; compare with 6
bne loops ; continue until done all pages
ldy #$E0
lda #$0e
dec $41
loopb:
sta ($40),y ; set pixel
iny ; increment index
cpy #$00 ; compare with 32
bne loopb ; continue until done the page
This code will write 4 lines across the 4 edges of the screen. It does so with 3 loops.
The first loop will loop across the addresses at the top of the screen inserting the colour into
those addresses
the second loop which is also the most involved will insert a pixel into the first address of a
line and then add to the cursor the 31 which brings it to the last pixel of the line, drawing a
different colour and then adding one again to start at the beginning once more. Once it has
gotten to the end of a page it will increment to the next page, resulting in two vertical lines.
lastly now that we are on the last page we can draw the final line at the bottom by starting at
the first pixel on the last line and looping through till the end of the line.
These tree loops result in the three lines being successfully drawn.
Friday, January 17, 2020
Lab 1 - Open Source Research
In my search for open source Software I decided to look into two which I have used in the past and continue to use to this day. Those being Firefox and GIMP.
Firefox being the software I'm currently using to display this page has come a long way in large part thanks to its open source community. There is a vast number of people bug hunting and bug fixing, as well as a fairly understandable code review process. The example which I looked at can be seen here:
https://phabricator.services.mozilla.com/D48202
This is a simple bug fix which was approved back in October of 2019, It was written by a single contributor and reviewed by a single reviewer, being either the module owner or a designated peer, before being accepted onto the main branch.
The other piece of software, an image editing tool known as GIMP also takes the open source approach. To get a contribution added to GIMP, you must like Firefox make a fork then make a merge request. This request is then viewed by a developer at GIMP for review and any tweaks that need to be made will be made as well as receiving community feedback. After the code has been finalized or approved in its current state by the developer, the code is merged.
https://gitlab.gnome.org/GNOME/gimp/merge_requests/195
These two ways of merging contributions are similar but show the difference in scale of the two projects. Firefox most likely receives far more merge requests than GIMP, thus forcing them to spread out their commit privilege to the community where as GIMP can afford to only allow employees to merge.
Firefox being the software I'm currently using to display this page has come a long way in large part thanks to its open source community. There is a vast number of people bug hunting and bug fixing, as well as a fairly understandable code review process. The example which I looked at can be seen here:
https://phabricator.services.mozilla.com/D48202
This is a simple bug fix which was approved back in October of 2019, It was written by a single contributor and reviewed by a single reviewer, being either the module owner or a designated peer, before being accepted onto the main branch.
The other piece of software, an image editing tool known as GIMP also takes the open source approach. To get a contribution added to GIMP, you must like Firefox make a fork then make a merge request. This request is then viewed by a developer at GIMP for review and any tweaks that need to be made will be made as well as receiving community feedback. After the code has been finalized or approved in its current state by the developer, the code is merged.
https://gitlab.gnome.org/GNOME/gimp/merge_requests/195
These two ways of merging contributions are similar but show the difference in scale of the two projects. Firefox most likely receives far more merge requests than GIMP, thus forcing them to spread out their commit privilege to the community where as GIMP can afford to only allow employees to merge.
Subscribe to:
Posts (Atom)