Lab 3: Bouncing Ball (Final)
Introduction
I discovered an error in the previous posting's code. In this post, I will show what error I found and how I fix it. Also, I have improved my code so it becomes more reusable. You can understand the steps on how I develop this code to read previous postings:
The option my group choose is bouncing graphic. The instructions of this option are the following:
3. Implementation
Whole Code
define POINTER $10
define POINTER_H $11
define ROW_COUNT $13
define ROW $14
define COLUMN $15
define CHECK_LINE $16
define ROWFLIP_FLAG $17
define COLFLIP_FLAG $18
define PTR_CALCULATE $19
define DATA_INDEX $20
define SCREENCOL_INDEX $21
; variables for key
define UP $80
define DOWN $82
define RIGHT $81
define LEFT $83
; variable for last line check
define LASTLINE $23
; variables for graph
define WIDTH 7
define HEIGHT 7
; ======= base initialize ======
lda #$14 ; Set a row value
sta ROW
lda #$04 ; Set a column value
sta COLUMN
lda #$00
sta ROWFLIP_FLAG
sta COLFLIP_FLAG
; last line check calculation
lda #$20
sec
sbc #WIDTH
sta LASTLINE
; ====== Calculation ======
; ====== Initialize for calculation ======
cal_initialize:
lda #$00 ; create a pointer at $10
sta POINTER
lda #$02
sta POINTER_H
lda #$00
sta ROW_COUNT
lda #$00
sta PTR_CALCULATE
cmp ROW
beq column_count
bne row_count
;====== row count =======
row_count:
lda PTR_CALCULATE
clc
adc #$20
sta PTR_CALCULATE
lda POINTER_H
adc #$00
sta POINTER_H
inc ROW_COUNT
lda ROW
cmp ROW_COUNT
bne row_count
beq column_count
;===== column_count =====
column_count:
lda PTR_CALCULATE
clc
adc COLUMN
sta PTR_CALCULATE
;===== store the value to pointer (calculation done) =====
sta POINTER
;===== draw graphic =====
; initializing for drawing graph
lda #$00
sta ROW_COUNT
ldx #$00 ; index for data
ldy #$00 ; index for screen column
; draw graph
draw: lda ball,x
sta (POINTER),y
inx
iny
cpy #WIDTH
bne draw
inc ROW_COUNT
lda #HEIGHT
cmp ROW_COUNT
beq getkey_initialize
lda POINTER
clc
adc #$20
sta POINTER
lda POINTER_H
adc #$00
sta POINTER_H
ldy #$00
beq draw
; ======= get key =======
getkey_initialize:
txa
sta DATA_INDEX
tya
sta SCREENCOL_INDEX
getkey: lda $ff ; get a keystroke
ldx #$00 ; clear out the key buffer
stx $ff
cmp #$80 ; if not a cursor key, ignore
bmi getkey_done
cmp #$84
bpl getkey_done
cmp #UP ; check key == up
bne rightKey_check
jsr rowPointCheck_dec ; ... if yes, decrement ROW
jmp getkey_done
rightKey_check:
cmp #RIGHT ; check key == right
bne downKey_check
jsr colPointCheck_inc ; ... if yes, increment COL
jmp getkey_done
downKey_check:
cmp #DOWN ; check if key == down
bne leftKey_check
jsr rowPointCheck_inc ; ... if yes, increment ROW
jmp getkey_done
leftKey_check:
cmp #LEFT ; check if key == left
bne getkey_done
jsr colPointCheck_dec ; ... if yes, decrement COL
clc
bcc getkey_done
getkey_done:
ldx DATA_INDEX
ldy SCREENCOL_INDEX
jmp check_location
; ======= Check the location ======
check_location:
jsr check_top
jsr check_bottom
jsr check_right
jsr check_left
jsr move_pointer
check_initialize:
lda #$00
sta CHECK_LINE
rts
check_top:
jsr check_initialize
lda ROW
cmp #$01
lda CHECK_LINE
adc #$00
cmp #$00
beq flip_rowFlag
rts
check_bottom:
jsr check_initialize
lda ROW
cmp LASTLINE
lda CHECK_LINE
adc #$00
cmp #$01
beq flip_rowFlag
rts
check_left:
jsr check_initialize
lda COLUMN
cmp #$01
lda CHECK_LINE
adc #$00
cmp #$00
beq flip_colFlag
rts
check_right:
jsr check_initialize
lda COLUMN
cmp LASTLINE
lda CHECK_LINE
adc #$00
cmp #$01
beq flip_colFlag
rts
; ======= Flip Row Flag ======
flip_rowFlag:
lda ROWFLIP_FLAG
cmp #$00
beq inc_rowFlag
bne dec_rowFlag
rts
inc_rowFlag:
inc ROWFLIP_FLAG
rts
dec_rowFlag:
dec ROWFLIP_FLAG
rts
; ======= Flip Col Flag ======
flip_colFlag:
lda COLFLIP_FLAG
cmp #$00
beq inc_colFlag
bne dec_colFlag
rts
inc_colFlag:
inc COLFLIP_FLAG
rts
dec_colFlag:
dec COLFLIP_FLAG
rts
; ======= move the graph ========
move_pointer:
jsr row_check
jsr col_check
jmp clear
row_check:
lda ROWFLIP_FLAG
cmp #$01
beq rowPointCheck_dec
bne rowPointCheck_inc
col_check:
lda COLFLIP_FLAG
cmp #$01
beq colPointCheck_dec
bne colPointCheck_inc
rowPointCheck_inc:
lda ROW
cmp LASTLINE
bne inc_row
rts
inc_row:
inc ROW
rts
rowPointCheck_dec:
lda ROW
cmp #$00
bne dec_row
rts
dec_row:
dec ROW
rts
colPointCheck_inc:
lda COLUMN
cmp LASTLINE
bne inc_col
rts
inc_col:
inc COLUMN
rts
colPointCheck_dec:
lda COLUMN
cmp #$00
bne dec_col
rts
dec_col:
dec COLUMN
rts
; ======= clear the screen before redraw the graph =====
clear: lda ball
sta POINTER
lda #$02
sta POINTER_H
ldy #$00
tya
clear_loop:
sta (POINTER),y
iny
bne clear_loop
inc POINTER_H
ldx POINTER_H
cpx #$06
bne clear_loop
jsr cal_initialize
; data constant byte
ball:
dcb 00,03,03,03,03,03,00
dcb 03,06,07,07,07,06,03
dcb 03,06,07,07,07,06,03
dcb 03,07,07,07,07,07,03
dcb 03,07,06,07,06,07,03
dcb 03,07,07,06,07,07,03
dcb 00,03,03,03,03,03,00
4. What I have learned from the lab
Even though the code is working, it is possible that there are some errors. Before I write the code, it is better to think about what should be checked to make my code successfully working. Also, the testing part is important to see whether I need to fix some parts or not.
I discovered an error in the previous posting's code. In this post, I will show what error I found and how I fix it. Also, I have improved my code so it becomes more reusable. You can understand the steps on how I develop this code to read previous postings:
- Lab 3: Bouncing Graphic (Part 1 - Calculate Pointer)
- Lab 3: Bouncing Graph - Drawing and Moving Graph in the Pointer Location
- Lab 3: Bouncing Graph - Done
The option my group choose is bouncing graphic. The instructions of this option are the following:
- Create a simple graphic in a square that is 5*5 or 7*7 pixels in size. Use the colors available in the emulator's bit-mapped display. The graphic could be a ball a happy face, a logo, an emoji, or anything else that you want to use
- Encode that graphic in bytes using DBC (declare constant byte) instructions
- Write code to make the graphic bounce around the screen, reflecting off the edges when it hits
- Make the speed keyboard-adjustable (faster/slower) and perturb the object's path once in a while
1. Error
1.1. Error
I have tested my code several times and I found the following error: when the user tried to move the graph using the key, the error sometimes happens because it does not check the row or column is on the last line or not. The range for row and column should be from #$00 to '#$20 - the width/height of the graph'. However, there is no code to check this point.
1.2. How to fix the error
To fix this error, I created subroutines to check whether the row or column is on the last line or not. The checkpoints to fix this error are the following:
- When the user tries to move the graph to right: check the column value. When the value is '#$20 - the width of the graph', nothing happens and go back to the previous subroutine
- When the user tries to move the graph to left: check the column value. When the value is #$00, nothing happens and go back to the previous subroutine
- When the user tries to move the graph to down: check the row value. When the value is '#$20 - the width of the graph', nothing happens and go back to the previous subroutine
- When the user tries to move the graph to up: check the row value. When the value is #$00, nothing happens and go back to the previous subroutine
To apply these checkpoints, I created the following subroutines:
- rowPointCheck_inc
- rowPointCheck_dec
- colPointCheck_inc
- colPointCheck_dec
Therefore, the recent code will check the row and column whether is it the last line or not instead of directly increasing or decreasing the value.
1.3. Code
The previous code of 'getting the key' part is the following:
Instead of directly increasing or decreasing the value, it goes to the subroutine to check the value using the instruction jsr. The highlighted code is the edited code.
The added subroutines and the code are the following:
The whole code is written below.
2. Improvement
2.1. The Steps I Think
I would like to change the size of the graph from 5 to 7. At the time, I found it takes some time to change to values to check the last line because the last line value will be changed depending on the size of the graph (e.i. If the graph is 5*5, the last line check value will be #$1b. When the size is 7*7, the last line check value will be #$19). If I make a variable to store the last line value and calculate the value in my code, my code can be more reusable.
2.2. How to Apply It
To apply this improvement, I need to define a variable to store the value and calculate it. The way to calculate is: #$20 - #$width/height. In this lab, the width and height are the same so I chose the width value to calculate the last line value.
2.3. Code
define the last line variable and calculate it in the base initialize part.
The whole code is written below.
3. Implementation
Whole Code
define POINTER $10
define POINTER_H $11
define ROW_COUNT $13
define ROW $14
define COLUMN $15
define CHECK_LINE $16
define ROWFLIP_FLAG $17
define COLFLIP_FLAG $18
define PTR_CALCULATE $19
define DATA_INDEX $20
define SCREENCOL_INDEX $21
; variables for key
define UP $80
define DOWN $82
define RIGHT $81
define LEFT $83
; variable for last line check
define LASTLINE $23
; variables for graph
define WIDTH 7
define HEIGHT 7
; ======= base initialize ======
lda #$14 ; Set a row value
sta ROW
lda #$04 ; Set a column value
sta COLUMN
lda #$00
sta ROWFLIP_FLAG
sta COLFLIP_FLAG
; last line check calculation
lda #$20
sec
sbc #WIDTH
sta LASTLINE
; ====== Calculation ======
; ====== Initialize for calculation ======
cal_initialize:
lda #$00 ; create a pointer at $10
sta POINTER
lda #$02
sta POINTER_H
lda #$00
sta ROW_COUNT
lda #$00
sta PTR_CALCULATE
cmp ROW
beq column_count
bne row_count
;====== row count =======
row_count:
lda PTR_CALCULATE
clc
adc #$20
sta PTR_CALCULATE
lda POINTER_H
adc #$00
sta POINTER_H
inc ROW_COUNT
lda ROW
cmp ROW_COUNT
bne row_count
beq column_count
;===== column_count =====
column_count:
lda PTR_CALCULATE
clc
adc COLUMN
sta PTR_CALCULATE
;===== store the value to pointer (calculation done) =====
sta POINTER
;===== draw graphic =====
; initializing for drawing graph
lda #$00
sta ROW_COUNT
ldx #$00 ; index for data
ldy #$00 ; index for screen column
; draw graph
draw: lda ball,x
sta (POINTER),y
inx
iny
cpy #WIDTH
bne draw
inc ROW_COUNT
lda #HEIGHT
cmp ROW_COUNT
beq getkey_initialize
lda POINTER
clc
adc #$20
sta POINTER
lda POINTER_H
adc #$00
sta POINTER_H
ldy #$00
beq draw
; ======= get key =======
getkey_initialize:
txa
sta DATA_INDEX
tya
sta SCREENCOL_INDEX
getkey: lda $ff ; get a keystroke
ldx #$00 ; clear out the key buffer
stx $ff
cmp #$80 ; if not a cursor key, ignore
bmi getkey_done
cmp #$84
bpl getkey_done
cmp #UP ; check key == up
bne rightKey_check
jsr rowPointCheck_dec ; ... if yes, decrement ROW
jmp getkey_done
rightKey_check:
cmp #RIGHT ; check key == right
bne downKey_check
jsr colPointCheck_inc ; ... if yes, increment COL
jmp getkey_done
downKey_check:
cmp #DOWN ; check if key == down
bne leftKey_check
jsr rowPointCheck_inc ; ... if yes, increment ROW
jmp getkey_done
leftKey_check:
cmp #LEFT ; check if key == left
bne getkey_done
jsr colPointCheck_dec ; ... if yes, decrement COL
clc
bcc getkey_done
getkey_done:
ldx DATA_INDEX
ldy SCREENCOL_INDEX
jmp check_location
; ======= Check the location ======
check_location:
jsr check_top
jsr check_bottom
jsr check_right
jsr check_left
jsr move_pointer
check_initialize:
lda #$00
sta CHECK_LINE
rts
check_top:
jsr check_initialize
lda ROW
cmp #$01
lda CHECK_LINE
adc #$00
cmp #$00
beq flip_rowFlag
rts
check_bottom:
jsr check_initialize
lda ROW
cmp LASTLINE
lda CHECK_LINE
adc #$00
cmp #$01
beq flip_rowFlag
rts
check_left:
jsr check_initialize
lda COLUMN
cmp #$01
lda CHECK_LINE
adc #$00
cmp #$00
beq flip_colFlag
rts
check_right:
jsr check_initialize
lda COLUMN
cmp LASTLINE
lda CHECK_LINE
adc #$00
cmp #$01
beq flip_colFlag
rts
; ======= Flip Row Flag ======
flip_rowFlag:
lda ROWFLIP_FLAG
cmp #$00
beq inc_rowFlag
bne dec_rowFlag
rts
inc_rowFlag:
inc ROWFLIP_FLAG
rts
dec_rowFlag:
dec ROWFLIP_FLAG
rts
; ======= Flip Col Flag ======
flip_colFlag:
lda COLFLIP_FLAG
cmp #$00
beq inc_colFlag
bne dec_colFlag
rts
inc_colFlag:
inc COLFLIP_FLAG
rts
dec_colFlag:
dec COLFLIP_FLAG
rts
; ======= move the graph ========
move_pointer:
jsr row_check
jsr col_check
jmp clear
row_check:
lda ROWFLIP_FLAG
cmp #$01
beq rowPointCheck_dec
bne rowPointCheck_inc
col_check:
lda COLFLIP_FLAG
cmp #$01
beq colPointCheck_dec
bne colPointCheck_inc
rowPointCheck_inc:
lda ROW
cmp LASTLINE
bne inc_row
rts
inc_row:
inc ROW
rts
rowPointCheck_dec:
lda ROW
cmp #$00
bne dec_row
rts
dec_row:
dec ROW
rts
colPointCheck_inc:
lda COLUMN
cmp LASTLINE
bne inc_col
rts
inc_col:
inc COLUMN
rts
colPointCheck_dec:
lda COLUMN
cmp #$00
bne dec_col
rts
dec_col:
dec COLUMN
rts
; ======= clear the screen before redraw the graph =====
clear: lda ball
sta POINTER
lda #$02
sta POINTER_H
ldy #$00
tya
clear_loop:
sta (POINTER),y
iny
bne clear_loop
inc POINTER_H
ldx POINTER_H
cpx #$06
bne clear_loop
jsr cal_initialize
; data constant byte
ball:
dcb 00,03,03,03,03,03,00
dcb 03,06,07,07,07,06,03
dcb 03,06,07,07,07,06,03
dcb 03,07,07,07,07,07,03
dcb 03,07,06,07,06,07,03
dcb 03,07,07,06,07,07,03
dcb 00,03,03,03,03,03,00
4. What I have learned from the lab
Even though the code is working, it is possible that there are some errors. Before I write the code, it is better to think about what should be checked to make my code successfully working. Also, the testing part is important to see whether I need to fix some parts or not.
Comments
Post a Comment