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:

Lab 3 Instruction
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

Popular posts from this blog

Open Source Project: Lab2 (Pull Request)

Release 4.0 - Part 3: Release

Lab 8: Automated Testing and Continuous Integration