###################
# Snake Game code #
###################

#<< INFO

##############
# MEMORY MAP #
##############
#<<
# ADDR		A1 A0 CS	WR						RD

# 0xFFF 	1  1  0		UART tx					UART rx
# 0xFFE     1  0  0		UART tx					UART status

# 0xFFD     0  1  1		GPIO A out				GPIO A out		  
# 0xFFC     0  0  1		GPIO A out  			GPIO A in 

# 0xFFB     0  1  2		ENCODER 				NU
# 0xFFA     1  0  2	    NU						NU 
# 0xFF9     0  1  2		NU 						NU
# 0xFF8     0  0  2		NU 						NU

# 0xFF7     1  1  3		VGA 					VGA
# 0xFF6     1  0  3		VGA TILE_Y_REG
# 0xFF5     0  1  3		VGA TILE_X_REG
# 0xFF4     0  0  3		VGA CPU_DST_ADDR_REG
# 0xFF3     1  1  3		VGA CPU_COLOUR_REG		VGA TILE_POS
# 0xFF2     1  0  3 	VGA CPU_DATA_IN_REG		VGA CPU_DATA_OUT_REG
# 0xFF1     0  1  3		VGA CPU_SRC_ADDR_REG
# 0xFF0     0  0  3		VGA CPU_CTL_REG 		VGA CPU_STATUS_REG 

# 0xFEF     1  1  MEM	RAM						RAM
# ...					...						...
# 0x000		0  0  MEM	RAM						RAM
#>>

##################
# UART REGISTERS #
##################
#<<
# TX : B7 - B0 data
# RX : B7 - B0 data 

# STATUS REGISTER
# ---------------
# B7 : NU
# B6 : NU
# B5 : NU
# B4 : NU
# B3 : NU
# B2 : TX Idle
# B1 : RX Idle
# B0 : RX Valid
#>>

###############
# ASCII CODES #
###############
#<<
# Dec Hex Char                           Dec Hex Char      Dec Hex Char     Dec Hex Char
# ------------                           ------------      ------------     ------------
#  0  00  NUL (null)                      32  20  SPACE     64  40  @        96   60  `
#  1  01  SOH (start of heading)          33  21  !         65  41  A        97   61  a
#  2  02  STX (start of text)             34  22  "         66  42  B        98   62  b
#  3  03  ETX (end of text)               35  23  #         67  43  C        99   63  c
#  4  04  EOT (end of transmission)       36  24  $         68  44  D        100  64  d
#  5  05  ENQ (enquiry)                   37  25  %         69  45  E        101  65  e
#  6  06  ACK (acknowledge)               38  26  &         70  46  F        102  66  f
#  7  07  BEL (bell)                      39  27  '         71  47  G        103  67  g
#  8  08  BS  (backspace)                 40  28  (         72  48  H        104  68  h
#  9  09  TAB (horizontal tab)            41  29  )         73  49  I        105  69  i
# 10  0A  LF  (NL line feed, new line)    42  2A  *         74  4A  J        106  6A  j
# 11  0B  VT  (vertical tab)              43  2B  +         75  4B  K        107  6B  k
# 12  0C  FF  (NP form feed, new page)    44  2C  ,         76  4C  L        108  6C  l
# 13  0D  CR  (carriage return)           45  2D  -         77  4D  M        109  6D  m
# 14  0E  SO  (shift out)                 46  2E  .         78  4E  N        110  6E  n
# 15  0F  SI  (shift in)                  47  2F  /         79  4F  O        111  6F  o
# 16  10  DLE (data link escape)          48  30  0         80  50  P        112  70  p
# 17  11  DC1 (device control 1)          49  31  1         81  51  Q        113  71  q
# 18  12  DC2 (device control 2)          50  32  2         82  52  R        114  72  r
# 19  13  DC3 (device control 3)          51  33  3         83  53  S        115  73  s
# 20  14  DC4 (device control 4)          52  34  4         84  54  T        116  74  t
# 21  15  NAK (negative acknowledge)      53  35  5         85  55  U        117  75  u
# 22  16  SYN (synchronous idle)          54  36  6         86  56  V        118  76  v
# 23  17  ETB (end of trans. block)       55  37  7         87  57  W        119  77  w
# 24  18  CAN (cancel)                    56  38  8         88  58  X        120  78  x
# 25  19  EM  (end of medium)             57  39  9         89  59  Y        121  79  y
# 26  1A  SUB (substitute)                58  3A  :         90  5A  Z        122  7A  z
# 27  1B  ESC (escape)                    59  3B  ;         91  5B  [        123  7B  {
# 28  1C  FS  (file separator)            60  3C  <         92  5C  \        124  7C  |
# 29  1D  GS  (group separator)           61  3D  =         93  5D  ]        125  7D  }
# 30  1E  RS  (record separator)          62  3E  >         94  5E  ^        126  7E  ~
# 31  1F  US  (unit separator)            63  3F  ?         95  5F  _        127  7F  DEL
#>>

#####################
# TERMINAL COMMANDS #
#####################
#<<
# Common serial port speeds 
# Bit rate (bit/s)  Time per bit (μs)  Common applications 
# ----------------  -----------------  -------------------
# 75 	            13333.3 	 	
# 110 	            9090.9 	     	   Bell 101 modem
# 134.5 	        7434.9 	  	
# 150 	            6666.6 	 	
# 300 	            3333.3 	       	   Bell 103 modem or V.21 modem
# 600 	            1666.7 	  	
# 1,200 	        833.3 	  	       Bell 202, Bell 212A, or V.22 modem
# 1,800 	        555.6 	  	
# 2,400 	        416.7 	  	       V.22bis modem
# 4,800 	        208.3 	     	   V.27ter modem
# 7,200 	        138.9 	  	
# 9,600 	        104.2 	  	       V.32 modem
# 14,400 	        69.4 	     	   V.32bis modem
# 19,200 	        52.1 	  	
# 31,250 	        32 	  	           MIDI port
# 38,400 	        26.0 	  	
# 56,000 	        17.9 	  	       V.90/V.92 modem
# 57,600 	        17.4 	  	       V.32bis modem with V.42bis compression   ****
# 76,800 	        13.0 	  	       BACnet MS/TP networks[20]
# 115,200 	        8.68 	  	       V.34 modem with V.42bis compression, 
#                                      low cost serial V.90/V.92 modem with V.42bis or V.44 compression 
#
# screen /dev/ttyS0 19200,cs8
# screen /dev/ttyUSB0 57600
#
# Screen Command 	  Task
# --------------      ----
# Ctrl+a c 	          Create new window
# Ctrl+a k 	          Kill the current window / session
# Ctrl+a w            List all windows
# Ctrl+a 0-9 	      Go to a window numbered 0 9, use Ctrl+a w to see number
# Ctrl+a Ctrl+a 	  Toggle / switch between the current and previous window
# Ctrl+a S 	          Split terminal horizontally into regions and press Ctrl+a c to create new window there
# Ctrl+a :resize 	  Resize region
# Ctrl+a :fit 	      Fit screen size to new terminal size. You can also hit Ctrl+a F for the the same task
# Ctrl+a :remove 	  Remove / delete region. You can also hit Ctrl+a X for the same taks
# Ctrl+a tab 	      Move to next region
# Ctrl+a D (Shift-d)  Power detach and logout
# Ctrl+a d 	          Detach but keep shell window open
# Ctrl-a Ctrl-\ 	  Quit screen
# Ctrl-a ? 	          Display help screen i.e. display a list of commands
#>>

#########################
# Sprite Data Structure #
#########################
#<<
# VISIBLE     : 0 = not visible, 1 = visible
# AUTO        : 0 = direction fixed, 1 = direction reversed (limit collisions)
# TILE_INC    : 0 = fixed tile number, 1 = incrementing tile number
# TILE_NUMBER : select tile address 0 - 7, 3bit value
# BG_COLOUR   : background colour, 8bit value
# FG_COLOUR   : foreground colour, 8bit value
# XPOS        : x position of top left pixel of tile, 8bit value
# YPOS        : y position of top left pixel of tile, 8bit value
# XOFFSET     : x offset, 8bit value
# YOFFSET     : y offset, 8bit value
# HB_XMAX     : hit box, x max, 8bit value
# HB_XMIN     : hit box, x min, 8bit value
# HB_YMAX     : hit box, y max, 8bit value
# HB_YMIN     : hit box, y min, 8bit value
# LT_XMAX     : limit, x max, 8bit value
# LT_XMIN     : limit, x min, 8bit value
# LT_YMAX     : limit, y max, 8bit value
# LT_YMIN     : limit, y min, 8bit value

# Sprite macro : initialize_sprite
# --------------------------------

# initialize_sprite( 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26 )

# VISIBLE, AUTO TILE_INC TILE_NUMBER, TILE_ADDR_0, TILE_ADDR_1, TILE_ADDR_2, TILE_ADDR_3, TILE_ADDR_4, TILE_ADDR_5, TILE_ADDR_6, TILE_ADDR_7, 
# 1        2    3        4            5            6            7            8            9            10           11           12       

# BG COLOUR, FG COLOUR, XPOS, YPOS, XOFFSET, YOFFSET, HB_XMAX, HB_XMIN, HB_YMAX, HB_YMIN, LT_XMAX, LT_XMIN, LT_YMAX, LT_YMIN,
# 13         14         15    16    17       18       19       20       21       22       23       24       25       26

#                                                               32 16 8 421 
# 0  : VISIBLE : AUTO : TILE_INC : TILE_NUMBER (1:1:1:3)      - 3  4  5 210
# 1  : TILE_ADDRESS_0                    
# 2  : TILE_ADDRESS_1
# 3  : TILE_ADDRESS_2
# 4  : TILE_ADDRESS_3
# 5  : TILE_ADDRESS_4
# 6  : TILE_ADDRESS_5
# 7  : TILE_ADDRESS_6
# 8  : TILE_ADDRESS_7
# 9  : BG COLOUR : FG COLOUR		(8:8)
# 10 : XPOS : YPOS					(8:8)
# 11 : XOFFSET : YOFFSET			(8:8)
# 12 : HB_XMAX : HB_XMIN			(8:8)
# 13 : HB_YMAX : HB_YMIN		    (8:8)
# 14 : LT_XMAX : LT_XMIN			(8:8)
# 15 : LT_YMAX : LT_YMIN		    (8:8)

# Sprite macro : turn_off_sprite
# ------------------------------
# sprite data initialised in sequential memory locations. SPRITE_BASE_ADDR is address of first sprite.
# SPRITE_NUMBER is offest from this address i.e. multiple of 16. Clear VISIBILITY flag.

#  turn_off_sprite( 1, 2 )

# SPRITE_BASE_ADDR, SPRITE_NUMBER
# 1                 2                           

# Sprite macro : turn_off_sprite
# ------------------------------
# sprite data initialised in sequential memory locations. SPRITE_BASE_ADDR is address of first sprite.
# SPRITE_NUMBER is offest from this address i.e. multiple of 16. Set VISIBILITY flag.

#  turn_on_sprite( 1, 2 )

# SPRITE_BASE_ADDR, SPRITE_NUMBER
# 1                 2                            

# Sprite macro : set_sprite
# -------------------------
# sprite data initialised in sequential memory locations. SPRITE_BASE_ADDR is address of first sprite.
# SPRITE_NUMBER is offest from this address i.e. multiple of 16. Set SPRITE tile (0-7).

#  set_sprite( 1, 2, 3 )

# SPRITE_BASE_ADDR, SPRITE_NUMBER, VAL
# 1                 2              3              

# Sprite macro : increment_sprite
# -------------------------------
# sprite data initialised in sequential memory locations. SPRITE_BASE_ADDR is address of first sprite.
# SPRITE_NUMBER is offest from this address i.e. multiple of 16. Increment sprite tile (0-7) loop round on overflow.

#  increment_sprite( 1, 2 )

# SPRITE_BASE_ADDR, SPRITE_NUMBER
# 1                 2                            

# Sprite macro : get_sprite
# -------------------------
# sprite data initialised in sequential memory locations. SPRITE_BASE_ADDR is address of first sprite.
# SPRITE_NUMBER is offest from this address i.e. multiple of 16. Get current sprite tile (0-7).

#  get_sprite( 1, 2 )

# SPRITE_BASE_ADDR, SPRITE_NUMBER
# 1                 2   

# Sprite macro : position_sprite
# ------------------------------
# sprite data initialised in sequential memory locations. SPRITE_BASE_ADDR is address of first sprite.
# SPRITE_NUMBER is offest from this address i.e. multiple of 16. Set sprite XY position on display

#  position_sprite( 1, 2, 3, 4 )

# SPRITE_BASE_ADDR, SPRITE_NUMBER, XVAL, YVAL
# 1                 2              3     4              

# Sprite macro : offset_sprite
# ----------------------------
# sprite data initialised in sequential memory locations. SPRITE_BASE_ADDR is address of first sprite.
# SPRITE_NUMBER is offest from this address i.e. multiple of 16. Set sprite XY offset i.e. distance moved during auto update

#  offset_sprite( 1, 2, 3, 4 )

# SPRITE_BASE_ADDR, SPRITE_NUMBER, XVAL, YVAL
# 1                 2              3     4              

# Sprite macro : delete_sprite
# ----------------------------
# sprite data initialised in sequential memory locations. SPRITE_BASE_ADDR is address of first sprite.
# SPRITE_NUMBER is offest from this address i.e. multiple of 16. Delete sprite i.e. zero sprite data

#  delete_sprite( 1, 2 )

# SPRITE_BASE_ADDR, SPRITE_NUMBER
# 1                 2                

# Sprite macro : draw_sprite
# --------------------------
# sprite data initialised in sequential memory locations. SPRITE_BASE_ADDR is address of first sprite.
# SPRITE_NUMBER is offest from this address i.e. multiple of 16. Position, colour defined in sprite data
 
# draw_sprite( 1, 2 )

# SPRITE_BASE_ADDR, SPRITE_NUMBER
# 1                 2                                                     

# Sprite macro : overlay_sprite
# -----------------------------
# sprite data initialised in sequential memory locations. SPRITE_BASE_ADDR is address of first sprite.
# SPRITE_NUMBER is offest from this address i.e. multiple of 16. Position, colour defined in sprite data
 
# overlay_sprite( 1, 2 )

# SPRITE_BASE_ADDR, SPRITE_NUMBER
# 1                 2     

# Sprite macro : move_sprite
# --------------------------
# sprite data initialised in sequential memory locations. SPRITE_BASE_ADDR is address of first sprite.
# SPRITE_NUMBER is offest from this address i.e. multiple of 16. Update sprite position using offset, 
# check limits, stop if hit, change direction if AUTO set.

# move_sprite( 1, 2 )

# SPRITE_BASE_ADDR, SPRITE_NUMBER
# 1                 2     

# Sprite macro : clear_sprite
# ---------------------------
# sprite data initialised in sequential memory locations. SPRITE_BASE_ADDR is address of first sprite.
# SPRITE_NUMBER is offest from this address i.e. multiple of 16. 

# clear_sprite( 1, 2 )

# SPRITE_BASE_ADDR, SPRITE_NUMBER
# 1                 2     

# Sprite macro : collision_sprite
# -------------------------------
# sprite data initialised in sequential memory locations. SPRITE_BASE_ADDR is address of first sprite.
# SPRITE_NUMBER is offest from this address i.e. multiple of 16. The data in this data structure is then
# compared to the data in the seond SPRITE_BASE_ADDR and SPRITE_NUMBER.

# collision_sprite( 1, 2, 3, 4 )

# SPRITE_BASE_ADDR_A, SPRITE_NUMBER_A, SPRITE_BASE_ADDR_B, SPRITE_NUMBER_B 
# 1                   2                3                   4                     
#
#>>
#>>

#<< MAIN

########
# MAIN #
########

start:
    clear_screen( 0 )            # clear screen
    call init_game               # setup game variable

    call update_display          # draw snake
    call food                    # draw food 

loop:
    call update                  # read keyboard, update state and display
    call delayVeryShort          # delay

    load RA food_eaten           # is food eaten flag set
    and RA 0xFF 
    jumpz test                   # no test finish flag

    move RA 0                    # clr flag
    store RA food_eaten
    call inc_player_score        # inc score
    call food                    # draw new food

test:
    load RA finish               # is finished flag set
    and RA 0xFF
    jumpz loop                   # no loop

    call draw_end_screen         # draw game over message
    call delayLonger             # wait
    jump start                   # restart

trap:
    jump trap
#>>

#<< SUBROUTINES

####################
# GAME SUBROUTINES #
####################
#<<
# ---------------
# -- INIT GAME --
# ---------------

init_game:
    move RA 0                      # zero main variables 
    store RA snake_x_tail_ptr
    store RA snake_y_tail_ptr
    store RA key
    store RA num
    store RA player_score
    store RA finish
    store RA food_eaten

    # set initial snake size to six segments

    movea( RA, snake_x_tail_ptr )  # set tail pointer
    store RA listTailPtr
    movea( RA, snake_x_pos )       # set list address
    store RA listAddr

    move RA 80                     # starting x pos
    store RA x_pos
    move RA 0
    store RA count                 # loop count

init_loopx:
    load RA x_pos
    store RA listData
    call listAppendHead

    load RA x_pos                   # snake segments four pixels in size
    add RA 4
    store RA x_pos

    load RA count
    add RA 1
    store RA count
    sub RA SNAKE_LENGTH            # repeat for snake length
    jumpnz init_loopx

    movea( RA, snake_y_tail_ptr )     # set index address
    store RA listTailPtr
    movea( RA, snake_y_pos )       # set list address
    store RA listAddr

    move RA 72                     # starting y pos
    store RA y_pos
    move RA 0
    store RA count

init_loopy:
    load RA y_pos
    store RA listData
    call listAppendHead

    load RA count                  # repeat for snake length
    add RA 1
    store RA count
    sub RA SNAKE_LENGTH
    jumpnz init_loopy

    # draw screen edge

    draw_box( TURQUOISE, 4, 17, 154, 19 )
    draw_box( TURQUOISE, 4, 114, 154, 116 )
    draw_box( TURQUOISE, 4, 17, 6, 114 )
    draw_box( TURQUOISE, 152, 17, 154, 114 ) 

    # draw score 

	draw_tile( LIGHT_GREY, eval( SCORE_X_POSITION + ( 7 * 0 ) ), SCORE_Y_POSITION, s_CHAR )
	draw_tile( LIGHT_GREY, eval( SCORE_X_POSITION + ( 7 * 1 ) -1 ), SCORE_Y_POSITION, c_CHAR )
	draw_tile( LIGHT_GREY, eval( SCORE_X_POSITION + ( 7 * 2 ) -1 ), SCORE_Y_POSITION, o_CHAR )
	draw_tile( LIGHT_GREY, eval( SCORE_X_POSITION + ( 7 * 3 ) ), SCORE_Y_POSITION, r_CHAR )
	draw_tile( LIGHT_GREY, eval( SCORE_X_POSITION + ( 7 * 4 ) +1 ), SCORE_Y_POSITION, e_CHAR )

	draw_tile( LIGHT_GREY, eval( SCORE_X_POSITION + ( 7 * 6 ) -2 ), SCORE_Y_POSITION, zero_CHAR )
	draw_tile( LIGHT_GREY, eval( SCORE_X_POSITION + ( 7 * 7 ) -1 ), SCORE_Y_POSITION, zero_CHAR )
	draw_tile( LIGHT_GREY, eval( SCORE_X_POSITION + ( 7 * 8 ) ), SCORE_Y_POSITION, zero_CHAR )

    ret

# ----------
# - UPDATE -
# ----------
 
update:
    load RA snake_x_pos
    store RA snake_x_data          # buffer current x pos
    load RA snake_y_pos
    store RA snake_y_data          # buffer current y pos

    call read_keyboard             # read last key pressed

    load RA key                    # test last key pressed  
    sub RA 0x1B                  
    jumpz update_down
    sub RA 1                       # 0x1C
    jumpz update_left
    sub RA 1                       # 0x1D
    jumpz update_up
    sub RA 6                       # 0x23
    jumpz update_right
    jump update_exit

update_right:
    load RA snake_x_data           #  
    add RA 4
    store RA snake_x_data
    jump update_check_snake

update_down:
    load RA snake_y_data           #
    add RA 4
    store RA snake_y_data
    jump update_check_snake

update_left:
    load RA snake_x_data           #
    sub RA 4
    store RA snake_x_data
    jump update_check_snake

update_up:
    load RA snake_y_data           #
    sub RA 4
    store RA snake_y_data

update_check_snake:
    move RA 0                      # has snake hit itself check
    store RA listIndex 

update_check_snake_loop:
    movea( RA, snake_x_pos )       # get pixel x 
    store RA listAddr
   
    call listGetIndex

    load RA listData
    store RA x_pos         

    movea( RA, snake_y_pos )        # get pixel y
    store RA listAddr
   
    call listGetIndex

    load RA listData
    store RA y_pos  

    load RA snake_x_data            # check x pos
    subm RA x_pos
    jumpnz update_check_snake_next

    load RA snake_y_data            # check y pos
    subm RA y_pos
    jumpnz update_check_snake_next

update_check_snake_hit:
    move RA 0xFF
    store RA finish                 # set finish flag
    jump update_exit                # snake hit snake finish

update_check_snake_next:
    load RA listIndex               # inc count
    add RA 1
    store RA listIndex
    subm RA snake_x_tail_ptr        # is count > tail pointer
    jumpnz update_check_snake_loop

update_check_edge:
    load RA snake_x_data            # has snake hit ede boundary
    sub RA SNAKE_LEFT_LIMIT
    jumpc update_check_edge_hit

    load RA snake_x_data
    moved( RB, SNAKE_RIGHT_LIMIT )
    sub RB RA
    jumpc update_check_edge_hit

    load RA snake_y_data
    sub RA SNAKE_TOP_LIMIT
    jumpc update_check_edge_hit

    load RA snake_y_data
    moved( RB, SNAKE_BOTTOM_LIMIT )
    sub RB RA
    jumpc update_check_edge_hit

    jump update_add_segment

update_check_edge_hit:
    move RA 0xFF
    store RA finish               # set finish flag
    jump update_exit              # snake hit edge finish 

update_add_segment:
    movea( RA, snake_x_pos )       # move segments to next position
    store RA listAddr
    movea( RA, snake_x_tail_ptr )     
    store RA listTailPtr
    load RA snake_x_data           # set data
    store RA listData

    call listAppendHead

    movea( RA, snake_y_pos )       # set list address
    store RA listAddr
    movea( RA, snake_y_tail_ptr )  # set tail pointer
    store RA listTailPtr
    load RA snake_y_data           # set data
    store RA listData

    call listAppendHead

update_check_food:
    load RA snake_x_data           # has snake hit food, check x pos
    subm RA food_x_pos
    jumpnz update_blank

    load RA snake_y_data           # has snake hit food, check y pos
    subm RA food_y_pos
    jumpnz update_blank

update_hit_food:
    move RA 0xFF
    store RA food_eaten            # set food eaten flag
    jump update_display            # update display

update_blank:    
    movea( RA, snake_x_pos )       # get tail x pos
    store RA listAddr
    movea( RA, snake_x_tail_ptr )       
    store RA listTailPtr

    call listGetTail

    load RA listData
    store RA x_pos

    movea( RA, snake_y_pos )       # get tail y pos
    store RA listAddr
    movea( RA, snake_y_tail_ptr )     
    store RA listTailPtr

    call listGetTail

    load RA listData
    store RA y_pos
 
    call drawBlank                  # clear old tail position pixel

    movea( RA, snake_x_pos )        
    store RA listAddr
    movea( RA, snake_x_tail_ptr )     
    store RA listTailPtr

    call listDeleteTail             # remove old x position from list

    movea( RA, snake_y_pos )
    store RA listAddr
    movea( RA, snake_y_tail_ptr )   
    store RA listTailPtr

    call listDeleteTail             # remove old y position from list

update_display:
    move RA 0                       # update pixels
    store RA listIndex

update_display_loop:
    movea( RA, snake_x_pos )        # get pixel x 
    store RA listAddr
   
    call listGetIndex

    load RA listData
    store RA x_pos         

    movea( RA, snake_y_pos )        # get pixel y
    store RA listAddr
   
    call listGetIndex

    load RA listData
    store RA y_pos  

    call drawSegment                # draw snake segment                

    load RA listIndex               # inc count
    add RA 1
    store RA listIndex

    subm RA snake_x_tail_ptr        # is count > index
    jumpnz update_display_loop

update_exit:
    ret

# -------------
# - DRAW FOOD -
# -------------

food:
    moved( RA, WIDTH )
    store RA num
    call random
    load RA num
    store RA food_x_pos

    moved( RA, HEIGHT )
    store RA num
    call random
    load RA num
    store RA food_y_pos

food_check_snake:
    move RA 0                      # check that new position is not in snake
    store RA listIndex 

food_check_snake_loop:
    movea( RA, snake_x_pos )       # get pixel x 
    store RA listAddr
   
    call listGetIndex

    load RA listData
    store RA x_pos         

    movea( RA, snake_y_pos )        # get pixel y
    store RA listAddr
   
    call listGetIndex

    load RA listData
    store RA y_pos  

    load RA food_x_pos             # check x pos
    subm RA x_pos
    jumpnz food_check_snake_next

    load RA food_y_pos             # check y pos
    subm RA y_pos
    jumpnz food_check_snake_next
    jump food                      # food in snake redo        

food_check_snake_next:
    load RA listIndex              # inc count
    add RA 1
    store RA listIndex
    subm RA snake_x_tail_ptr       # is count > tail pointer
    jumpnz food_check_snake_loop

    load RA food_x_pos             # draw food 
    store RA x_pos
    load RA food_y_pos
    store RA y_pos
    call drawFood

    ret

# --------
# - DRAW -
# --------

drawBlank:
    move RA BLACK
    store RA block_colour
    jump draw

drawSegment:
    move RA GREEN
    store RA block_colour
    jump draw

drawFood:
    move RA RED
    store RA block_colour

draw:
    load RA x_pos
    store RA block_x_top_pos
    load RA y_pos
    store RA block_y_top_pos

    load RA x_pos
    add RA 2 
    store RA block_x_bottom_pos
    load RA y_pos
    add RA 3
    store RA block_y_bottom_pos

    call draw_rectangle   
    ret

# -------------------
# - DRAW END SCREEN -
# -------------------

draw_end_screen:
    draw_box( BLACK, 9, 21, 152, 112 )	

	draw_tile( LIGHT_GREY, eval( GAME_OVER_X_POSITION + ( 7 * 0 ) ), GAME_OVER_Y_POSITION, G_CHAR )
	draw_tile( LIGHT_GREY, eval( GAME_OVER_X_POSITION + ( 7 * 1 ) +1 ), GAME_OVER_Y_POSITION, A_CHAR )
	draw_tile( LIGHT_GREY, eval( GAME_OVER_X_POSITION + ( 7 * 2 ) +2), GAME_OVER_Y_POSITION, M_CHAR )
	draw_tile( LIGHT_GREY, eval( GAME_OVER_X_POSITION + ( 7 * 3 ) +3), GAME_OVER_Y_POSITION, E_CHAR )
	draw_tile( LIGHT_GREY, eval( GAME_OVER_X_POSITION + ( 7 * 4 ) +4), GAME_OVER_Y_POSITION, sp_CHAR )

	draw_tile( LIGHT_GREY, eval( GAME_OVER_X_POSITION + ( 7 * 6 ) ), GAME_OVER_Y_POSITION, O_CHAR )
	draw_tile( LIGHT_GREY, eval( GAME_OVER_X_POSITION + ( 7 * 7 ) +1), GAME_OVER_Y_POSITION, V_CHAR )
	draw_tile( LIGHT_GREY, eval( GAME_OVER_X_POSITION + ( 7 * 8 ) +2), GAME_OVER_Y_POSITION, E_CHAR )
	draw_tile( LIGHT_GREY, eval( GAME_OVER_X_POSITION + ( 7 * 9 ) +3), GAME_OVER_Y_POSITION, R_CHAR )

    ret

# -------------
# - BIN 2 DEC -
# -------------

bin_2_dec:
	move RB RA

	move RA 0
	store RA bin_2_dec_1s_cnt
	store RA bin_2_dec_10s_cnt
	store RA bin_2_dec_100s_cnt

100s_loop:
	sub RB	100
	move RC RB
	rol RC
    and RC 1
	jumpnz 100s_loop_exit
	load RA bin_2_dec_100s_cnt
	add RA 1
	store RA bin_2_dec_100s_cnt
	jump 100s_loop

100s_loop_exit:
	add RB 100

10s_loop:
	sub RB	10
	move RC RB
	rol RC
    and RC 1
	jumpnz 10s_loop_exit
	load RA bin_2_dec_10s_cnt
	add RA 1
	store RA bin_2_dec_10s_cnt
	jump 10s_loop

10s_loop_exit:
	add RB 10

	move RA RB
	store RA bin_2_dec_1s_cnt
	ret

# --------------------
# - INC PLAYER SCORE -
# --------------------

inc_player_score:
	load RA player_score
	add RA 1
	store RA player_score
	
	call bin_2_dec 

update_1s_digit:
	load RA bin_2_dec_1s_cnt
	and RA 0xFF
	jumpz inc_player_score_1s_0
	sub RA 1
	jumpz inc_player_score_1s_1
	sub RA 1
	jumpz inc_player_score_1s_2
	sub RA 1
	jumpz inc_player_score_1s_3
	sub RA 1
	jumpz inc_player_score_1s_4
	sub RA 1
	jumpz inc_player_score_1s_5
	sub RA 1
	jumpz inc_player_score_1s_6
	sub RA 1
	jumpz inc_player_score_1s_7
	sub RA 1
	jumpz inc_player_score_1s_8
	sub RA 1
	jumpz inc_player_score_1s_9
	jump inc_player_score_1s_0

inc_player_score_1s_0:
#	1s 0
	draw_tile( LIGHT_GREY, eval( SCORE_X_POSITION + ( 7 * 8 ) ), SCORE_Y_POSITION, zero_CHAR )
	jump update_10s_digit

inc_player_score_1s_1:
#	1s 1
	draw_tile( LIGHT_GREY, eval( SCORE_X_POSITION + ( 7 * 8 ) ), SCORE_Y_POSITION, one_CHAR )
	jump update_10s_digit

inc_player_score_1s_2:
#	1s 2
	draw_tile( LIGHT_GREY, eval( SCORE_X_POSITION + ( 7 * 8 ) ), SCORE_Y_POSITION, two_CHAR )
	jump update_10s_digit

inc_player_score_1s_3:
#	1s 3
	draw_tile( LIGHT_GREY, eval( SCORE_X_POSITION + ( 7 * 8 ) ), SCORE_Y_POSITION, three_CHAR )
	jump update_10s_digit

inc_player_score_1s_4:
#	1s 4
	draw_tile( LIGHT_GREY, eval( SCORE_X_POSITION + ( 7 * 8 ) ), SCORE_Y_POSITION, four_CHAR )
	jump update_10s_digit

inc_player_score_1s_5:
#	1s 5
	draw_tile( LIGHT_GREY, eval( SCORE_X_POSITION + ( 7 * 8 ) ), SCORE_Y_POSITION, five_CHAR )
	jump update_10s_digit

inc_player_score_1s_6:
#	1s 6
	draw_tile( LIGHT_GREY, eval( SCORE_X_POSITION + ( 7 * 8 ) ), SCORE_Y_POSITION, six_CHAR )
	jump update_10s_digit

inc_player_score_1s_7:
#	1s 7
	draw_tile( LIGHT_GREY, eval( SCORE_X_POSITION + ( 7 * 8 ) ), SCORE_Y_POSITION, seven_CHAR )
	jump update_10s_digit

inc_player_score_1s_8:
#	1s 8
	draw_tile( LIGHT_GREY, eval( SCORE_X_POSITION + ( 7 * 8 ) ), SCORE_Y_POSITION, eight_CHAR )
	jump update_10s_digit

inc_player_score_1s_9:
#	1s 9
	draw_tile( LIGHT_GREY, eval( SCORE_X_POSITION + ( 7 * 8 ) ), SCORE_Y_POSITION, nine_CHAR )
	jump update_10s_digit

update_10s_digit:
	load RA bin_2_dec_10s_cnt
	and RA 0xFF
	jumpz inc_player_score_10s_0
	sub RA 1
	jumpz inc_player_score_10s_1
	sub RA 1
	jumpz inc_player_score_10s_2
	sub RA 1
	jumpz inc_player_score_10s_3
	sub RA 1
	jumpz inc_player_score_10s_4
	sub RA 1
	jumpz inc_player_score_10s_5
	sub RA 1
	jumpz inc_player_score_10s_6
	sub RA 1
	jumpz inc_player_score_10s_7
	sub RA 1
	jumpz inc_player_score_10s_8
	sub RA 1
	jumpz inc_player_score_10s_9
	jump inc_player_score_10s_0

inc_player_score_10s_0:
	draw_tile( LIGHT_GREY, eval( SCORE_X_POSITION + ( 7 * 7 ) -1 ), SCORE_Y_POSITION, zero_CHAR )
	jump update_100s_digit

inc_player_score_10s_1:
	draw_tile( LIGHT_GREY, eval( SCORE_X_POSITION + ( 7 * 7 ) -1 ), SCORE_Y_POSITION, one_CHAR )
	jump update_100s_digit

inc_player_score_10s_2:
	draw_tile( LIGHT_GREY, eval( SCORE_X_POSITION + ( 7 * 7 ) -1 ), SCORE_Y_POSITION, two_CHAR )
	jump update_100s_digit

inc_player_score_10s_3:
	draw_tile( LIGHT_GREY, eval( SCORE_X_POSITION + ( 7 * 7 ) -1 ), SCORE_Y_POSITION, three_CHAR )
	jump update_100s_digit

inc_player_score_10s_4:
	draw_tile( LIGHT_GREY, eval( SCORE_X_POSITION + ( 7 * 7 ) -1 ), SCORE_Y_POSITION, four_CHAR )
	jump update_100s_digit

inc_player_score_10s_5:
	draw_tile( LIGHT_GREY, eval( SCORE_X_POSITION + ( 7 * 7 ) -1 ), SCORE_Y_POSITION, five_CHAR )
	jump update_100s_digit

inc_player_score_10s_6:
	draw_tile( LIGHT_GREY, eval( SCORE_X_POSITION + ( 7 * 7 ) -1 ), SCORE_Y_POSITION, six_CHAR )
	jump update_100s_digit

inc_player_score_10s_7:
	draw_tile( LIGHT_GREY, eval( SCORE_X_POSITION + ( 7 * 7 ) -1 ), SCORE_Y_POSITION, seven_CHAR )
	jump update_100s_digit

inc_player_score_10s_8:
	draw_tile( LIGHT_GREY, eval( SCORE_X_POSITION + ( 7 * 7 ) -1 ), SCORE_Y_POSITION, eight_CHAR )
	jump update_100s_digit

inc_player_score_10s_9:
	draw_tile( LIGHT_GREY, eval( SCORE_X_POSITION + ( 7 * 7 ) -1 ), SCORE_Y_POSITION, nine_CHAR )
	jump update_100s_digit

update_100s_digit:
	load RA bin_2_dec_100s_cnt
	and RA 0xFF
	jumpz inc_player_score_100s_0
	sub RA 1
	jumpz inc_player_score_100s_1
	sub RA 1
	jumpz inc_player_score_100s_2
	sub RA 1
	jumpz inc_player_score_100s_3
	sub RA 1
	jumpz inc_player_score_100s_4
	sub RA 1
	jumpz inc_player_score_100s_5
	sub RA 1
	jumpz inc_player_score_100s_6
	sub RA 1
	jumpz inc_player_score_100s_7
	sub RA 1
	jumpz inc_player_score_100s_8
	sub RA 1
	jumpz inc_player_score_100s_9
	jump inc_player_score_100s_0

inc_player_score_100s_0:
	draw_tile( LIGHT_GREY, eval( SCORE_X_POSITION + ( 7 * 6 ) -2 ), SCORE_Y_POSITION, zero_CHAR )
	jump inc_player_score_exit

inc_player_score_100s_1:
	draw_tile( LIGHT_GREY, eval( SCORE_X_POSITION + ( 7 * 6 ) -2 ), SCORE_Y_POSITION, one_CHAR )
	jump inc_player_score_exit

inc_player_score_100s_2:
	draw_tile( LIGHT_GREY, eval( SCORE_X_POSITION + ( 7 * 6 ) -2 ), SCORE_Y_POSITION, two_CHAR )
	jump inc_player_score_exit

inc_player_score_100s_3:
	draw_tile( LIGHT_GREY, eval( SCORE_X_POSITION + ( 7 * 6 ) -2 ), SCORE_Y_POSITION, three_CHAR )
	jump inc_player_score_exit

inc_player_score_100s_4:
	draw_tile( LIGHT_GREY, eval( SCORE_X_POSITION + ( 7 * 6 ) -2 ), SCORE_Y_POSITION, four_CHAR )
	jump inc_player_score_exit

inc_player_score_100s_5:
	draw_tile( LIGHT_GREY, eval( SCORE_X_POSITION + ( 7 * 6 ) -2 ), SCORE_Y_POSITION, five_CHAR )
	jump inc_player_score_exit

inc_player_score_100s_6:
	draw_tile( LIGHT_GREY, eval( SCORE_X_POSITION + ( 7 * 6 ) -2 ), SCORE_Y_POSITION, six_CHAR )
	jump inc_player_score_exit

inc_player_score_100s_7:
	draw_tile( LIGHT_GREY, eval( SCORE_X_POSITION + ( 7 * 6 ) -2 ), SCORE_Y_POSITION, seven_CHAR )
	jump inc_player_score_exit

inc_player_score_100s_8:
	draw_tile( LIGHT_GREY, eval( SCORE_X_POSITION + ( 7 * 6 ) -2 ), SCORE_Y_POSITION, eight_CHAR )
	jump inc_player_score_exit

inc_player_score_100s_9:
	draw_tile( LIGHT_GREY, eval( SCORE_X_POSITION + ( 7 * 6 ) -2 ), SCORE_Y_POSITION, nine_CHAR )
	jump inc_player_score_exit

inc_player_score_exit:
	ret

#>>

#######################
# GENERAL SUBROUTINES #
#######################
#<<
# ------------------ 
# - Software Delay -
# ------------------

delay:
    move RB 0x0F
    jump delay_start

delayVeryShort:
    move RB 0x1F 
    jump delay_start

delayShort:
    move RB 0x3F 
    jump delay_start

delayMedium:
    move RB 0x7F 
    jump delay_start

delayLong:
    move RB 0xFF 
    and RB 0xFF
    jump delay_start

delayLonger:
    moved( RB, 0x01FF ) 
    jump delay_start

delayVeryLong:
    move RB 0xFF 

delay_start:
    movea( RA, 0xFFF )

delay_loop:
    sub RA 1
    jumpnz delay_loop
    sub RB 1
    jumpnz delay_start

    ret
#>>

######################
# SPRITE SUBROUTINES #
######################
#<<
# --------------------
# - READ XY POSITION -
# --------------------

get_xy_position:
#   tile_X_pos
    move RB RD
    add RB SPRITE_XYPOS_OFFSET
    load RA (RB)
    rotate_left( RA, SPRITE_XPOS_SHIFT )
    and RA SPRITE_XPOS_MASK
    store RA TILE_X_REG
	store RA sprite_x_pos

#   tile_Y_pos
    load RA (RB)
    and RA SPRITE_YPOS_MASK    
    store RA TILE_Y_REG
	store RA sprite_y_pos

    ret

# ------------------
# - READ XY LIMITS -
# ------------------

get_xy_limits:
#   tile_X_limits
    move RB RD
    add RB SPRITE_XLIMIT_OFFSET
    load RA (RB)
    rotate_left( RA, SPRITE_XLIMIT_MAX_SHIFT )
    and RA 0xFF
	store RA sprite_limit_x_max
    load RA (RB)
    and RA 0xFF
	store RA sprite_limit_x_min

#   tile_Y_limits
    move RB RD
    add RB SPRITE_YLIMIT_OFFSET
    load RA (RB)
    rotate_left( RA, SPRITE_YLIMIT_MAX_SHIFT )
    and RA 0xFF
	store RA sprite_limit_y_max
    load RA (RB)
    and RA 0xFF
	store RA sprite_limit_y_min
    ret

# ------------------
# - READ XY OFFSET -
# ------------------
#
# offset is a signed 8bit value

get_xy_offsets:
#   tile_X_offset
    move RB RD
    add RB SPRITE_XYOFFSET_OFFSET
    load RA (RB)
    rotate_left( RA, SPRITE_XOFFSET_SHIFT )
    and RA SPRITE_XOFFSET_MASK
	store RA sprite_x_offset
    and RA 0x80
	jumpz get_xy_offset_1

	load RA negative_sign_extend
	addm RA sprite_x_offset
	store RA sprite_x_offset 

get_xy_offset_1:
#   tile_Y_offset
    load RA (RB)
    and RA SPRITE_YOFFSET_MASK    
	store RA sprite_y_offset
    and RA 0x80
	jumpz get_xy_offset_exit

	load RA negative_sign_extend
	addm RA sprite_y_offset
	store RA sprite_y_offset

get_xy_offset_exit:
    ret

# --------------------------
# - READ FOREGROUND COLOUR -
# --------------------------

get_foreground_colour:
#   cpu_colour_reg
    move RB RD
    add RB SPRITE_FB_COLOUR_OFFSET
    load RA (RB)
    and RA SPRITE_FG_COLOUR_MASK  
    store RA CPU_COLOUR_REG
    ret

# --------------------------
# - READ BACKGROUND COLOUR -
# --------------------------

get_background_colour:
#   cpu_colour_reg
    move RB RD
    add RB SPRITE_FB_COLOUR_OFFSET
    load RA (RB)
    rotate_left( RA, SPRITE_BG_COLOUR_SHIFT )
    and RA SPRITE_FG_COLOUR_MASK  
    store RA CPU_COLOUR_REG
    ret

# --------------------
# - READ SPRITE TILE -
# --------------------

get_sprite_tile:
#   select tile 0 address
    move RB RD
    add RB SPRITE_TILE_OFFSET  

#   add tile offset  
    load RC (RD)
    and RC SPRITE_TILE_NUMBER_MASK    
    add RB RC

#   auto inc?
    load RA (RD)
    and RA SPRITE_AUTO_MASK
    jumpz get_sprite_tile_1
    
#   yes, inc tile number
    load RA (RD)
    and RA SPRITE_FLAG_MASK  
    add RC 1
    and RC SPRITE_TILE_NUMBER_MASK
    add RA RC
    store RA (RD)

get_sprite_tile_1:
#   cpu_addr_reg
    load RA (RB)
    store RA CPU_SRC_ADDR_REG
  	store RA sprite_tile
    ret

get_current_sprite_tile:
#   select tile 0 address
    move RB RD
    add RB SPRITE_TILE_OFFSET  

    load RC (RD)
    and RC SPRITE_TILE_NUMBER_MASK    
    add RB RC

#   cpu_addr_reg
    load RA (RB)
    store RA CPU_SRC_ADDR_REG
  	store RA sprite_tile
    ret

# ---------------
# - READ HITBOX -
# ---------------

get_hitbox:
#   get XHITBOX max min
    move RB RD
    add RB SPRITE_XHITBOX_OFFSET

    load RA (RB)
    and RA 0xFF
    store RA sprite_hitbox_x_min

    load RA (RB)
    rotate_left( RA, SPRITE_XHITBOX_MAX_SHIFT )
    and RA 0xFF
    store RA sprite_hitbox_x_max

#   get YHITBOX max min
    move RB RD
    add RB SPRITE_YHITBOX_OFFSET

    load RA (RB)
    and RA 0xFF
    store RA sprite_hitbox_y_min

    load RA (RB)
    rotate_left( RA, SPRITE_XHITBOX_MAX_SHIFT )
    and RA 0xFF
    store RA sprite_hitbox_y_max

get_hitbox_end:
    ret

# ---------------
# - DRAW SPRITE -
# ---------------

# write FG and BG (assumed black) colours
# use XY pos

draw_sprite_subroutine:
#   test visibility
    load RC (RD)
    and RC SPRITE_VISIBLE_MASK   
    jumpz draw_sprite_visible

draw_sprite_not_visible:
    move RA OVERLAY_TILE
    store RA sprite_command
    call get_background_colour
    jump draw_sprite_update

draw_sprite_visible:
    move RA DRAW_TILE
    store RA sprite_command
    call get_foreground_colour

draw_sprite_update:
    call get_current_sprite_tile
    call sprite_operation
    ret

# ------------------
# - OVERLAY SPRITE -
# ------------------

# write only FG colour, if sprite pixel is no set do not write BG colour
# use XY pos

overlay_sprite_subroutine:
#   test visibility
    load RC (RD)
    and RC SPRITE_VISIBLE_MASK   
    jumpz overlay_sprite_visible

overlay_sprite_not_visible:
    call get_background_colour
    jump overlay_sprite_update

overlay_sprite_visible:
    call get_foreground_colour

overlay_sprite_update:
    move RA OVERLAY_TILE
    store RA sprite_command
    call get_current_sprite_tile
    call sprite_operation
    ret

# ----------------
# - CLEAR SPRITE -
# ----------------

# same as overlay sprite, but use background colour
# BG assumed black, through deletes previous FG pixels.

clear_sprite_subroutine:
    move RA OVERLAY_TILE
    store RA sprite_command
    call get_background_colour
    call get_current_sprite_tile
    call sprite_operation
    ret

# --------------------
# - SPRITE OPERATION -
# --------------------

# used to display sprites in DRAW and OVERLAY 

sprite_operation:
    call get_xy_position

#   ctl reg
    load RA sprite_command
    store RA CPU_CTL_REG

sprite_operation_wait:
    movea( RA, CPU_STATUS_REG )
    load RB (RA)
    and RB CTL_MASK
    jumpz sprite_operation_wait
    ret

sprite_end:
    ret

# ---------------
# - MOVE SPRITE -
# ---------------

# move sprite to new position by adding a signed 8bit offset, limit checks
# also update (increment) tile pointer and offset if AUTO flag set.

# miss <= x2 > w1 + x1 || x1 > w2 + x2 || y2 > h1 + y1 || y1 > h2 + y2
#
# 0 = MISS, 1 = TOP, 2 = RIGHT, 3 = LEFT, 4 = BOTTOM
#

move_sprite_subroutine:
#   get / update XY position and offset
    call get_xy_position
	call get_xy_offsets
	call get_xy_limits

#   get sprite hit bix
	call get_hitbox

#   calc new X position
    load RA sprite_x_pos
    addm RA sprite_x_offset
	store RA sprite_x_pos

#   calc new Y position 
    load RA sprite_y_pos
    addm RA sprite_y_offset
	store RA sprite_y_pos

#   auto tile_inc, if 1 then yes, if 0 then no
    load RA (RD)
    and RA SPRITE_INC_MASK
    jumpz move_sprite_limit_check_1

#   yes, inc tile number
    load RC (RD)
    add RC 1
    and RC SPRITE_TILE_NUMBER_MASK

#   update tile number
    load RA (RD)
    and RA SPRITE_FLAG_MASK  
    add RA RC
    store RA (RD)

move_sprite_limit_check_1:
#   read offsets to determine X checks
	load RA sprite_x_offset

#   use jumpc if supported
    jumpc move_sprite_left_limit_check
#	rol RA
#	and RA 0x01
#	jumpnz move_sprite_left_limit_check

move_sprite_right_limit_check:
#   test if x+w>MAX_X_LIMIT
    load RA sprite_x_pos
    addm RA sprite_hitbox_x_max
    subm RA sprite_limit_x_max

#   use jumpc if supported
    jumpc move_sprite_limit_check_2
#   rol RA
#   and RA 0x01
#   jumpnz move_sprite_limit_check_2

#   retore old value of X
    load RA sprite_x_pos
    subm RA sprite_x_offset
	store RA sprite_x_pos

#   auto direction change?
    load RA (RD)
    and RA SPRITE_AUTO_MASK
    jumpz move_sprite_limit_check_2 

    move RA 0xFF
    store RA sprite_x_offset
    jump move_sprite_limit_check_2

move_sprite_left_limit_check: 
#   test if x+w>MIN_X_LIMIT
    load RA sprite_x_pos
    addm RA sprite_hitbox_x_min
    subm RA sprite_limit_x_min

#   use jumpc if supported
    jumpc move_sprite_limit_check_2
#   rol RA
#   and RA 0x01
#   jumpz move_sprite_limit_check_2

#   retore old value of X   
    load RA sprite_x_pos
    subm RA sprite_x_offset
	store RA sprite_x_pos

#   auto direction change?
    load RA (RD)
    and RA SPRITE_AUTO_MASK
    jumpz move_sprite_limit_check_2 

    move RA 0x01
    store RA sprite_x_offset

move_sprite_limit_check_2:
#   read offsets to determine Y checks
	load RA sprite_y_offset

#   use jumpc if supported
    jumpc move_sprite_top_limit_check
#	rol RA
#	and RA 0x01
#	jumpnz move_sprite_top_limit_check

move_sprite_bottom_limit_check:
#   test if y+w>MAX_Y_LIMIT
    load RA sprite_y_pos
    addm RA sprite_hitbox_y_max 
    subm RA sprite_limit_y_max

#   use jumpc if supported
    jumpc move_sprite_exit
#   rol RA
#   and RA 0x01
#   jumpnz move_sprite_exit

#   retore old value of Y  
    load RA sprite_y_pos
    subm RA sprite_y_offset
	store RA sprite_y_pos

#   auto direction change?
    load RA (RD)
    and RA SPRITE_AUTO_MASK
    jumpz move_sprite_exit 

    move RA 0xFF
    store RA sprite_y_offset
    jump move_sprite_exit

move_sprite_top_limit_check:
#   test if y+w<MIN_Y_LIMIT
    load RA sprite_y_pos
    addm RA sprite_hitbox_y_min
    subm RA sprite_limit_y_min

#   use jumpc if supported
    jumpc move_sprite_exit
#   rol RA 
#   and RA 0x01
#   jumpz move_sprite_exit

#   retore old value of Y  
    load RA sprite_y_pos
    subm RA sprite_y_offset
	store RA sprite_y_pos

#   auto direction change?
    load RA (RD)
    and RA SPRITE_AUTO_MASK
    jumpz move_sprite_exit 

    move RA 0x01
    store RA sprite_y_offset

move_sprite_exit:
    move RC RD
    add RC SPRITE_XYPOS_OFFSET
    load RA sprite_y_pos
    move RB RA
	and RB 0xFF
    load RA sprite_x_pos
    and RA 0xFF
    rotate_left( RA, 8 )
    add RB RA
    store RB (RC)

    move RC RD
    add RC SPRITE_XYOFFSET_OFFSET
    load RA sprite_y_offset
    move RB RA
	and RB 0xFF
    load RA sprite_x_offset
    and RA 0xFF
    rotate_left( RA, 8 )
    add RB RA
    store RB (RC)

    ret

# --------------------
# - COLLISION SPRITE -
# --------------------

# miss <= x2 > w1 + x1 || x1 > w2 + x2 || y2 > h1 + y1 || y1 > h2 + y2
#

collision_sprite_subroutine:
#   check if both sprites are visable
	load RA (RD)
	and RA SPRITE_VISIBLE_MASK
	jumpz collision_sprite_miss

	load RA sprite_pntr_1
	move RD RA
	load RA (RD)
	and RA SPRITE_VISIBLE_MASK
	jumpz collision_sprite_miss

#   get sprite_1 data
	call get_xy_position

	load RA sprite_x_pos
	store RA sprite_x_pos_1

	load RA sprite_y_pos
	store RA sprite_y_pos_1
 
    call get_hitbox

    load RA sprite_hitbox_x_min
    store RA sprite_hitbox_x_min_1

	load RA sprite_hitbox_x_max
    store RA sprite_hitbox_x_max_1

	load RA sprite_hitbox_y_min
	store RA sprite_hitbox_y_min_1

	load RA sprite_hitbox_y_max
	store RA sprite_hitbox_y_max_1

#   get sprite_0 data
	load RA sprite_pntr
	move RD RA

	call get_xy_position
    call get_hitbox

#   test if x2 > w1 + x1
    load RA sprite_x_pos
    addm RA sprite_hitbox_x_max
    subm RA sprite_x_pos_1
    subm RA sprite_hitbox_x_min_1

#   use jumpc if supported
    jumpc collision_sprite_miss
#   rol RA
#   and RA 0x01
#   jumpnz collision_sprite_miss

#   test if x1 > w2 + x2
    load RA sprite_x_pos_1
    addm RA sprite_hitbox_x_max_1
    subm RA sprite_x_pos
    subm RA sprite_hitbox_x_min

#   use jumpc if supported
    jumpc collision_sprite_miss
#   rol RA
#   and RA 0x01
#   jumpnz collision_sprite_miss

#   test if y2 > h1 + y1
    load RA sprite_y_pos
    addm RA sprite_hitbox_y_max
    subm RA sprite_y_pos_1
    subm RA sprite_hitbox_y_min_1

#   use jumpc if supported
    jumpc collision_sprite_miss
#   rol RA
#   and RA 0x01
#   jumpnz collision_sprite_miss

#   test if y1 > h2 + y2
    load RA sprite_y_pos_1
    addm RA sprite_hitbox_y_max_1
    subm RA sprite_y_pos
    subm RA sprite_hitbox_y_min

#   use jumpc if supported
    jumpc collision_sprite_miss
#   rol RA
#   and RA 0x01
#   jumpnz collision_sprite_miss

	load RA sprite_collision
	or RA 1
	store RA sprite_collision

collision_sprite_miss:
	ret

#>>

####################
# TILE SUBROUTINES #
####################
#<<
# -------------
# - DRAW TILE -
# -------------

draw_tile_subroutine:
#   cpu_colour_reg
    load RA block_colour
    store RA CPU_COLOUR_REG         # colour

#   xy_position:
    load RA block_x_top_pos         # x
    store RA TILE_X_REG
    load RA block_y_top_pos         # y
    store RA TILE_Y_REG

#   set tile address
    move RA RD
    store RA CPU_SRC_ADDR_REG       # tile addr in video memory  

#   ctl reg
    move RA DRAW_TILE
    store RA CPU_CTL_REG            # start 

draw_tile_wait:
    movea( RA, CPU_STATUS_REG )
    load RB (RA)
    and RB CTL_MASK
    jumpz draw_tile_wait
    ret

# ----------------
# - OVERLAY TILE -
# ----------------

overlay_tile_subroutine:
#   cpu_colour_reg
    load RA block_colour
    store RA CPU_COLOUR_REG         # colour

#   xy_position:
    load RA block_x_top_pos         # x
    store RA TILE_X_REG
    load RA block_y_top_pos         # y
    store RA TILE_Y_REG

#   set tile address
    move RA RD
    store RA CPU_SRC_ADDR_REG       # tile addr in video memory  

#   ctl reg
    move RA OVERLAY_TILE
    store RA CPU_CTL_REG            # start 

overlay_tile_wait:
    movea( RA, CPU_STATUS_REG )
    load RB (RA)
    and RB CTL_MASK
    jumpz overlay_tile_wait
    ret

# -------------------
# - DRAW TILE DIGIT -
# -------------------

# draw digit character at XY position, tile selected by NIBBLE
# colour              : block_colour
# X pos               : block_x_top_pos
# Y pos               : block_y_top_pos 
# offset              : nibble 

draw_tile_digit:
    moved( RD, zero_CHAR )            # base address
    load RA nibble                    # get offset
    and RA 0x0F
    move RB RA
    sub RB 9                          # <= 9 
    jumpc draw_tile_digit_exit

    asl RA                            # x 4
    asl RA
    add RD RA                         # add offset to base address
    
    call draw_tile_subroutine

draw_tile_digit_exit:
    ret

# -------------------
# - DRAW TILE ASCII -
# -------------------

# draw ASCII character at XY position, tile selected by BYTE (0x20 - 0x7F)
# colour              : block_colour
# X pos               : block_x_top_pos
# Y pos               : block_y_top_pos 
# offset              : byte 

draw_tile_ascii:
    moved( RD, sp_CHAR )        # base address SPACE (0x20)
    load RA byte                # get ASCII value
    and RA 0xFF     
    move RB RA 
    sub RB 0x7F                 # <= 0x7F
    jumpc draw_tile_digit_exit

    asl RA                      # x 4
    asl RA
    add RD RA                   # add offset to base address
    
    call draw_tile_subroutine

    ret

# ------------------
# - DRAW TILE CHAR -
# ------------------

# draw CHAR character at XY position, tile selected by WORD address
# colour              : block_colour
# X pos               : block_x_top_pos
# Y pos               : block_y_top_pos 
# address             : word

draw_tile_char:
    load RA word                # get ASCII value
    move RD RA                   
    
    call draw_tile_subroutine

    ret
#>>

#####################
# BLOCK SUBROUTINES #
#####################
#<<
# ---------------
# - BLOCK WRITE -
# ---------------

# write data directly to frame buffer
# colour        : block_colour
# start address : block_start_address
# end address   : block_start_address + block_size
#

block_write_subroutine:
#   cpu_colour_reg
    movea( RB, CPU_COLOUR_REG )
    load RA block_colour
    store RA (RB)

#   cpu_data_in_reg - end src addr
    movea( RB, CPU_DATA_IN_REG )
    load RA block_start_address
    addm RA block_width
    store RA (RB)

#   cpu_addr_reg
    movea( RB, CPU_DST_ADDR_REG )
    load RA block_start_address
    store RA (RB)

#   ctl reg 
    movea( RA, CPU_CTL_REG )
    move RB BLOCK_WRITE
    store RB (RA)

block_write_wait:
    movea( RA, CPU_STATUS_REG )
    load RB (RA)
    and RB CTL_MASK
    jumpz block_write_wait
    ret

# ------------
# - DRAW BOX -
# ------------

#             1   2  3  4  5 
#  draw_box( RED, 6, 2, 7, 3 )
#             |   |  |  |  |
# 1 : COLOUR --   |  |  |  |
# 2 : X_TOP -------  |  |  |
# 3 : Y_TOP ----------  |  |
# 4 : X_BOTTOM ----------  |
# 5 : Y_BOTTOM -------------

# draw box directly to frame buffer, multiples of 2 pixel along X axis
# 
# colour                       : block_colour
# start addr in frame buffer   : block_start_address
# number of 2 pixel blocks     : block_width
# number of rows               : block_depth


draw_box_subroutine:
    call block_write_subroutine

    load RA block_depth
    sub RA 1
    store RA block_depth
    jumpz draw_box_exit

    load RA block_start_address
    add RA eval( VDC_MAX_X_LIMIT >> 1 )
    store RA block_start_address

    jump draw_box_subroutine

draw_box_exit:
    ret

# ------------------
# - DRAW RECTANGLE -
# ------------------

# draw rectangle directly to frame buffer, multiples of 2 pixel along X axis

# colour                  : block_colour
# top left x location     : block_x_top_pos
# bottom right x location : block_x_bottom_pos
# top left y location     : block_y_top_pos
# bottom right y location : block_y_bottom_pos

draw_rectangle:
#   calc start address
    load RA block_x_top_pos
    store RA TILE_X_REG
    load RA block_y_top_pos
    store RA TILE_Y_REG
    load RA TILE_POS
	store RA block_start_address

#   calc width
    load RA block_x_bottom_pos 
    subm RA block_x_top_pos
    asr RA
    store RA block_width

#   draw line
    call block_write_subroutine

#   check if all rows drawn
    load RA block_y_bottom_pos
    subm RA block_y_top_pos
    jumpz draw_rectangle_exit

#   increment Y
    load RA block_y_top_pos
    add RA 1
    store RA block_y_top_pos
    jump draw_rectangle

draw_rectangle_exit:
    ret

# --------------
# - BLOCK COPY -
# --------------

# write data directly to frame buffer
# colour              : block_colour
# start address       : block_start_address
# end address         : block_start_address + block_width
# destination address : block_destination_address 
#

block_copy_subroutine:
#   cpu_data_in_reg - end src addr
    movea( RB, CPU_DATA_IN_REG )
    load RA block_start_address
    addm RA block_width
    store RA (RB)

#   cpu_addr_reg
    movea( RB, CPU_SRC_ADDR_REG )
    load RA block_start_address
    store RA (RB)

#   cpu_dest_reg
    movea( RB, CPU_DST_ADDR_REG )
    load RA block_destination_address
    store RA (RB)

#   ctl reg 
    movea( RA, CPU_CTL_REG )
    move RB BLOCK_COPY
    store RB (RA)

block_copy_wait:
    movea( RA, CPU_STATUS_REG )
    load RB (RA)
    and RB CTL_MASK
    jumpz block_copy_wait
    ret
    ret

# ------------
# - COPY BOX -
# ------------

#                1  2  3  4  5  6
#      copy_box( 0  0  5  5  6  6 )
#                |  |  |  |  |  | 
# 1 : SRC_X_TOP --  |  |  |  |  |
# 2 : SRC_Y_TOP -----  |  |  |  |
# 3 : SRC_X_BOTTOM -----  |  |  |
# 4 : SRC_Y_BOTTOM --------  |  |
# 5 : DEST_X_TOP -------------  |
# 6 : DEST_Y_TOP ----------------

# draw box directly to frame buffer, multiples of 2 pixel along X axis
# 
# colour                       : block_colour
# start addr in frame buffer   : block_start_address
# destination address          : block_destination_address 
# number of 2 pixel blocks     : block_width
# number of rows               : block_depth

copy_box_subroutine:
    call block_copy_subroutine

    load RA block_depth
    sub RA 1
    store RA block_depth
    jumpz copy_box_exit

    load RA block_start_address
    add RA eval( VDC_MAX_X_LIMIT >> 1 )
    store RA block_start_address

    load RA block_destination_address
    add RA eval( VDC_MAX_X_LIMIT >> 1 )
    store RA block_destination_address

    jump copy_box_subroutine

copy_box_exit:
    ret

#>>

###########################
# SERIAL PORT SUBROUTINES #
###########################
#<<
# ------------------------------------------
# - Copy string (must terminate with a \0) -
# ------------------------------------------
# Source / destination addresses passed in SRCADDR / DESTADDR

strcpy:
    load RA srcAddr          # get source address
    move RB RA
    load RA destAddr         # get destination address
    move RC RA 

strcpy_loop:              
    load RA (RB)             # load  char
    and RA 0xFF
    jumpz strcpy_exit        # exit if 0
    store RA (RC)            # copy
    add RB 1
    add RC 1
    jump strcpy_loop         # repeat

strcpy_exit:
    ret

# ----------------------------------------
# - TX String (must terminate with a \0) -
# ----------------------------------------
# data in txBuffer written to serial port, blocking 

txString:
    movea( RB, txBuffer )  # get string address

tx_loop:              
    load RA (RB)             # load  char
    and RA 0xFF
    jumpz tx_exit            # exit if 0
    store RA UART_TX         # tx

waitTX:
    load RA UART_STATUS      # test TX status wait till 1
    and RA 0x04          
    jumpz waitTX    

    add RB 1                 # inc address
    jump tx_loop             # repeat

tx_exit:
    move RA 0
    store RA txBufferIndex
    ret

# --------------------------------
# - Add NULL to tx string buffer -
# --------------------------------

addNULL:
    load RA txBufferIndex 
    movea( RC, txBuffer )
    add RC RA 
    move RA 0
    store RA (RC)
    ret

# ---------------------------------
# - Add LF CR to tx string buffer -
# ---------------------------------

addLFCR:
    load RA txBufferIndex 
    movea( RC, txBuffer )
    add RC RA 
    add RA 2
    store RA txBufferIndex

    move RA 0x0A
    store RA (RC)
    add RC 1
    move RA 0x0D
    store RA (RC)
    ret

# ---------------------------------
# - Add SPACE to tx string buffer -
# ---------------------------------

addSPACE:
    load RA txBufferIndex 
    movea( RC, txBuffer )
    add RC RA 
    add RA 1
    store RA txBufferIndex

    move RA 0x20
    store RA (RC)
    ret

# --------------------------------------------
# - Convert word into hexidecimal characters -
# --------------------------------------------
# data to be convert in variable "word", characters written to txBuffer

word2Hex:
    load RA word
    asr RA
    asr RA
    asr RA
    asr RA
    asr RA
    asr RA
    asr RA
    asr RA
    store RA byte
    call byte2Hex

    load RA word
    and RA 0xFF
    store RA byte
    call byte2Hex

    ret

# --------------------------------------------
# - Convert byte into hexidecimal characters -
# --------------------------------------------
# data to be convert in variable "byte", characters written to txBuffer

byte2Hex:
    load RA txBufferIndex 
    movea( RC, txBuffer )
    add RC RA
    add RA 2
    store RA txBufferIndex

    movea( RB, nibble2HexTable )
    load RA byte
    and RA 0xF0
    asr RA
    asr RA
    asr RA
    asr RA
    add RB RA

    load RA (RB)
    store RA (RC)
    add RC 1

    movea( RB, nibble2HexTable )

    load RA byte
    and RA 0x0F
    add RB RA

    load RA (RB)
    store RA (RC)

    ret

# ----------------------------------------
# - Convert byte into decimal characters -
# ----------------------------------------
# data to be convert in variable "byte", characters written to txBuffer

byte2Dec:
    load RA txBufferIndex 
    movea( RC, txBuffer )
    add RC RA
    add RA 3
    store RA txBufferIndex

    load RA byte
    and RA 0xFF
    move RB 0

byte2Dec_100s_loop:
    sub RA 100                   # sub 100
    jumpc byte2Dec_100s_exit     # yes, exit
    add RB 1                     # inc count 
    jump byte2Dec_100s_loop      # repeat

byte2Dec_100s_exit:
    add RB 0x30
    store RB (RC)
    add RC 1
    
    add RA 100                   # undo last sub
    move RB 0
 
byte2Dec_10s_loop:
    sub RA 10                    # sub 10
    jumpc byte2Dec_10s_exit      # yes, exit
    add RB 1                     # inc count 
    jump byte2Dec_10s_loop       # repeat

byte2Dec_10s_exit:
    add RB 0x30
    store RB (RC)
    add RC 1
    
    add RA 10                    # undo last sub
    add RA 0x30
    store RA (RC)

    ret

# --------------
# - RX TX Test -
# --------------

rx_tx_test:
    load RA UART_STATUS      # has ASCII char been RX?
    and RA 0x01
    jumpz rx_tx_test         # no check again

    load RA UART_RX
    store RA UART_TX

rx_tx_waitTX:
    load RA UART_STATUS      # test TX status wait till 1
    and RA 0x04          
    jumpz rx_tx_waitTX    

    jump rx_tx_test          # repeat

# ------------------
# - Welcome banner -
# ------------------

welcome_banner:
    move RA 0
    store RA txBufferIndex 

    movea( RA, welcome )           # set string address      
    store RA srcAddr         
    movea( RA, txBuffer )          # set buffer address
    store RA destAddr
    call strcpy 
    call txString
    ret
#>>

###########################
# LCD DISPLAY SUBROUTINES #
###########################
#<<
# ---------------
# - LCD PULSE E -
# ---------------

# GPIO PORT
# ---------
# 128  bit 7 : NU
# 64   bit 6 : RW (0)
# 32   bit 5 : E
# 16   bit 4 : RS (1=text, 0=config)
# 8    bit 3 : D7
# 4    bit 2 : D6
# 2    bit 1 : D5
# 1    bit 0 : D5

lcd_pulse_e:
    load RA GPIO_A_DATA       # set enable line
    add RA 32
    store RA GPIO_A_OUT
    
    call lcd_delay

    load RA GPIO_A_DATA       # clear enable line
    sub RA 32
    store RA GPIO_A_OUT

    call lcd_delay
    ret

# ------------------
# - LCD SET LINE 1 -
# ------------------
# LCD_LINE_1 = 0x80 LCD RAM address for the 1st line

lcd_line_1:
    move RA 0x08
    store RA GPIO_A_OUT
    call lcd_delay

    call lcd_pulse_e

    move RA 0x00
    store RA GPIO_A_OUT
    call lcd_delay

    call lcd_pulse_e
    ret

# ------------------
# - LCD SET LINE 2 -
# ------------------
# LCD_LINE_2 = 0xC0 LCD RAM address for the 2nd line

lcd_line_2:
    move RA 0x0C
    store RA GPIO_A_OUT
    call lcd_delay

    call lcd_pulse_e

    move RA 0x00
    store RA GPIO_A_OUT
    call lcd_delay

    call lcd_pulse_e
    ret

# -----------------
# - LCD TX STRING -
# -----------------

lcd_tx:
    move RA 0                 # clear all bits
    store RA GPIO_A_OUT

    load RA lcd_config
    and RA 0xFF
    jumpnz lcd_tx_continue

    load RA GPIO_A_DATA       # set RS bit (text)
    add RA 16
    store RA GPIO_A_OUT

lcd_tx_continue:
    load RA lcd_pntr
    move RB RA

    load RA (RB)              # exit if NULL
    and RA 0xFF
    jumpz lcd_tx_exit

    and RA 0xF0               # extract high nibble
    ror RA
    ror RA
    ror RA
    ror RA
    addm RA GPIO_A_DATA
    store RA GPIO_A_OUT
    call lcd_delay

    call lcd_pulse_e
 
    load RA (RB)
    and RA 0x0F              # extract low nibble
    move RB RA
    load RA GPIO_A_DATA
    and RA 0xF0
    add RA RB
    store RA GPIO_A_OUT
    call lcd_delay

    call lcd_pulse_e

    move RA 0
    store RA GPIO_A_OUT

    call lcd_delay

    load RA lcd_pntr
    add RA 1
    store RA lcd_pntr
    jump lcd_tx

lcd_tx_exit:
    ret

# ------------------
# - LCD TIME DELAY -
# ------------------

lcd_delay:
    moved( RA, 0x200 )
    store RA lcd_delay_count

lcd_delay_loop:
    load RA lcd_delay_count
    sub RA 1
    store RA lcd_delay_count
    jumpnz lcd_delay_loop

    ret

#>>

##########################
# CONTROLLER SUBROUTINES #
##########################
#<<
# ----------------------
# - READ ROTARY SWITCH -
# ----------------------   
  
# 0=STOP, 1=LEFT, 2=RIGHT

read_rotary_switch:
	move RB 0
	
	load RA ROTARY_DATA        # move?
	and RA 0x04	
	jumpz read_rotary_switch_exit
	
	store RA ROTARY_RESET	   # reset flag
	
	load RA ROTARY_DATA        # left/right
	and RA 0x02	
	jumpz read_rotary_switch_left
	
read_rotary_switch_right:
	move RB 1
	jump read_rotary_switch_exit

read_rotary_switch_left:
	move RB 2    
    
read_rotary_switch_exit:
	move RA RB
	store RA rotary_switch
	ret
	
# ----------------------
# - READ FIRE SWITCH -
# ----------------------   
  
# 0=NOT PRESSED, 1=~PRESSED

read_fire_switch:
    move RB 0
    
	load RA ROTARY_DATA		   # fire pressed
	and RA 0x01	
	jumpz read_fire_switch_exit
	
	move RB 1
	
read_fire_switch_exit:	
	move RA RB
	store RA fire_switch
	ret
	
# --------------------
# - READ SNES SWITCH -
# -------------------- 	

read_snes_switch:
    move RA 0                  # start SNES update
    store RA SNES_START

read_snes_switch_wait:
    load RA SNES_STATUS        # wait for data
    and RA 0x01
    jumpnz read_snes_switch_wait
    
    load RA SNES_DATA          # read data
    store RA snes_switch
    ret

# ---------------------
# - READ PS2 KEYBOARD -
# ---------------------

read_keyboard:
    moved( RB, UPPER_BYTE_MASK )
    load RA PS2_DATA
    and RA RB
    jumpz read_keyboard_exit

    load RA PS2_DATA
    store RA PS2_DATA
    and RA 0xFF
    store RA key

read_keyboard_exit:
    ret
#>>

##################################
# DRAW LINE & CIRCLE SUBROUTINES #
##################################
#<<
# --------------
# - DRAW PIXEL -
# --------------

# draw pixel directly to frame buffer
# 
# colour                     : pixel_colour
# x location                 : pixel_x_pos
# y location                 : pixel_y_pos

draw_pixel:
#   ret
    load RA pixel_x_pos
    store RA PIXEL_X_REG
    load RA pixel_y_pos
    store RA PIXEL_Y_REG

#   cpu_colour_reg
    movea( RB, CPU_COLOUR_REG )
    load RA pixel_colour
    store RA (RB)

#   ctl reg 
    movea( RA, CPU_CTL_REG )
    move RB DRAW_PIXEL
    store RB (RA)

draw_pixel_wait:
    movea( RA, CPU_STATUS_REG )
    load RB (RA)
    and RB CTL_MASK
    jumpz draw_pixel_wait
    ret

# -------------
# - DRAW LINE -
# -------------

# Plot line between top points (X1,Y1) and (X0,Y0)
# Bresenham's line drawing algorithm
#
#   dx = abs(x2 - x1)
#   dy = abs(y2 - y1)
#   slope = dy > dx
#
#   if slope:
#     x1, y1 = y1, x1
#     x2, y2 = y2, x2
#
#   if x1 > x2:
#     x1, x2 = x2, x1
#     y1, y2 = y2, y1
#
#   dx = abs(x2 - x1)
#   dy = abs(y2 - y1)
#
#   error = dx // 2
#
#   y = y1
#   ystep = 1 if y1 < y2 else -1
#
#   for x in range(x1, x2 + 1):
#     if slope:
#       plot(y,x)
#     else:
#       plot(x,y)
#
#     error = error - dy
#     if error < 0:
#       y += ystep
#       error = error + dx

# colour                     : line_colour
# x location                 : line_x
# y location                 : line_y
# x0 location                : line_x0
# y0 location                : line_y0
# x1 location                : line_x1
# y1 location                : line_y1
# absolute (x1 - x0)         : line_dx 
# absolute (y1 - y0)         : line_dy
# error                      : line_error
# step                       : line_step
# slope                      : line_slope

# -----------
# - CALC DX -
# -----------

draw_line_dx:
    load RA line_x1          # test if X1 > X0
    subm RA line_x0
    move RB RA
    load RA msb_mask
    and RA RB              
    jumpnz draw_line_dx1

draw_line_dx0:
    load RA line_x1          # X1 > X0
    subm RA line_x0
    store RA line_dx         # calc DX
    jump draw_line_dx_exit

draw_line_dx1:
    load RA line_x0          # X0 > X1   
    subm RA line_x1 
    store RA line_dx         # calc DX

draw_line_dx_exit:
    ret

# -----------
# - CALC DY -
# -----------

draw_line_dy:
    load RA line_y1          # test if Y1 > Y0
    subm RA line_y0
    move RB RA
    load RA msb_mask
    and RA RB  
    jumpnz draw_line_dy1

draw_line_dy0:
    load RA line_y1          # Y1 > Y0     
    subm RA line_y0 
    store RA line_dy         # calc DY
    jump draw_line_dy_exit

draw_line_dy1:
    load RA line_y0          # Y0 > Y2     
    subm RA line_y1 
    store RA line_dy         # calc DY

draw_line_dy_exit:
    ret

# --------------------  
# - CALC LINE POINTS -
# --------------------

draw_line:
    call draw_line_dx
    call draw_line_dy

draw_line_slope:
    move RA 0
    store RA line_slope

    load RA line_dx          # is dy > dx
    subm RA line_dy   
    
#   use jumpc is available
    jumpc draw_line_slope_gt1   
        
#   move RB RA
#   load RA msb_mask
#   and RA RB
#   jumpnz draw_line_slope_gt1
    jump draw_line_slope_lt1

draw_line_slope_gt1:
    move RA 1
    store RA line_slope

    load RA line_y0         # x0,y0 = y0,x0
    move RB RA
    load RA line_x0
    store RA line_y0
    move RA RB 
    store RA line_x0

    load RA line_y1         # x1,y1 = y1,x1
    move RB RA
    load RA line_x1
    store RA line_y1
    move RA RB
    store RA line_x1 

draw_line_slope_lt1:
    load RA line_x1         # x0 > x1?
    subm RA line_x0

#   use jumpc is available
    jumpc draw_line_update1

#   move RB RA
#   load RA msb_mask
#   and RA RB
#   jumpnz draw_line_update1
    jump draw_line_start

draw_line_update1:
    move RC 0xFF
    load RA line_x0         # x0,x1 = x1,x0
    move RB RA
    load RA line_x1
    store RA line_x0
    move RA RB 
    store RA line_x1

    load RA line_y0         # y0,y1 = y1,y0
    move RB RA
    load RA line_y1
    store RA line_y0
    move RA RB 
    store RA line_y1

draw_line_start:
    call draw_line_dx
    call draw_line_dy

    load RA line_dx
    asr RA
    store RA line_error

    load RA line_y0
    store RA line_y
    load RA line_x0
    store RA line_x

    move RA 1
    store RA line_step

    load RA line_y0              
    subm RA line_y1

#   use jumpc is available
    jumpc draw_line_colour
 
#   move RB RA
#   load RA msb_mask
#   and RA RB
#   jumpnz draw_line_colour

    move RA 0xFF
    store RA line_step

draw_line_colour:
    load RA line_colour     # set line colour
    store RA pixel_colour

#    move RD 0xFF
#    load RA line_dx
#    move RD RA
#    load RA line_dy
#    move RD RA
#    load RA line_error
#    move RD RA
#    load RA line_step
#    move RD RA
#    load RA line_x0
#    move RD RA
#    load RA line_x1
#    move RD RA

draw_line_loop:
    load RA line_slope         # is dy > dx 
    and RA 0xFF
    jumpnz draw_line_loop_gt1
    jump draw_line_loop_lt1

draw_line_loop_gt1:
    load RA line_y          # set XY 
    store RA pixel_x_pos
    load RA line_x
    store RA pixel_y_pos
    jump draw_line_loop_update

draw_line_loop_lt1:
    load RA line_x          # set XY 
    store RA pixel_x_pos
    load RA line_y
    store RA pixel_y_pos

draw_line_loop_update:
    call draw_pixel          # plot XY

    load RA line_error
    subm RA line_dy
    store RA line_error

# use jumpc if available
    jumpc draw_line_error_lt0

#   move RB RA
#   load RA msb_mask
#   and RA RB
#   jumpnz draw_line_error_lt0
    jump draw_line_next

draw_line_error_lt0:
    load RA line_y
    addm RA line_step
    store RA line_y

    load RA line_error
    addm RA line_dx
    store RA line_error

draw_line_next:
    load RA line_x
    add RA 1
    store RA line_x

#   move RD 0xFF
#   load RA line_x
#   move RD RA
#   load RA line_y
#   move RD RA

    load RA line_x1
    subm RA line_x

# use jumpc if available
    jumpc draw_line_exit
 
#   move RB RA
#   load RA msb_mask
#   and RA RB
#   jumpnz draw_line_exit 
    jump draw_line_loop

draw_line_exit:
    ret

# ---------------
# - DRAW CIRCLE -
# ---------------

# Plot circle with mid-point x0, y0 and radius
# Bresenham's circle drawing algorithm

#  x = radius
#  y = 0
#  err = 0
#
#  while x >= y:
#    plot((x0 + x, y0 + y))  A
#    plot((x0 + y, y0 + x))  B
#    plot((x0 - y, y0 + x))  C
#    plot((x0 - x, y0 + y))  D
#    plot((x0 - x, y0 - y))  E
#    plot((x0 - y, y0 - x))  F
#    plot((x0 + y, y0 - x))  G 
#    plot((x0 + x, y0 - y))  H
#
#    y += 1
#    err += 1 + 2*y
#    if 2*(err-x) + 1 > 0:
#      x -= 1
#      err += 1 - 2*x

# colour            : circle_colour
# filled            : circle_filled
# x location        : circle_x
# y location        : circle_y
# midpoint x        : circle_x0
# midpoint y        : circle_y0
# radius            : circle_r
# error             : circle_error 

# A - B, C - D, E - F, G - H

#( p.x+x, p.y+y ) to ( p.x-x, p.y+y )
#       A                    D
#( p.x+y, p.y+x ) to ( p.x-y, p.y+x )
#       B                    C
#( p.x+x, p.y-y ) to ( p.x-x, p.y-y )
#       H                    E
#( p.x+y, p.y-x ) to ( p.x-y, p.y-x )
#       G                    F

draw_circle:
    load RA circle_r
    store RA circle_x
    move RA 0
    store RA circle_y
    store RA circle_error

    load RA circle_colour     # set line colour
    store RA line_colour
    store RA pixel_colour

draw_circle_loop:
    load RA circle_x
    subm RA circle_y

# use jumpc if available
    jumpc circle_exit

#   move RB RA
#   load RA msb_mask
#   and RA RB
#   jumpnz circle_exit

circle_A:
    load RA circle_x0
    addm RA circle_x
    store RA pixel_x_pos
    store RA circle_A_x   

    load RA circle_y0
    addm RA circle_y
    store RA pixel_y_pos
    store RA circle_A_y

    load RA circle_filled
    jumpnz circle_B
    call draw_pixel          # plot((x0 + x, y0 + y)) A

circle_B:
    load RA circle_x0
    addm RA circle_y
    store RA pixel_x_pos
    store RA circle_B_x   

    load RA circle_y0
    addm RA circle_x
    store RA pixel_y_pos
    store RA circle_B_y   

    load RA circle_filled
    jumpnz circle_C
    call draw_pixel          # plot((x0 + y, y0 + x)) B

circle_C:
    load RA circle_x0
    subm RA circle_y
    store RA pixel_x_pos
    store RA circle_C_x   

    load RA circle_y0
    addm RA circle_x
    store RA pixel_y_pos
    store RA circle_C_y   

    load RA circle_filled
    jumpnz circle_D
    call draw_pixel          # plot((x0 - y, y0 + x)) C

circle_D:
    load RA circle_x0
    subm RA circle_x
    store RA pixel_x_pos
    store RA circle_D_x   

    load RA circle_y0
    addm RA circle_y
    store RA pixel_y_pos
    store RA circle_D_y   

    load RA circle_filled
    jumpnz circle_E
    call draw_pixel          # plot((x0 - x, y0 + y)) D

circle_E:
    load RA circle_x0
    subm RA circle_x
    store RA pixel_x_pos
    store RA circle_E_x   

    load RA circle_y0
    subm RA circle_y
    store RA pixel_y_pos
    store RA circle_E_y   

    load RA circle_filled
    jumpnz circle_F
    call draw_pixel          # plot((x0 - x, y0 - y)) E

circle_F:
    load RA circle_x0
    subm RA circle_y
    store RA pixel_x_pos
    store RA circle_F_x   

    load RA circle_y0
    subm RA circle_x
    store RA pixel_y_pos
    store RA circle_F_y   

    load RA circle_filled
    jumpnz circle_G
    call draw_pixel          # plot((x0 - y, y0 - x)) F

circle_G:
    load RA circle_x0
    addm RA circle_y
    store RA pixel_x_pos
    store RA circle_G_x   

    load RA circle_y0
    subm RA circle_x
    store RA pixel_y_pos
    store RA circle_G_y   

    load RA circle_filled
    jumpnz circle_H
    call draw_pixel          # plot((x0 + y, y0 - x)) G

circle_H:
    load RA circle_x0
    addm RA circle_x
    store RA pixel_x_pos
    store RA circle_H_x   

    load RA circle_y0
    subm RA circle_y
    store RA pixel_y_pos
    store RA circle_H_y   

    load RA circle_filled
    jumpnz circle_fill
    call draw_pixel          # plot((x0 + x, y0 - y)) H
    jump circle_next

#( p.x+x, p.y+y ) to ( p.x-x, p.y+y )
#       A                    D
#( p.x+y, p.y+x ) to ( p.x-y, p.y+x )
#       B                    C
#( p.x+x, p.y-y ) to ( p.x-x, p.y-y )
#       H                    E
#( p.x+y, p.y-x ) to ( p.x-y, p.y-x )
#       G                    F

circle_fill:
    load RA circle_A_x     
    store RA line_x0 
    load RA circle_A_y
    store RA line_y0

    load RA circle_D_x            
    store RA line_x1 
    load RA circle_D_y  
    store RA line_y1

    call draw_line

    load RA circle_B_x     
    store RA line_x0 
    load RA circle_B_y
    store RA line_y0

    load RA circle_C_x            
    store RA line_x1 
    load RA circle_C_y  
    store RA line_y1

    call draw_line

    load RA circle_H_x     
    store RA line_x0 
    load RA circle_H_y
    store RA line_y0

    load RA circle_E_x            
    store RA line_x1 
    load RA circle_E_y  
    store RA line_y1

    call draw_line

    load RA circle_G_x     
    store RA line_x0 
    load RA circle_G_y
    store RA line_y0

    load RA circle_F_x            
    store RA line_x1 
    load RA circle_F_y  
    store RA line_y1

    call draw_line

#    y += 1
#    err += 1 + 2*y
#    if 2*(err-x) + 1 > 0:
#      x -= 1
#      err += 1 - 2*x

circle_next:
    load RA circle_y
    add RA 1
    store RA circle_y
    
    addm RA circle_y
    addm RA circle_error
    add RA 1
    store RA circle_error

    load RA circle_error
    subm RA circle_x
#   move RB RA
#   add RA RB
    add RA RA
    add RA 1

# use jumpc if availabl
    jumpc draw_circle_loop

#   move RB RA
#   load RA msb_mask
#   and RA RB
#   jumpnz draw_circle_loop

    load RA circle_x
    sub RA 1
    store RA circle_x

    addm RA circle_x
    move RB RA
    load RA circle_error
    add RA 1
    sub RA RB
    store RA circle_error
    jump draw_circle_loop

circle_exit:
    ret
#>>

####################
# LIST SUBROUTINES #
####################
#<<
# ------------------
# - LIST GET INDEX -
# ------------------

listGetIndex:
    load RA listIndex          # get index 
    addm RA listAddr           # add base addr  
    load RA (RA)               # read data
    store RA listData          # store to buffer
    ret

# -----------------
# - LIST GET TAIL -
# -----------------

listGetTail:
    load RA listTailPtr        # get pntr address           
    load RA (RA)               # read index
    sub RA 1                   # dec ptr to last written addr
    addm RA listAddr           # add base addr  
    load RA (RA)               # read data
    store RA listData          # store to buffer
    ret

# --------------------
# - LIST DELETE HEAD -
# --------------------

listDeleteHead:
    load RA listTailPtr            # get pntr address
    load RA (RA)                   # load index
    sub RA 0                       # is list empty?
    jumpz listDeleteHead_exit      # yes

listDeleteHead_data:
    load RA listAddr
    move RC RA                     # RC=dst 
    add RA 1                       # RB=src
    move RB RA

listDeleteHead_loop:    
    load RA (RB)                   # load data 
    store RA (RC)                  # shuffle data up
    add RB 1                       # move pointers
    add RC 1
    load RA listTailPtr            # reached end of list?
    sub RA RB                      # if src = tail finish
    jumpnz listDeleteHead_loop

    load RA listTailPtr            # load tail ptr
    move RB RA                     # buffer address
    load RA (RB)                   # load index
    sub RA 1                       # inc index
    store RA (RB)                  # update index

listDeleteHead_exit:
    ret

# --------------------
# - LIST DELETE TAIL -
# --------------------

listDeleteTail:
    load RA listTailPtr        # get pntr address  
    move RB RA                 # buffer pntr
    load RA (RB)               # read index
    sub RA 0                   # is index 0?
    jumpz listDeleteTail_exit  # yes, exit

    sub RA 1                   # no, decrement
    store RA (RB)              # store index

listDeleteTail_exit:
    ret

# --------------------
# - LIST APPEND HEAD -
# --------------------

listAppendHead:
    load RA listTailPtr            # load index addr
    load RA (RA)                   # load index
    move RB RA                     # buffer index
    addm RA listAddr               # generate read address
    move RC RA                     # RC=dst

    sub RB 0                       # is list empty?
    jumpz listAppendHead_update    # yes

    moved( RA, MAX_LIST_SIZE )     # max index
    sub RA RB
    jumpz listAppendHead_exit     # 0=full 0!=space

listAppendHead_data:
    move RB RC
    sub RB 1                       # RB=src

listAppendHead_loop:    
    load RA (RB)                   # load data 
    store RA (RC)                  # shuffle data down
    sub RB 1                       # move pointers
    sub RC 1
    load RA listAddr               # read head of list addr
    sub RA RC                      # if dst = head finish
    jumpnz listAppendHead_loop

listAppendHead_update:
    load RA listData               # write data to head of list
    store RA (RC)

    load RA listTailPtr            # load tail pointer
    move RB RA                     # buffer address
    load RA (RB)                   # load index
    add RA 1                       # inc index
    store RA (RB)                  # update index

listAppendHead_exit:
    ret

# --------------------
# - LIST APPEND TAIL -
# --------------------

listAppendTail:
    load RA listTailPtr            # load index addr
    load RA (RA)                   # load index
    addm RA listAddr               # generate read address
    move RC RA                     # RC=dst

    moved( RA, MAX_LIST_SIZE )     # max index
    sub RA RB
    jumpz listAppendTail_exit      # 0=full 0!=space

    load RA listData
    store RA (RC)

    load RA listTailPtr            # inc tail pointer 
    move RB RA
    load RA (RA)                   #
    add RA 1                       # 
    store RA (RB)

listAppendTail_exit:
    ret

#>>

####################
# MATH SUBROUTINES #
####################
#<<
# -----------------
# - RANDOM NUMBER -
# -----------------

random:
    load RA num                 # base number (8bit) 
    move RC RA                  # 
    movea( RA, randomNumber )   # get base address
    addm RA randomNumberIndex   # add index
    load RB (RA)                # read random number (0-255)
    mul RC RB                   # mul
    asr RC                      # /256 
    asr RC
    asr RC
    asr RC     
    asr RC
    asr RC
    asr RC
    asr RC  
    load RA random_mask
    and RA RC
    store RA num
    sub RA 20
    jumpc random_min
    jump random_update 

random_min:
    move RA 24
    store RA num

random_update:
    moved( RB, MAX_RND_LIST_SIZE )  # is index > list size
    load RA randomNumberIndex    
    sub RB RA
    jumpnz random_exit

    move RA 0xFF                # set index -1 
  
random_exit:
    add RA 1                    # inc index 
    store RA randomNumberIndex
    ret
#>>
#>>

#<< CONSTANTS

###########################
# CONSTANTS AND VARIABLES #
###########################
#<<
negative_sign_extend:
	.data 0xFF00

random_mask:
    .data 0xFFFC

msb_mask:
    .data 0x8000

count:
    .data 0

line:
    .data 0

# -------------------
# - Block Variables -
# -------------------

block_colour:
    .data 0

block_x_top_pos:
    .data 0

block_x_bottom_pos:
    .data 0

block_y_top_pos:
    .data 0

block_y_bottom_pos:
    .data 0

block_start_address:
    .data 0

block_width:
    .data 0

block_depth:
    .data 0

block_destination_address:
    .data 0

# ---------------------
# - Convert Variables -
# ---------------------

word:
    .data 0

byte:
    .data 0

nibble:
    .data 0

bin_2_dec_1s_cnt:
	.data 0

bin_2_dec_10s_cnt:
	.data 0

bin_2_dec_100s_cnt:
	.data 0

# --------------------
# - String Variables -
# --------------------

srcAddr:
    .data 0x00

destAddr:
    .data 0x00

# -------------------------
# - Serial Port Variables -
# -------------------------

txBufferIndex:
    .data 0x00

rxBufferIndex:
    .data 0x00

keyPressed:
    .data 0x00

txBuffer:
    space( 64 )

rxBuffer:
    space( 64 )

# --------------------
# - Bin to Hex table -
# --------------------

nibble2HexTable:
    .data 0x30    # '0'
    .data 0x31    # '1' 
    .data 0x32    # '2'
    .data 0x33    # '3' 
    .data 0x34    # '4'
    .data 0x35    # '5' 
    .data 0x36    # '6'
    .data 0x37    # '7' 
    .data 0x38    # '8'
    .data 0x39    # '9' 
    .data 0x41    # 'A'
    .data 0x42    # 'B' 
    .data 0x43    # 'C'
    .data 0x44    # 'D' 
    .data 0x45    # 'E'
    .data 0x46    # 'F' 

# ---------------
# - Test String -
# ---------------

hello:
    .data 0x48    # H
    .data 0x65    # e
    .data 0x6C    # l
    .data 0x6C    # l
    .data 0x6F    # 0
    .data 0x20    # SPACE
    .data 0x57    # W
    .data 0x6F    # o
    .data 0x72    # r
    .data 0x6C    # l
    .data 0x64    # d
    .data 0x0A    # LF
    .data 0x0D    # CR
    .data 0x00    # NULL

# ------------------
# - Welcome string -
# ------------------

welcome:
    .data 0x0A    # LF
    .data 0x0D    # CR
    .data 0x53    # S
    .data 0x69    # i
    .data 0x6D    # m
    .data 0x70    # p
    .data 0x6C    # l
    .data 0x65    # e
    .data 0x43    # C
    .data 0x50    # P
    .data 0x55    # U
    .data 0x20    # SPACE
    .data 0x44    # D
    .data 0x65    # e
    .data 0x62    # b
    .data 0x75    # u
    .data 0x67    # g
    .data 0x0A    # LF
    .data 0x0D    # CR
    .data 0x00    # NULL

# ------------------
# - Software delay -
# ------------------

delay_low_count:
	.data 0

delay_high_count:
	.data 0

# ---------
# - Pixel -
# ---------

pixel_colour:
    .data 0

pixel_x_pos:
    .data 0

pixel_y_pos:
    .data 0

# ---------
# - Line  -
# ---------

line_colour:
    .data 0

line_x:
    .data 0

line_y:
    .data 0

line_x0:
    .data 0

line_y0:
    .data 0

line_x1:
    .data 0

line_y1:
    .data 0

line_dx:
    .data 0

line_dy:
    .data 0

line_error:
    .data 0

line_step:
    .data 0

line_slope:
    .data 0

# ----------
# - Circle -
# ----------

circle_colour:
    .data 0

circle_filled:
    .data 0

circle_x:
    .data 0

circle_y:
    .data 0

circle_x0:
    .data 0

circle_y0:
    .data 0

circle_r:
    .data 0

circle_error:
    .data 0 

circle_A_x:
    .data 0 

circle_A_y:
    .data 0 

circle_B_x:
    .data 0 

circle_B_y:
    .data 0 

circle_C_x:
    .data 0 

circle_C_y:
    .data 0 

circle_D_x:
    .data 0 

circle_D_y:
    .data 0 

circle_E_x:
    .data 0 

circle_E_y:
    .data 0 

circle_F_x:
    .data 0 

circle_F_y:
    .data 0 

circle_G_x:
    .data 0 

circle_G_y:
    .data 0 

circle_H_x:
    .data 0 

circle_H_y:
    .data 0 

# -----------
# - Sprites -
# -----------

sprite:
    .data 0
    
sprite_command:
    .data 0

sprite_pntr:
    .data 0

sprite_pntr_1:
    .data 0

sprite_collision:
    .data 0

sprite_tile:
    .data 0

sprite_x_offset:
    .data 0

sprite_y_offset:
    .data 0

sprite_x_pos:
    .data 0

sprite_x_pos_1:
    .data 0

sprite_y_pos:
    .data 0

sprite_y_pos_1:
    .data 0

sprite_hitbox_x_min:
    .data 0

sprite_hitbox_x_min_1:
    .data 0

sprite_hitbox_x_max:
    .data 0

sprite_hitbox_x_max_1:
    .data 0

sprite_hitbox_y_min:
    .data 0

sprite_hitbox_y_min_1:
    .data 0

sprite_hitbox_y_max:
    .data 0

sprite_hitbox_y_max_1:
    .data 0

sprite_limit_x_min:
    .data 0

sprite_limit_x_max:
    .data 0

sprite_limit_y_min:
    .data 0

sprite_limit_y_max:
    .data 0

sprite_edge:
    .data 0

sprite_number:
    .data 0

# -------------- 
# - Controller -
# --------------

rotary_switch:
	.data 0
	
fire_switch:
	.data 0
	
snes_switch:
	.data 0	

key:
    .data 0

# --------------- 
# - LCD Display -
# ---------------

lcd_delay_count:
	.data 0

lcd_pntr:
    .data 0

lcd_config:
    .data 0

# 0011 0011 Initialise
# 0011 0010 Initialise
# 0000 0110 Cursor move direction
# 0000 1100 Display On,Cursor Off, Blink Off
# 0010 1000 Data length, number of lines, font size
# 0000 0001 Clear display
# 1000 0000 LCD RAM address for the 1st line

init_string:
    .data 0x33
    .data 0x32
    .data 0x06
    .data 0x0C
    .data 0x28
    .data 0x01
    .data 0x80
    .data 0

message_string:
    .data 0x4F # O
    .data 0x58 # X
    .data 0x4F # O
    .data 0x20 # space
    .data 0x44 # D
    .data 0x65 # e
    .data 0x6D # m
    .data 0x6F # o
    .data 0

# --------- 
# - Maths -
# ---------

num:
    .data 0

randomNumberIndex:
    .data 0

randomNumber:
    .data 50
    .data 100
    .data 224
    .data 221
    .data 40
    .data 210
    .data 31
    .data 218
    .data 181
    .data 129
    .data 92
    .data 228
    .data 248
    .data 76
    .data 69
    .data 127
    .data 195
    .data 199
    .data 219
    .data 149
    .data 196
    .data 143
    .data 243
    .data 205
    .data 105
    .data 115
    .data 142
    .data 192
    .data 168
    .data 256
    .data 28
    .data 145
    .data 52
    .data 27
    .data 50
    .data 155
    .data 190
    .data 44
    .data 124
    .data 118
    .data 251
    .data 128
    .data 107
    .data 232
    .data 247
    .data 60
    .data 102
    .data 35
    .data 200
    .data 39
    .data 252
    .data 73
    .data 123
    .data 198
    .data 99
    .data 165
    .data 254
    .data 49
    .data 185
    .data 12
    .data 203
    .data 121
    .data 67
    .data 184
    .data 136
    .data 30
    .data 179
    .data 100
    .data 183
    .data 197
    .data 255
    .data 48
    .data 146
    .data 86
    .data 139
    .data 208
    .data 175
    .data 186
    .data 89
    .data 101
    .data 158
    .data 32
    .data 156
    .data 242
    .data 238
    .data 207
    .data 225
    .data 11
    .data 62
    .data 154
    .data 37
    .data 163
    .data 138
    .data 103
    .data 135
    .data 94
    .data 239
    .data 15
    .data 125
    .data 236
    .data 217
    .data 66
    .data 216
    .data 202
    .data 24
    .data 106
    .data 180
    .data 172
    .data 77
    .data 18
    .data 14
    .data 63
    .data 53
    .data 78
    .data 176
    .data 204
    .data 26
    .data 234
    .data 84
    .data 25
    .data 34
    .data 85
    .data 171
    .data 194
    .data 170
    .data 206
    .data 116
    .data 64
    .data 148
    .data 222
    .data 249
    .data 241
    .data 51
    .data 229
    .data 144
    .data 152
    .data 45
    .data 161
    .data 240
    .data 157
    .data 20
    .data 117
    .data 80
    .data 23
    .data 90
    .data 131
    .data 134
    .data 108
    .data 191
    .data 160
    .data 46
    .data 93
    .data 36
    .data 91
    .data 233
    .data 147
    .data 230
    .data 58
    .data 231
    .data 56
    .data 110
    .data 42
    .data 57
    .data 226
    .data 174
    .data 167
    .data 72
    .data 83
    .data 214
    .data 119
    .data 223
    .data 55
    .data 98
    .data 97
    .data 133
    .data 173
    .data 79
    .data 59
    .data 95
    .data 178
    .data 193
    .data 82
    .data 126
    .data 215
    .data 75
    .data 21
    .data 151
    .data 253
    .data 112
    .data 13
    .data 114
    .data 54
    .data 209
    .data 245
    .data 81
    .data 104
    .data 38
    .data 227
    .data 68
    .data 19
    .data 132

# -------- 
# - List -
# --------

listHeadPtr:
    .data 0

listTailPtr:
    .data 0

listIndex:
    .data 0

listAddr:
    .data 0

listData:
    .data 0

#>>

##################
# GAME VARIABLES #
##################
#<<
food_eaten:
    .data 0

finish:
    .data 0

x_pos:
    .data 0

y_pos:
    .data 0

player_score:
    .data 0

snake_x_pos:
    space( 100 )

snake_x_tail_ptr:
    .data 0

snake_x_data:
    .data 0

snake_y_pos:
    space( 100 )

snake_y_tail_ptr:
    .data 0

snake_y_data:
    .data 0

food_x_pos:
    .data 0

food_y_pos:
    .data 0
#>>

###############
# SPRITE DATA #
###############
#<<


#>>
#>>
