Welcome to the C64 Assembly Project of 2020

I have finally begun work on laying out the plans to expand the “Machine Language Project” that came into creation in 2016 on YouTube after a series of video promotions. In this section you will find information that highlights the various functions and files that make up the “game in progress”. Be sure to visit my page C64 Assembly Language Project 2020″ to learn more about the scope of this project. To keep this project organized, I have divided this into a series of compartments as listed below.

Variable Constants – Usually found at the beginning of the code. These are used all throughout the project for player data, memory addresses, and game mechanics.

Functions Defined: This shows all of the functions (subroutines) that exist within a given file. I have also included several definitions of the functionality of each. Variables – these are listed at the top for easier access. When breaking apart a large program like this one, it is essential to have the definitions in plain sight since they are distributed all throughout the entire project.

Subroutine (JSR): This section highlights all of the jsr (Jump to Subroutine) opcodes that exist within a specific file. It is used to trace the flow of the program as it compiles into memory and begins execution.

Return from Subroutine: This remembers the last area the program was before using a jsr (xxx) – “Jump to Subroutine”. It is useful to track the last location in memory so that the program provides a consistent flow.

Projects 1-9

This tutorial currently is divided into Projects 1-9 as seen on YouTube. Currently there are several file type associated that make up the whole “game in progress”

Throughout this document “File Name” represents the file that is being referenced within the CBM Prg Studio environment. This is listed for easier accessibility to that particular part of the project.

Return back from Subroutine: The code below this section continues back to the previous file right after the last JSR was executed (works very similar to a GOSUB/RETURN as seen in Basic language programming).

Note: This document will continue to be updated. So be sure to bookmark and favorite this page! Coming soon – the new project upload.

The original game project can be downloaded clicking on the button Download Project. This current game has created several enhanced alterations to maintain more control over the flow of the new project. The page for the project (“Spelunker Clone”) can be viewed here.

Alterations to C64 Assembly Project

When I started making my own changes to the project, I felt it was necessary to document what was adjusted to handle the new expansion. So I have listed the following code below and why those individual segments were changed. File location: Main.asm Changed the starting position of where the map centers at on the game player.

ldx #12 ; X start position (in tile coordinates)
ldy #23 ; Y start position (in tile coordinates)
jsr DrawMap

File Name: [Player.asm]

Originally changed the ability to move up and down to test the new sprite animation in the set direction.

Function altered: PlayerStateIdle

Unused Function: PlayerState_Framework.

Breaking down the C64 Code

For the start of this project, I will begin by showing small code segments so it will be easier to follow the logic step by step. The file name will be listed at the top (ending in “asm”) and the variables and code will follow afterwards. When the program encounters a JSR, the program will then show the subroutine so we can attempt to follow a direct path to learn how everything fits together.


Start of the C64 Project

Main.asm Variables accessed in this file:

VIC_SPRITE_ENABLE: $D015 (53269)
VIC_SCREEN_CONTROL: $D011 (53265)
VIC_BANK: $DD00 (56576)
VIC_MEMORY_CONTROL: $D018 (53272)
VIC_SPRITE_X_EXTEND: $D010 (53264)
SPRITE_POS_X_EXTEND

Set Sprite Multicolors:

VIC_SPRITE_MULTICOLOR: $D016 (53276)
VIC_SPRITE_MULTICOLOR_1: $D025 (53285)
VIC_SPRITE_MULTICOLOR_2: $D026 (53286)
Set Sprite Colors:
VIC_SPRITE_COLOR: $D027(53287)
VIC_SPRITE_COLOR_1:$D028 (53288)
VIC_SPRITE_COLOR + 7: $D02E (53294)

Border/Background Colors:

VIC_BORDER_COLOR: $D020 (53280)
VIC_BACKGROUND_COLOR: $D021 (53281)

Set Character(Tile) Multicolors:

VIC_CHARSET_MULTICOLOR_1: $D022 (53282)
VIC_CHARSET_MULTICOLOR_2: $D023 (53283)


Explanation of Main.asm

First we start off the game project by disabling our sprites

lda #0
sta VIC_SPRITE_ENABLE

Next we Mask our bit 4. This bit controls will blank out the entire screen to the same color as the border. In this example we mask it out to temporarily turn off the screen.

lda VIC_SCREEN_CONTROL ; turn screen off with bit 4
and #%11100000 ; mask out bit 4 – Screen on/off
sta VIC_SCREEN_CONTROL ; save back – setting bit 4 to off

Set the C64 Vic Bank

lda VIC_BANK ; Fetch the status of CIA 2 ($DD00)
and #%11111100 ; mask for bits 2-8
ora #%00000010 ; the first 2 bits are the VIC bank value

; This is set to bank 1 ($4000 = $7FFF)

sta VIC_BANK

Define C64 Character Set Area

Routines (JSR): CopyChars, ClearScreen1, ClearScreen2, ClearScore Screen, ClearColorRam, LoadLevel, DrawMap, CopyToBuffer, DisplayText, DisplaySpriteInfoNow, WaitFrame, UpdateTimers, UpdateScroll, UpdatePlayer, CONSOLE_DISPLAY, IncreaseScore, DisplayByte

; Using the Vic Bank we can setup our screen and character set memory using the VIC_MEMORY_CONTROL at $D018

lda #%00000010 ; bits 1-3 (001) = character memory 2: $0800 – $0FFF)
sta VIC_MEMORY_CONTROL

; The screen memory is set to $4000 and the character set is at $4800
; Sprite pointers start at $4000 + $3f8
; Sprite data starts at $5C00 using up to 144 sprite images

Copy Level 1 Data

; Here is where we copy level 1 data from the start setup to under $E000 so it can be used later when the game resets (using some bank switching).

: first we load the Processor Port ($0001). Then it is used to turn off Basic (LORAM), Kernal (HIRAM), CHARACTER ROM (CHAREN). After this a routine is used to copy the sprite and chraacter memory under there before the value of $0001 is restored and the interrupts are reactivated.

lda PROC_PORT ; store ram setup
sta PARAM1

lda #%00110000 ; Switch out BASIC, KERNAL, CHAREN, IO
sta PROC_PORT

At the start of the game, Level 1 tiles and characters are stored in place to run. When the game resets these will need to be restored to keep the levels intact.
They are saved here to be loaded later under the KERNAL at $E000-$EFFF (4k).
In order to do this some bank switching is necessary, copying data, and then restored as the KERNAL could be used for other things later in the game.

Macros being used

loadPointer ZEROPAGE_POINTER_1, MAP_MEM ; source
loadPointer ZEROPAGE_POINTER_2, LEVEL_1_MAP ; destination
jsr CopyChars ; copy 2048 bytes of character data.

loadPointer, ZEROPAGE_POINTER_1, CHAR_MEM
loadPointer, ZEROPAGE_POINTER_2, LEVEL_1_chars

jsr CopyChars


Subroutine (JSR): CopyChars

File Name: [core_routines.asm]

Then the program executes a subroutine to “CopyChars” as seen below. By copying data from  ZEROPAGE_POINTER_1 into ZEROPAGE_POINTER_2 we are able to work with new characters that make up the screen.

CopyChars

saveRegs

ldx #$00 ; clear X, Y, A and PARAM2
ldy #$00
lda #$00
sta PARAM2

@NextLine
lda (ZEROPAGE_POINTER_1),Y ; copy from source to target
sta (ZEROPAGE_POINTER_2),Y

inx ; increment x / y
iny
cpx #$08 ; test for next character block (8 bytes)
bne @NextLine ; copy next line
cpy #$00 ; test for edge of page (256 wraps back to 0)
bne @PageBoundryNotReached

inc ZEROPAGE_POINTER_1 + 1 ; if reached 256 bytes, increment high byte
inc ZEROPAGE_POINTER_2 + 1 ; of source and target

@PageBoundryNotReached
inc PARAM2 ; Only copy 254 characters (to keep irq vectors intact)
lda PARAM2 ; If copying to F000-FFFF block
cmp #255
beq @CopyCharactersDone
ldx #$00
jmp @NextLine

@CopyCharactersDone

restoreRegs
rts


Return from Subroutine: [Main.asm]

lda PARAM1 ; restore ram setup
sta PROC_PORT
cli

C64 Game screen setup

In this area we setup the background color for the screen, multicolors, and CURRENT_SCREEN to SCREEN1_MEM and CURRENT_BUFFER to SCREEN2_MEM.

Subroutine : Screen_Setup

File Name: [Main.asm]

Screen_Setup
lda #COLOR_ORANGE
sta VIC_BACKGROUND_COLOR

lda #COLOR_ORANGE
sta VIC_CHARSET_MULTICOLOR_1
lda #COLOR_BROWN
sta VIC_CHARSET_MULTICOLOR_2

loadPointer CURRENT_SCREEN,SCREEN1_MEM
loadPointer CURRENT_BUFFER,SCREEN2_MEM

lda #$40 ; Use #$40 as the fill character on GameScreen
jsr ClearScreen1 ; Clear both screens (double buffer)

We will be clearing 2 different screens. Two screens are necessary to achieve a nice fine scrolling effect in a game. The reason is because the Commodore 64 is constantly updating the screen during interrupt periods. So first we clear them, then we will copy data, switching from SCREEN1_MEM and then to SCREEN2_MEM cycling back in forth. More on this later.


Subroutine (JSR): ClearScreen1

File Name: [screen_routines.asm]

ClearScreen1
ldx #$00
@clearLoop
sta SCREEN1_MEM,x ; clear SCREEN1_MEM
sta SCREEN1_MEM + 250,x
sta SCREEN1_MEM + 500,x ; Game screen only goes to 720
sta SCREEN1_MEM + 750,x
inx
cpx #250
bne @clearLoop
rts


Return back from Subroutine: [Main.asm]

jsr ClearScreen2; clear SCREEN2_MEM


Subroutine (JSR): ClearScreen2

File name: [screen_routines.asm]

ClearScreen2
ldx #$00
@clearLoop
sta SCREEN2_MEM,x
sta SCREEN2_MEM + 250,x
sta SCREEN2_MEM + 500,x ; Game screen only goes to 720
sta SCREEN2_MEM + 750,x
inx
cpx #250
bne @clearloop
rts


Return from Subroutine: [Main.asm]

lda #32 ; fill character on the
jsr ClearScoreScreen


Subroutine (JSR): ClearScoreScreen

File Name: [screen_routines.asm]

ClearScoreScreen
ldx #$00
@clearLoop
sta SCORE_SCREEN,x
sta SCORE_SCREEN + 250,x
sta SCORE_SCREEN + 500,x
sta SCORE_SCREEN + 750,x
inx
cpx #250
bne @clearloop
rts


Return from Subroutine: [Main.asm]

lda #COLOR_BLUE ; Fill entire visible color with COLOR_BLUE
jsr ClearColorRam

The Commodore 64 screen is made up of tiny pixels that can be lit up with bytes in the Color RAM. This section is used to clear the Color RAM to prepare for a new screen. At this time you will likely see squiggly lines (like below) since the character set has not been defined yet and the memory is now pointing to an empty set in memory (filled with garbage characters).  Please also remember that the bank is now pointing to $0800 – $0fff.

Change VIC Bank Memory R


Subroutine (JSR): ClearColorRam

File Name: [screen_routines.asm]

ClearColorRam
ldx #$00
@clearLoop
sta COLOR_MEM,x
sta COLOR_MEM + 250,x
sta COLOR_MEM + 500,x
sta COLOR_MEM + 750,x
inx
cpx #250
bne @clearLoop
rts


Return from Subroutine: [Main.asm]

In this phase we begin to setup the vertical scroll (SCROLL_COUNT_Y), horizontal scroll (SCROLL_COUNT_X), direction of scroll (SCROLL_STOP), and the scrolling speed (SCROLL_SPEED).

C64 Vertical Scroll Start

lda #3 ; begin vertical scroll at 3
sta SCROLL_COUNT_Y ; which is the default

C64 Horizontal Scroll Start

lda #4 ; start centered on character
sta SCROLL_COUNT_X ; at X #4

lda #SCROLL_STOP ; direction = up
sta SCROLL_DIRECTION
lda #0 ; speed = 0
sta SCROLL_SPEED ; (not implemented yet)


Game Map Position

lda #1 ; Start Level = 1
sta CURRENT_LEVEL
jsr LoadLevel ; load level 1 data


Subroutine (JSR): LoadLevel

File Name: [CharPadTools.asm]

LoadLevel
ldx CURRENT_LEVEL ; Load current level number
dex ; subtract 1 (levels start at 1 not 0)
txa
lsr ; multiply by 2 to get a word offset
tax ; put it in x to lookup our table
sta PARAM5 ; save in scratch param (5)

;—————————————————————–
; Here we load all level data into the area below VIC mem ($8000)
; To setup the level

;————————————————— Loading banked data
@loadBanked

sei
lda PROC_PORT
sta PARAM1

lda #%00110000 ; Switch out BASIC, KERNAL, CHAREN, IO
sta PROC_PORT

Now we begin reading from the CHAR_ADDRESS (word for LEVEL_1_MAP – contained in the file “CharPadTools.asm”) which reads from a binary map file that was generated using the application CharPad used to create the game map (as seen below). We will explore reading the DrawMap and DrawTile subroutines later (which is used to complete the map on the screen and fill in each tile (“the little black squares”).

Total Tile Calculations

The following sums below show how to calculate all the pixels in a 4 x 4 map for one individual tile on the screen. The tiles are the blocks that exist in the map above separated by the grid shapes.  An example of the tile editor is found below from the CharPad Editor for the Commodore 64.

Each Tile (“black squares”) are is built like the following

Each Block Contains: 4 pixels across x 8 rows down = 32

Each Row Contains: 4 blocks in a row horizontally: 32 x 4 = 128

Vertical Rows: 4 blocks going down and 4 going across:

Totals: 32 x 4 = 128: So 128 x 4 = 512 (total pixels)

ldx PARAM5
lda CHAR_ADDRESS,x
sta ZEROPAGE_POINTER_1
lda CHAR_ADDRESS + 1,x
sta ZEROPAGE_POINTER_1 + 1

loadPointer ZEROPAGE_POINTER_2, CHAR_MEM

jsr CopyChars

ldx PARAM5
lda MAP_ADDRESS,x
sta ZEROPAGE_POINTER_1
lda MAP_ADDRESS + 1,x
sta ZEROPAGE_POINTER_1 + 1

loadPointer ZEROPAGE_POINTER_2, MAP_MEM
jsr CopyChars

lda PARAM1
sta PROC_PORT
cli
rts


Return from Subroutine: [Main.asm]

Begin copying the data on the screen (SCREEN1_MEM) to a temporary variable (SCREEN2_MEM) being sure to record the entire screen RAM into this area. This is used (as explained earlier) to copy the data from one screen to another to obtain the fastest amount of speed for our pixel scrolling routine, which is used when the screen shifts either to the left, right, up, or down. This takes place in the subroutine CopyToBuffer below.

ldx #26 ; X start position (in tile coordinates)
ldy #0 ; Y start position (in tile coordinates)
jsr DrawMap ; Draw the level map (Screen1)


Subroutine (JSR): DrawMap

File Name: [CharPadTools.asm]

Here we begin marking out the positions for our MAP_X_DELTA (horizontal) and MAP_Y_DELTA (vertical) to check if a character is flush up against an object (more on this later). Then we reset our MAP_X_POS (horizontal) and MAP_Y_POS (vertical) positions to start at the upper corner of the screen when placing a map on the screen from a read file.

DrawMap

lda #0
sta MAP_X_DELTA
sta MAP_Y_DELTA

stx MAP_X_POS
sty MAP_Y_POS

;—————————————————————
; First find the address for the starting map position

ldx MAP_Y_POS

lda MAP_LINE_LOOKUP_LO,x ; fetch the address for the line (Y pos)
sta ZEROPAGE_POINTER_4
lda MAP_LINE_LOOKUP_HI,x
sta ZEROPAGE_POINTER_4 + 1

clc
lda ZEROPAGE_POINTER_4 ; add the x position
adc MAP_X_POS
sta ZEROPAGE_POINTER_4
lda ZEROPAGE_POINTER_4 + 1
adc #0
sta ZEROPAGE_POINTER_4 + 1 ; ZEROPAGE_POINTER_1 now holds the map start address

; Save this info for map usage
copyPointer ZEROPAGE_POINTER_4, MAP_POS_ADDRESS
;————————————————————————————
; Fetch map data and draw tile – coords are in ’tiles’ not character positions

ldy #0 ; holds X screen coord
ldx #0 ; holds Y screen coord
@loop
lda (ZEROPAGE_POINTER_4),y ; fetch map data
jsr DrawTile ; draw the tile


Subroutine (JSR): DrawTile

File Name: [CharPadTools.asm]

DrawTile
sta PARAM1 ; save tile number
sty PARAM2 ; save X pos
stx PARAM3 ; save Y pos

saveRegs ; put registers on the stack to
; exit cleaner – this routine will
; likely be nested
;——————————————————
; First get the destination for the tile

lda PARAM3 ; fetch the Y pos (in tile coords)
asl
asl ; multiply by 4 (tiles are 4 x 4 chars)
tax ; screen line in X

jsr FetchScreenLineAddress ; fetch line address based on current displayed screen
; Y line address is in ZEROPAGE_POINTER_1


Subroutine (JSR): FetchScreenLineAddress

File Name: [screen_routines.asm]

This subroutine is used to detect screen buffering routines that can occur whenever the screen is being updated, which is utilized here for speed of screen redraws.

FetchScreenLineAddress
lda CURRENT_SCREEN + 1
jmp detectScreen

FetchBufferLineAddress
lda CURRENT_BUFFER + 1

detectScreen
cmp #>SCREEN1_MEM
beq @screen1
cmp #>SCREEN2_MEM
beq @screen2
cmp #>SCORE_SCREEN
beq @score
; if none of the above,
it will default to Screen1

@screen1
lda SCREEN1_LINE_OFFSET_TABLE_LO,x
sta ZEROPAGE_POINTER_1
lda SCREEN1_LINE_OFFSET_TABLE_HI,x
sta ZEROPAGE_POINTER_1 + 1
rts

@screen2
lda SCREEN2_LINE_OFFSET_TABLE_LO,x
sta ZEROPAGE_POINTER_1
lda SCREEN2_LINE_OFFSET_TABLE_HI,x
sta ZEROPAGE_POINTER_1 + 1
rts

@score
lda SCORE_LINE_OFFSET_TABLE_LO,x
sta ZEROPAGE_POINTER_1
lda SCORE_LINE_OFFSET_TABLE_HI,x
sta ZEROPAGE_POINTER_1 + 1
rts


Return from Subroutine: [CharPadTools.asm]

lda COLOR_LINE_OFFSET_TABLE_LO,x ; fetch color ram line address too
sta ZEROPAGE_POINTER_3
lda COLOR_LINE_OFFSET_TABLE_HI,x
sta ZEROPAGE_POINTER_3 + 1

lda PARAM2 ; get X coord
asl ; multiply by 4
asl
tax ; save it in x

clc ; add to Y line address
adc ZEROPAGE_POINTER_1
sta ZEROPAGE_POINTER_1
lda ZEROPAGE_POINTER_1 + 1
adc #0
sta ZEROPAGE_POINTER_1 + 1 ; destination base address is in ZEROPAGE_POINTER_1

txa
clc
adc ZEROPAGE_POINTER_3 ; color ram destination is in ZEROPAGE_POINTER_3
sta ZEROPAGE_POINTER_3
lda ZEROPAGE_POINTER_3 + 1
adc #0
sta ZEROPAGE_POINTER_3 + 1

;————————————————————
; Fetch the source tile address

ldx PARAM1 ; Fetch the tile number

lda TILE_NUMBER_LOOKUP_LO,x
sta ZEROPAGE_POINTER_2
lda TILE_NUMBER_LOOKUP_HI,x
sta ZEROPAGE_POINTER_2 + 1

;————————————————————-
; Loop through and draw the tile

ldy #0

@drawloop
lda (ZEROPAGE_POINTER_2),y ; Get the character code
sta (ZEROPAGE_POINTER_1),y ; store it on the screen
tax ; pass to X as an offset
lda ATTRIBUTE_MEM,x ; fetch the color/data attribute
; lda #COLOR_WHITE
sta (ZEROPAGE_POINTER_3),y ; write it to color ram

cpy #15 ; drawn the 15th character? We’re finished
beq @done

tya ; save Y before the increment for our test
iny ; (saves having to inc it in 2 diff places)

; I need to count 0-3 to draw a row of tiles,
and #%00000011 ; but I need to count 0-15 to fetch the tile data
cmp #3 ; both NEED to use indirect Y addressing, and saving/
bne @drawloop ; fetching Y rapidly becomes a tangled nightmare.

; by masking out the last 2 bits in A, we get a number
; that counts 0-3 over and over without stopping the
; data fetch count 0-15. It’s also faster than my other
; options by quite a bit.

clc ; add new line
lda ZEROPAGE_POINTER_1 ; increment destination and color ram by 1 line – 4 chars
adc #40 – 4
sta ZEROPAGE_POINTER_1 ; by ‘backsetting’ our pointers, we don’t need to change Y
lda ZEROPAGE_POINTER_1 + 1 ; when drawing 0-3 characters, and can leave the 0-15
adc #0 ; count intact.
sta ZEROPAGE_POINTER_1 + 1 ; We have to increase them to the next line anyways, so this
; only saves time.
clc
lda ZEROPAGE_POINTER_3
adc #40 – 4
sta ZEROPAGE_POINTER_3
lda ZEROPAGE_POINTER_3 + 1
adc #0
sta ZEROPAGE_POINTER_3 + 1

jmp @drawloop
@done
restoreRegs ; pull registers back off the stack

rts


Return from Subroutine: [DrawTile]

File Name: [CharPadTools.asm]

iny ; inc X and check for end of screen
cpy #10 ; (10 tiles)
bne @loop
; go down one line on the map (64 char)
addPointer ZEROPAGE_POINTER_4, 100
ldy #0
inx
cpx #5
bne @loop
rts


Return from Subroutine: [Main.asm]

jsr CopyToBuffer ; Copy to the backbuffer (Screen2)


Subroutine (JSR): CopyToBuffer

File Name: [screen_routines.asm]

CopyToBuffer

@copy_screen1
ldx #$00
@loop1
lda SCREEN1_MEM,x
sta SCREEN2_MEM,x

lda SCREEN1_MEM + 250,x
sta SCREEN2_MEM + 250,x

lda SCREEN1_MEM + 500,x
sta SCREEN2_MEM + 500,x

lda SCREEN1_MEM + 750,x ; Game screen only goes to 720
sta SCREEN2_MEM + 750,x

inx
cpx #250
bne @loop1

rts


Return from Subroutine: [Main.asm]

loadpointer ZEROPAGE_POINTER_1, CONSOLE_TEXT

lda #0 ; PARAM1 contains X screen coord (column)
sta PARAM1
lda #19 ; PARAM2 contains Y screen coord (row)
sta PARAM2
lda #COLOR_WHITE ; PARAM3 contains the color to use
sta PARAM3
jsr DisplayText ; Then we display the text


Subroutine (JSR): DisplayText

File Name: [screen_routines.asm]

DisplayText

ldx PARAM2
lda SCORE_LINE_OFFSET_TABLE_LO,x
sta ZEROPAGE_POINTER_2
lda SCORE_LINE_OFFSET_TABLE_HI,x
sta ZEROPAGE_POINTER_2 + 1

lda COLOR_LINE_OFFSET_TABLE_LO,x ; Fetch the address for the line in color ram
sta ZEROPAGE_POINTER_3
lda COLOR_LINE_OFFSET_TABLE_HI,x
sta ZEROPAGE_POINTER_3 + 1

; add the X offset to the destination address

lda ZEROPAGE_POINTER_2
clc
adc PARAM1
sta ZEROPAGE_POINTER_2
lda ZEROPAGE_POINTER_2 + 1
adc #0
sta ZEROPAGE_POINTER_2 + 1

; Same for color ram

lda ZEROPAGE_POINTER_3
clc
adc PARAM1
sta ZEROPAGE_POINTER_3
lda ZEROPAGE_POINTER_3 + 1
adc #0
sta ZEROPAGE_POINTER_3 + 1

; Start the write for this line

ldy #0
@inlineLoop
lda (ZEROPAGE_POINTER_1),y
cmp #00
beq @endMarkerReached
cmp #$2F
beq @lineBreak
sta (ZEROPAGE_POINTER_2),y
lda PARAM3
sta (ZEROPAGE_POINTER_3),y
iny
jmp @inLineLoop

@lineBreak
iny
tya
clc
adc ZEROPAGE_POINTER_1
sta ZEROPAGE_POINTER_1
lda #0
adc ZEROPAGE_POINTER_1 + 1
sta ZEROPAGE_POINTER_1 + 1

inc PARAM2
jmp DisplayText

@endMarkerReached
rts


Return from Subroutine: [Main.asm]

jsr DisplaySpriteInfoNow ; Now update it with the debug info


Subroutine : DisplaySpriteInfoNow

File Name: [Main.asm]

DisplaySpriteInfoNow

loadPointer WPARAM1,SCORE_SCREEN

lda SPRITE_POS_X ; Display sprite X and Y coords
ldx #19
ldy #7
jsr DisplayByte


Subroutine: DisplayByte

File Name: [screen_routines.asm]

DisplayByte

sta PARAM4 ; store the byte to display in PARAM4

jsr FetchLineAddress


Subroutine: FetchLineAddress

File Name: [screen_routines.asm]

FetchLineAddress

lda WPARAM1 + 1

jmp detectScreen


Return from Subroutine: [screen_routines.asm]

lda COLOR_LINE_OFFSET_TABLE_LO, x ; fetch line address for color
sta ZEROPAGE_POINTER_3
lda COLOR_LINE_OFFSET_TABLE_HI,x
sta ZEROPAGE_POINTER_3 + 1

lda PARAM4 ; load the byte to be displayed
and #$0F
clc ; mask for the lower half (0-F)
adc #$30 ; add $30 (48) to display character set
; numbers

clc ; clear carry flag
cmp #$3A ; less than the code for A (10)?
bcc @writeDigit ; Go to the next digit

sec
sbc #$39 ; if so we set the character code back to
; display A-F ($01 – $0A)

@writeDigit
iny ; increment the position on the line
sta (ZEROPAGE_POINTER_1), y ; write the character code
lda #COLOR_YELLOW ; set the color to white
sta (ZEROPAGE_POINTER_3), y ; write the color to color ram

dey ; decrement the position on the line
lda PARAM4 ; fetch the byte to DisplayText
and #$F0 ; mask for the top 4 bits (00 – F0) – 11110000
lsr ; shift it right to a value of 0-F
lsr
lsr
lsr
adc #$30 ; from here, it’s the same

clc
cmp #$3A ; check for A-F
bcc @lastDigit
sbc #$39

@lastDigit
sta (ZEROPAGE_POINTER_1),y ; write character and color
lda #COLOR_YELLOW
sta (ZEROPAGE_POINTER_3),y

rts


Return from Subroutine

File Name: [Main.asm]

jsr WaitFrame


Subroutine (JSR): WaitFrame

File Name: [core_routines.asm]

WaitFrame
lda VIC_RASTER_LINE ; fetch the current raster line
cmp #$F8 ; wait here till l lda VIC_RASTER_LINE ; fetch the current raster lineine #$f8
beq WaitFrame

@WaitStep2
lda VIC_RASTER_LINE
cmp #$F8
bne @WaitStep2
rts


Return from Subroutine: [Main.asm]

jsr InitRasterIRQ ; Setup raster interrupt


Subroutine: InitRasterIRQ

File Name: [raster.asm]

InitRasterIRQ
sei ; stop all interrupts
lda PROC_PORT

lda #$7f ; disable cia #1 generating timer irqs
sta INT_CONTROL ; which are used by the system to flash cursor, etc.

lda #$01 ; tell the VIC we want to generate raster irqs
; Note – by directly writing #$01 and not setting bits
; we are also turning off sprite/sprite sprite/background
; and light pen interrupts.. But those are rather shite anyways
; and won’t be missed

sta VIC_INTERRUPT_CONTROL

lda #$10 ; number of the rasterline we want the IRQ to occur at
sta VIC_RASTER_LINE ; we used this for WaitFrame, remember? Reading gives the current
; raster line, writing sets the line for a raster interrupt to occur

; The raster counter goes from 0-312, so we need to set the
; most significant bit (MSB)

; The MSB for setting the raster line is bit 7 of $D011 – this
; is also VIC_SCREEN_CONTROL, that sets the height of the screen,
; if it’s turned on, text or bitmap mode, and other things.
; so we could easily use this ‘short’ method
; lda #$14 ; screen on and at 25 rows..
; sta $D011

; But doing things properly and only setting the bits we want is a
; good practice to get into.
; also we now have the option of turning the screen on when we want
; to – like after everything is set up

lda VIC_SCREEN_CONTROL ; Fetch the VIC_SCREEN_CONTROL
and #%01111111 ; mask the surrounding bits
; ora #%00000000 ; or in the value we want to set the MSB (Most significant bit)
; in this case, it’s cleared
sta VIC_SCREEN_CONTROL
; set the irq vector to point to our routine
lda #IrqTopScreen
sta $0315
; Acknowlege any pending cia timer interrupts
; just to be 100% safe
lda $dc0d
lda $dd0d

cli ; turn interrupts back on
rts


IRQ Routine: IrqTopScreen

File Name: [raster.asm]

IrqTopScreen
sei ; acknowledge VIC irq
lda $D019
sta $D019
; install glitch irq
lda #IrqGlitchCatcher
sta $0315

lda #$BF
sta $D012

;======================================================================= OUR CODE GOES HERE
@start
;lda #COLOR_BLUE
;sta VIC_BORDER_COLOR

lda CURRENT_SCREEN + 1 ; Hi byte of the current screen
cmp #>SCREEN2_MEM ; compare to start of Screen2
beq @screen2

lda #%00000010 ; Set VIC to Screen0, Charset 1
sta VIC_MEMORY_CONTROL
jmp @scroll

@screen2
lda #%00010010 ; Set VIC to Screen1, Charset 1
sta VIC_MEMORY_CONTROL

@scroll
;—————————————————— SCREEN HARDWARE SCROLL (VERT)
lda VIC_SCREEN_CONTROL_Y ; Take the current values
and #%11111000 ; mask out the scroll values
ora SCROLL_COUNT_Y ; or in the scroll count (bits 0-2 – y scroll value)
sta VIC_SCREEN_CONTROL_Y ; save the updated info in the registers

;—————————————————— SCREEN HARDWARE SCROLL (HORIZ)
lda VIC_SCREEN_CONTROL_X ; Take the current values (Set at IrqScoreBoard)
and #%11111000 ; mask out the lower 4 bits (screen cols and scroll)
ora SCROLL_COUNT_X ; Or in the scroll count (bits 0-2 – x scroll value)
sta VIC_SCREEN_CONTROL_X ; Save the updated info

;—————————————————— TIMERS AND SYSTEM UPDATES
; jsr UpdateTimers ; Update our timers and automatic systems
jsr ReadJoystick
jsr JoyButton

;lda #COLOR_VIOLET
;sta VIC_BORDER_COLOR

;——————————————————————————————-
cli
jmp $ea31

rts


Subroutine: IrqGlitchCatcher

File Name: [raster.asm]

IrqGlitchCatcher
sei
; acknowledge VIC irq
lda $D019
sta $D019

; install scroller irq
lda #IrqScoreBoard
sta $0315

; nr of rasterline we want the NEXT irq to occur at
lda #$c7 ; Scoreboard appears 8 raster lines after the glitch catcher
sta $D012
;——————————————————————————————-

lda SCROLL_COUNT_Y
beq @fr_0
cmp #7
beq @fr_7
cmp #1
beq @fr_1
cmp #2
beq @fr_2
cmp #3
beq @fr_3
cmp #4
beq @fr_4
cmp #5
beq @fr_5
cmp #6
beq @fr_6

jmp @start

@fr_1
; nop
@fr_2
nop
nop
@fr_3
nop
nop
nop
@fr_4
nop
@fr_5
nop
nop
@fr_6
nop
nop
nop
nop
nop
@fr_7
nop
@fr_0

@start

lda #%01100100 ; Set VIC to Screen 6, Charset 2
sta VIC_MEMORY_CONTROL
lda #%01010111 ; Set Y to scroll 7 to force badline to every frame
sta VIC_SCREEN_CONTROL_Y ; set extended background mode
lda #%11010000
sta VIC_SCREEN_CONTROL_X ; X scroll to 0 / multicolor on / 38 cols
; If you set multicolor AND extended background
; you get an illegal mode that sets everything to
; black
lda #COLOR_BLACK
sta VIC_BACKGROUND_COLOR
;sta VIC_BORDER_COLOR
;——————————————————————————————-
cli
jmp $ea31


Subroutine: IrqScoreBoard

File Name: [raster.asm]

IrqScoreBoard
sei ; acknowledge VIC irq
lda $D019
sta $D019

; install scroller irq
lda #IrqTopScreen
sta $0315

; nr of rasterline we want the NEXT irq to occur at
lda #$10
sta $D012
;======================================================================= OUR CODE GOES HERE
; lda #%01100100 ; Set VIC to Screen 6, Charset 2
; sta VIC_MEMORY_CONTROL

;lda #COLOR_GREEN
;sta VIC_BORDER_COLOR

lda #%00010000 ; Restore to Y Scroll = 0
sta VIC_SCREEN_CONTROL_Y ; Be aware that :
; bit #0-2 = vertical scroll
; bit #3 = screen height (0 = 24 rows)
; bit #4 = screen on/off
; bit #5 = text/bitmap (0 = text)
; bit #6 = extended background on/off
; bit #7 = read/write current raster line bit #8
; So ‘3’ is the default vert scroll location
lda #%11010000
sta VIC_SCREEN_CONTROL_X ; Set screen to default
; bit #3 controls screen width (0 = 38 cols)
; bit #4 controls multicolor (0 = off)

;——————————————————————————————-
cli
jmp $ea31

rts


Return from Subroutine: [Main.asm]

jsr WaitFrame

lda #%00011011 ; Default (Y scroll = 3 by default)
;
sta VIC_SCREEN_CONTROL
lda #COLOR_BLACK
sta VIC_BORDER_COLOR


C64 Sprite Setup

lda #0
sta VIC_SPRITE_ENABLE ; Turn all sprites off
sta VIC_SPRITE_X_EXTEND ; clear all extended X bits
sta SPRITE_POS_X_EXTEND ; in registers and data

;———————————————– SETUP AND DISPLAY PLAYER
jsr PlayerSetup


Subroutine (JSR): PlayerSetup

File Name: [Player.asm]

;——————————————————————————
; PLAYER has a strange setup as it’s ALWAYS going to be using sprites 0 and 1
; As well as always being ‘active’ (used)
;——————————————————————————

PlayerSetup

lda #%00000011 ; Turn on multicolor for sprites 0 and 1
sta VIC_SPRITE_MULTICOLOR ; also turn all others to single color

lda #COLOR_LTRED
sta VIC_SPRITE_MULTICOLOR_1 ; Set sprite shared multicolor 1 to brown
lda #COLOR_BROWN
sta VIC_SPRITE_MULTICOLOR_2 ; set sprite shared multicolor 2 to ‘pink’

lda #COLOR_LTGREEN
sta VIC_SPRITE_COLOR ; set sprite 0 color to yellow
lda #COLOR_GREEN
sta VIC_SPRITE_COLOR + 1 ; set sprite 1 color to orange (bkground sprite)

;——————————————————————————
; We now use a system that tracks the sprite position in character coords on
; the screen, so to avoid costly calculations every frame, we set the sprite
; to a character border intially and track all movement from there. That way
; we need only do this set of calculations once in the lifetime of the Player.
;
; To initially place the sprite, we use ‘SpriteToCharPos’
;——————————————————————————

lda #19
sta PARAM1 ; Char X pos = 19
lda #11
sta PARAM2 ; Char Y pos = 10

ldx #0 ; Sprite number 0
jsr SpriteToCharPos ; Set sprite and store coords


Subroutine: SpriteToCharPos

File Name: [sprite_routines.asm]

SpriteToCharPos
lda BIT_TABLE,x ; Lookup the bit for this sprite number (0-7)
eor #$ff ; flip all bits (invert the byte %0001 would become %1110)
and SPRITE_POS_X_EXTEND ; mask out the X extend bit for this sprite
sta SPRITE_POS_X_EXTEND ; store the result back – we’ve erased just this sprites bit
sta VIC_SPRITE_X_EXTEND ; store this in the VIC register for extended X bits

lda PARAM1 ; load the X pos in character coords (the column)
sta SPRITE_CHAR_POS_X,x ; store it in the character X position variable
cmp #30 ; if X is less than 30, no need set the extended bit
bcc @noExtendedX

lda BIT_TABLE,x ; look up the the bit for this sprite number
ora SPRITE_POS_X_EXTEND ; OR in the X extend values – we have set the correct bit
sta SPRITE_POS_X_EXTEND ; Store the results back in the X extend variable
sta VIC_SPRITE_X_EXTEND ; and the VIC X extend register

@noExtendedX
; Setup our Y register so we transfer X/Y values to the
; correct VIC register for this sprite
txa ; first, transfer the sprite number to A
asl ; multiply it by 2 (shift left)
tay ; then store it in Y
; (note : see how VIC sprite pos registers are ordered
; to understand why I’m doing this)

lda PARAM1 ; load in the X Char position
asl ; 3 x shift left = multiplication by 8
asl
asl
clc
adc #24 – SPRITE_DELTA_OFFSET_X ; add the edge of screen (24) minus the delta offset
; to the rough center 8 pixels (1 char) of the sprite

sta SPRITE_POS_X,x ; save in the correct sprite pos x variable
sta VIC_SPRITE_X_POS,y ; save in the correct VIC sprite pos register

lda PARAM2 ; load in the y char position (rows)
sta SPRITE_CHAR_POS_Y,x ; store it in the character y pos for this sprite
asl ; 3 x shift left = multiplication by 8
asl
asl
clc
adc #50 – SPRITE_DELTA_OFFSET_Y ; add top edge of screen (50) minus the delta offset
sta SPRITE_POS_Y,x ; store in the correct sprite pos y variable
sta VIC_SPRITE_Y_POS,y ; and the correct VIC sprite pos register

lda #0
sta SPRITE_POS_X_DELTA,x ;set both x and y delta values to 0 – we are aligned
sta SPRITE_POS_Y_DELTA,x ;on a character border (for the purposes of collisions)
rts


Return from Subroutine [Player.asm]

ldx #1 ; Sprite number 1
jsr SpriteToCharPos ; Set sprite and store coords

;—————————————————————————
; Set sprite images. The sprites from the MLP Spelunker demo used 2 sprites
; overlapped so they could use an extra color. So our main player sprite
; uses 2 sprites (0 and 1). The first walking frame image 1, and it’s
; background sprite is image 8. We use the SetSpriteImage subroutine as it
; will update the pointers for both Screen1 and Screen2 for us.
;—————————————————————————

lda #PLAYER_STATE_IDLE ; Set initial state (idle)
jsr ChangePlayerState

; Player states are incremented by 2 as they are indexes to look up the address of the state

; code on the PLAYER_STATE_JUMPTABLE. An address is 2 bytes (1 word) egro the index must increase ; by 2 bytes. ;—————————————————————————————————

PLAYER_STATE_IDLE = 0 ; standing still – awaiting input

PLAYER_STATE_WALK_R = 2 ; Walking right

PLAYER_STATE_WALK_L = 4 ; Walking left

PLAYER_STATE_FALL = 6 ; Falling

PLAYER_STATE_STAIRS_R = 8 ; stairs on the right

PLAYER_STATE_STAIRS_L = 10 ; stairs on the left

PLAYER_STATE_ROPE = 12 ; climb rope

PLAYER_SUBSTATE_ENTER = 0 ; we have just entered this state

PLAYER_SUBSTATE_RUNNING = 1 ; This state is running normally

PLAYER_STATE_JUMPTABLE
word PlayerStateIdle
word PlayerStateWalkR
word PlayerStateWalkL
word PlayerStateFall
word PlayerStateStairsR
word PlayerStateStairsL
word PlayerStateRope


Subroutine (JSR): ChangePlayerState

File Name: [Player.asm]

Using variables PLAYER_STATE_IDLE, PLAYER_STATE_WALK_R, PLAYER_STATE_WALK_L, PLAYER_STATE_FALL, PLAYER_STATE_STAIRS_R, PLAYER_STATE_STAIRS_L or PLAYER_STATE_ROPE will execute a subroutine found in the table PLAYER_STATE_JUMPTABLE. This is used to provide a simple state based on an action the player is doing such as walking right, walking left, falling, climbing stairs to the right, climbing stairs to the left, or standing before a rope.

ChangePlayerState
tax ; transfer A to X
stx PLAYER_STATE ; store the new player state
lda #PLAYER_SUBSTATE_ENTER ; Set substate to ENTER
sta PLAYER_SUBSTATE

lda #1
sta SPRITE_ANIM_PLAY

lda PLAYER_STATE_JUMPTABLE,x ; lookup state to change to
sta ZEROPAGE_POINTER_1 ; and store it in ZEROPAGE_POINTER_1

lda PLAYER_STATE_JUMPTABLE + 1,x
sta ZEROPAGE_POINTER_1 + 1

jmp (ZEROPAGE_POINTER_1) ; jump to state (to setup)

; NOTE: This is NOT a jsr.
; The state will act as an extension of
; this routine then return.

rts

; Update the player based on their state
;—————————————————————————————————
UpdatePlayerState
ldx PLAYER_STATE ; Load player state
lda PLAYER_STATE_JUMPTABLE,x ; fetch the state address from the jump table
sta ZEROPAGE_POINTER_1 ; store it in ZEROPAGE_POINTER_1
lda PLAYER_STATE_JUMPTABLE +1,x
sta ZEROPAGE_POINTER_1 + 1
jmp (ZEROPAGE_POINTER_1) ; jump to the right state (note – NOT a jsr)
rts

; The player is standing still and waiting input.
; Possible optimizations we are doublechecking CheckBlockUnder and CheckDown, we can check once
; and store those in a temp variable and look them up if needed.
;—————————————————————————————————

#region “Player State Idle”
PlayerStateIdle
jsr IsPlayerJumping
lda PLAYER_SUBSTATE ; Check for first entry to state
bne @running

ldx #0 ; load sprite number (0) in X
lda #
sta ZEROPAGE_POINTER_1
lda #>ANIM_PLAYER_IDLE
sta ZEROPAGE_POINTER_1 + 1

jsr InitSpriteAnim ; setup the animation for Idle
lda PLAYER_SUBSTATE_RUNNING ; set the substate to Running
sta PLAYER_SUBSTATE
rts ; wait till next frame to start

@running

;———————————————————— JOYSTICK INPUT
lda #0 ; clear the idle variable
sta IDLE_VAR

jsr JoystickReady
beq @input

rts ; not ready for input, we return

@input ; process valid joystick input

jsr CheckBlockUnder ; Check for standing on a special block
;cmp #COLL_ROPE
;beq @horizcheck

jsr ApplyGravity ; Apply gravity and test for ground

@horizCheck
lda JOY_X ; horizontal movement
beq @vertCheck ; check zero – ho horizontal input
bmi @left ; negative = left

;——————————————— JOYSTICK RIGHT
@right
lda #PLAYER_STATE_WALK_R ; go to walk state right
jmp ChangePlayerState

;——————————————— JOYSTICK LEFT
@left
lda #PLAYER_STATE_WALK_L ; go to walk state left
jmp ChangePlayerState
rts

@vertCheck
lda JOY_Y ; check vertical joystick input
beq @end ; zero means no input
bmi @up ; negative means up
bpl @down ; already checked for 0 – so this is positive
rts

;——————————————— JOYSTICK UP
@up
ldx #0
jsr CheckBlockUnder
cmp #COLL_ROPE ; Check for rope under player
bne @end
lda #PLAYER_STATE_ROPE ; change to climb rope state
jmp ChangePlayerState
jsr MovePlayerUp

;——————————————– JOYSTICK DOWN
@down

ldx #0 ; if we are on a rope, can we move down?
jsr CheckBlockUnder ; first check we are on a rope
;cmp #COLL_ROPE
;bne @noRope

jsr CheckMoveDown ; If we are at the end, there will be solid ground under us
beq @goRopeClimb ; No blocking and on rope? We change to climbing

; Otherwise we have more checks.
lda SPRITE_POS_X_DELTA ; if not lined up on the rope we get a false positive
cmp #4 ; for collisions around a ‘rope hole’
beq @end ; If we are lined up, and blocked, its’ solid ground

bcc @deltaLess ; if less than 4 – shift left one
jsr MovePlayerLeft
rts
@deltaLess ; Otherwise shift right one
jsr MovePlayerRight
rts

@goRopeClimb
lda #PLAYER_STATE_ROPE
jmp ChangePlayerState

@noRope
@end
rts

IDLE_VAR
byte $00

PlayerStateWalkR
lda PLAYER_SUBSTATE
bne @running
;——————————————————- SETUP CODE GOES HERE
ldx #0 ; Use sprite number 0
lda #<ANIM_PLAYER_WALK_R ; load animation in ZEROPAGE_POINTER_1
sta ZEROPAGE_POINTER_1
lda #>ANIM_PLAYER_WALK_R
sta ZEROPAGE_POINTER_1 + 1

jsr InitSpriteAnim ; initialize the animation
lda #PLAYER_SUBSTATE_RUNNING ; set substate to RUNNING
sta PLAYER_SUBSTATE
rts ; wait till next frame to start
;—————————————————————————–
@running
jsr JoystickReady
beq @input ; Check creates the ‘fix’ pause for scroll resetting
rts
@input
@gravity

ldx #0 ; Rope Check
;jsr CheckBlockUnder
lda COLLIDER_ATTR
cmp #COLL_ROPE
beq @joyCheck

jsr ApplyGravity ; Apply Gravity – if we are not falling
bne @joyCheck ; check the joystick input

inc PLAYER_FALLCOUNT
lda PLAYER_FALLCOUNT
cmp #10
bcs @falling
rts
; New – 2/1/20

@falling
;lda #PLAYER_STATE_FALL ; Otherwise change to falling state
;jmp ChangePlayerState

@joyCheck
lda #0
sta PLAYER_FALLCOUNT

lda JOY_X
beq @idle ; if JOY_X is 0 we are idling and need to change states
bmi @idle ; if negative we are idling

jsr IsPlayerJumping
jsr MovePlayerRight ; Move player one pixel across – A = move? 0 or 1
beq @doneJoy ; move code clear, we can continue

;lda COLLIDER_ATTR
;cmp #COLL_STAIR
;bne @doneJoy
; Switch to climb stairs here
lda #PLAYER_STATE_STAIRS_R
jmp ChangePlayerState

@doneJoy
rts
@idle
lda #PLAYER_STATE_IDLE
jmp ChangePlayerState

PlayerStateWalkL
lda PLAYER_SUBSTATE
bne @running
;——————————————————- SETUP CODE GOES HERE
ldx #0 ; Use sprite number 0
lda #
sta ZEROPAGE_POINTER_1
lda #>ANIM_PLAYER_WALK_L
sta ZEROPAGE_POINTER_1 + 1

jsr InitSpriteAnim ; initialize the animation
lda #PLAYER_SUBSTATE_RUNNING ; set substate to RUNNING
sta PLAYER_SUBSTATE
rts ; wait till next frame to start
;—————————————————————————–
@running
jsr JoystickReady
beq @input ; check to do the ‘fix’ pause for scroll reset
rts
@input

ldx #0 ; Rope Check
;jsr CheckBlockUnder
lda COLLIDER_ATTR
cmp #COLL_ROPE
beq @joyCheck

jsr ApplyGravity ; we are falling
bne @joyCheck

inc PLAYER_FALLCOUNT ; if we fall > 8 pixels we are truely falling
lda PLAYER_FALLCOUNT
cmp #10 ; Player continues to fall for 10 frames
bcs @falling

rts

@falling
lda JOY_X
cmp #2
beq @facingLeft
cmp #3
beq @facingRight
rts

@facingLeft
; New – 2/1/20
lda #PLAYER_STATE_WALK_L ; PLAYER_STATE_FALL
jmp ChangePlayerState

@facingRight
; New – 2/1/20
lda #PLAYER_STATE_WALK_R ; PLAYER_STATE_FALL
jmp ChangePlayerState

@joyCheck
lda #0
sta PLAYER_FALLCOUNT
lda JOY_X
bpl @idle ; if it’s 0 or greater, it’s back to idle

jsr IsPlayerJumping
jsr MovePlayerLeft ; move the player one pixel left
beq @doneJoy ; if move code is clear, we can continue

;lda COLLIDER_ATTR ; check if our collider was a stair
;cmp #COLL_STAIR
;bne @doneJoy

lda #PLAYER_STATE_STAIRS_L ; if it is, change state to climb stairs
jmp ChangePlayerState

@doneJoy
rts

@idle
lda #PLAYER_STATE_IDLE
jmp ChangePlayerState

PlayerStateFall
lda PLAYER_SUBSTATE ; test for first run
bne @running
;——————————————————- SETUP CODE GOES HERE
ldx #0 ; Use sprite number 0
lda #
sta ZEROPAGE_POINTER_1 ; when falling we don’t want to immediately
lda #>ANIM_PLAYER_FALL ; go to a falling animation
sta ZEROPAGE_POINTER_1 + 1

jsr InitSpriteAnim ; initialize the animation

lda #PLAYER_SUBSTATE_RUNNING ; set substate to RUNNING
sta PLAYER_SUBSTATE
rts ; wait for the next frame to take effect
;—————————————————————————–
@running
;———————————————————- JOYSTICK INPUT
jsr JoystickReady
beq @input ; not ready for input
rts
@input
; process valid joystick input
; for falling there is no input
jsr ApplyGravity
;————————————————————————–
bne @idle
rts
@idle
lda #PLAYER_STATE_IDLE
jmp ChangePlayerState

; Player state for climbing stairs
;—————————————————————————————————
#region “PlayerStateStairsR”
PlayerStateStairsR
lda PLAYER_SUBSTATE ; test for first run
bne @running
;——————————————————- SETUP CODE GOES HERE
; TODO – some check to change to walking right animation
; if it’s currently different

; ldx #0 ; Use sprite number 0
; lda #
; sta ZEROPAGE_POINTER_1
; lda #>ANIM_TEST
; sta ZEROPAGE_POINTER_1 + 1

; jsr InitSpriteAnim ; initialize the animation
lda #PLAYER_SUBSTATE_RUNNING ; set substate to RUNNING
sta PLAYER_SUBSTATE
rts ; state change goes into effect next frame
;—————————————————————————–
@running
;———————————————————- JOYSTICK INPUT
jsr JoystickReady
beq @input ; not ready for input
rts
@input
lda JOY_X
beq @vert_check ; X axis in 0 – check for up
bmi @idle ; if it’s -1 (left) return to idle
; so it has to be 1 (right) – climb the stair
@vert_check
; TO DO : check for an up press

@climb
; check for stair collider
jsr MovePlayerRight ; attempt to move right
lda COLLIDER_ATTR ; we can’t – because of a stair
cmp #COLL_STAIR
beq @go_up ; so go up instead

lda #PLAYER_STATE_WALK_R ; else go back to walking (top of stair)
jmp ChangePlayerState

@go_up
jsr MovePlayerUp
rts
@idle
lda #PLAYER_STATE_IDLE ; return to idle (which will likely go to fall)
jmp ChangePlayerState

; Player state for climbing stairs left
;—————————————————————————————————
PlayerStateStairsL
lda PLAYER_SUBSTATE ; test for first run
bne @running
;——————————————————- SETUP CODE GOES HERE
; ldx #0 ; Use sprite number 0
; lda #<ANIM_TEST ; load animation in ZEROPAGE_POINTER_1
; sta ZEROPAGE_POINTER_1
; lda #>ANIM_TEST
; sta ZEROPAGE_POINTER_1 + 1

; jsr InitSpriteAnim ; initialize the animation
lda #PLAYER_SUBSTATE_RUNNING ; set substate to RUNNING
sta PLAYER_SUBSTATE
;—————————————————————————–
@running
;———————————————————- JOYSTICK INPUT
jsr JoystickReady
beq @input ; not ready for input
rts
;————————————————————————
@input ; process valid joystick input
lda JOY_X
beq @vert_check ; TODO – check for vert joystick (up)
bpl @idle ; if it’s 0 or 1, go to idle

@vert_check

@climb ; check for stair collider
jsr MovePlayerLeft ; attempt to move right
lda COLLIDER_ATTR ; check for stair char to the right
cmp #COLL_STAIR
beq @go_up ; if is, climb it (go up)

lda #PLAYER_STATE_WALK_L ; it’s clear (or something else) so change state
jmp ChangePlayerState ; to walking left

@go_up
jsr MovePlayerUp
jsr MovePlayerUp
rts

@idle
lda #PLAYER_STATE_IDLE ; return to idle state
jmp ChangePlayerState
;————————————————————————

rts

; Climbing a rope up
;—————————————————————————————————
PlayerStateRope
lda PLAYER_SUBSTATE ; test for first run
bne @running
;——————————————————- SETUP CODE GOES HERE
ldx #0 ; Use sprite number 0
lda #<ANIM_CLIMB_ROPE_UP ; load animation in ZEROPAGE_POINTER_1
sta ZEROPAGE_POINTER_1
lda #>ANIM_CLIMB_ROPE_UP
sta ZEROPAGE_POINTER_1 + 1

jsr InitSpriteAnim ; initialize the animation
lda #PLAYER_SUBSTATE_RUNNING ; set substate to RUNNING
sta PLAYER_SUBSTATE
rts ; change takes effect next frame
;—————————————————————————–
@running
;———————————————————- JOYSTICK INPUT
jsr JoystickReady
beq @input ; not ready for input
rts
;————————————————————————–
; Process valid joystick input
@input
lda JOY_X
beq @vertCheck
bmi @left
bpl @right
rts

@right
ldx #0
jsr CheckMoveRight
beq @goRight
rts

@goRight
lda #PLAYER_STATE_WALK_R
jmp ChangePlayerState

@left
ldx #0
jsr CheckMoveLeft
beq @goLeft
rts

@goLeft
lda #PLAYER_STATE_WALK_L
jmp ChangePlayerState

@vertCheck
; lets auto align the player to the rope
; so they can pass through ‘holes’
ldx #0
lda SPRITE_POS_X_DELTA,x
cmp #4 ; they pass through if delta is 4
beq @check
bcc @less ; if less than 4, shift right one pixel

jsr MovePlayerLeft ; not equal, not less, must be more – shift left one
jmp @check
@less
jsr MovePlayerRight

@check
lda JOY_Y
beq @end
bmi @up
bpl @down

rts
@up
lda #1
sta SPRITE_ANIM_PLAY ; play our animation

jsr MovePlayerUp
rts
@down
lda #1
sta SPRITE_ANIM_PLAY
jsr MovePlayerDown
bne @endclimb
rts

@endclimb
lda SPRITE_POS_X_DELTA
cmp #4
beq @stopClimb
rts

@stopClimb
lda #PLAYER_STATE_IDLE
jmp ChangePlayerState

@end
lda #0
sta SPRITE_ANIM_PLAY ; pause our animation
;————————————————————————–
rts

; A blank state template to make adding new states easier
;—————————————————————————————————
#region “Player State Framework”
PlayerState_Framework
lda PLAYER_SUBSTATE ; test for first run
bne @running
;——————————————————- SETUP CODE GOES HERE
ldx #0 ; Use sprite number 0
lda #<ANIM_TEST ; load animation in ZEROPAGE_POINTER_1
sta ZEROPAGE_POINTER_1
lda #>ANIM_TEST
sta ZEROPAGE_POINTER_1 + 1

jsr InitSpriteAnim ; initialize the animation
lda #PLAYER_SUBSTATE_RUNNING ; set substate to RUNNING
sta PLAYER_SUBSTATE
rts ; change takes effect next frame
;—————————————————————————–
@running
;———————————————————- JOYSTICK INPUT
jsr JoystickReady
beq @input ; not ready for input
rts
;————————————————————————–
; Process valid joystick input
@input
; lda JOY_X
; beq @vertCheck
; bmi @left
; bpl @right
; rts
@right
@left

@vertCheck
; lda JOY_Y
; beq @end
; bmi @up
; bpl @down
; rts
@up
@down

@end
;————————————————————————–
rts

; Process valid joystick input
@input
; lda JOY_X
; beq @vertCheck
; bmi @left
; bpl @right
; rts
@right
@left

@vertCheck
; lda JOY_Y
; beq @end
; bmi @up
; bpl @down
; rts
@up
@down

@end
;————————————————————————–
rts

; PLAYER DATA
;–————————————————————————————————-

PLAYER_DATA

PLAYER_STATE ; Current state – walking, standing, dying, climbing
byte 0
PLAYER_SUBSTATE
byte 0 ; Current substate – jumping up, falling down, start dying
PLAYER_DIRECTION_X
byte 0
PLAYER_DIRECTION_Y
byte 0
PLAYER_FALLCOUNT
byte 0 ; Counting frames to actual fall
PLAYER_JUMPUPRIGHT
byte 0
PLAYER_JUMPUPLEFT
byte 0
PLAYER_FALLFLAG
byte 0


Return from Subroutine: [Player.asm]

File Name: [Player.asm]

 lda #0
sta SPRITE_DELTA_TRIM_X

;——————————– Set Player Mob active
lda #1
sta SPRITE_IS_ACTIVE ; Set sprite 0 to active
sta SPRITE_IS_ACTIVE + 1 ; Set sprite 1 to active

lda #0 ; reset the player fallcount
sta PLAYER_FALLCOUNT
rts


Return from Subroutine: [Main.asm]

lda VIC_SPRITE_X_EXTEND ; Set X extended bit for sprite 7
ora #%10000000 ; (mask it into existing values)
sta VIC_SPRITE_X_EXTEND

lda SPRITE_POS_X_EXTEND ; Set X extended bit for sprite 7 in variables
ora #%10000000
sta SPRITE_POS_X_EXTEND

; lda #60 ; Set Y pos for sprite 7 in registers and variables
; sta VIC_SPRITE_Y_POS + 14
; sta SPRITE_POS_Y + 7

lda #COLOR_VIOLET ; Set sprite color for sprite 7
sta VIC_SPRITE_COLOR + 7

lda #SPRITE_BASE + 41 ; Set image. We give it a different image for screen 1
sta SPRITE_7_PTR ; and screen 2 in the pointers, so when the screen flips,
lda #SPRITE_BASE + 42 ; the number changes with zero overhead
sta SPRITE_POINTER_BASE2 + 7

lda #%10000011 ; Turn on sprites 0 1 and 7
sta VIC_SPRITE_ENABLE

#endregion

;===========================================================================================
; MAIN LOOP
;===========================================================================================
; The main loop of the program – timed to the verticle blanking period
;———————————————————————–
MainLoop
jsr WaitFrame ; wait for the vertical blank period

jsr UpdateTimers

Setup a Fast and Slow Timer


Subroutine (JSR): UpdateTimers

File Name: [core_routines.asm]

UpdateTimers
inc TIMER ; increment TIMER by 1
lda TIMER
and #$0F ; check if it’s equal to 16
beq @updateSlowTimer ; if so we update SLOW_TIMER
rts

@updateSlowTimer
inc SLOW_TIMER ; increment slow timer
rts


Return from Subroutine: [Main.asm]

jsr UpdateScroll


Subroutine (JSR): UpdateScroll

File Name: [scrolling.asm]

SCROLL_STOP = 0
SCROLL_RIGHT = 1
SCROLL_LEFT = 2
SCROLL_UP = 3
SCROLL_DOWN = 4

;—————————————————————————————————
; UPDATE SCROLLING
;—————————————————————————————————
; Entry point for all scrolling. Here we work out what direction (if any) we are scrolling in
; and branch off to the directional scroll routines – there we divide the work up along the
; frames and perform the actual scrolling.

Check for Scroll Movement

UpdateScroll
lda SCROLL_DIRECTION ; Check for direction of scroll
bne @start

; We have stopped – but we need to know we are on
; the correct ‘start frames’ so if we scroll again
; (especially in the opposite direction) then everything
; is setup properly – otherwise we could miss copying
; the buffers, updating the back screen, or glitching the
; the color shift. The extra shifts will be barely noticeable

lda SCROLL_MOVING ; If we are stoppped (SCROLL_STOP = 0) then we can return
bne @fix_frame
rts

@fix_frame
@fix_right
cmp #SCROLL_RIGHT ; Check if we’re scrolling right
bne @fix_left
; fix right frame
lda SCROLL_COUNT_X ; get the scroll counter
cmp #4 ; if we’re at frame 4 we stop scrolling
beq @fix_done

;————————————————————————-
; Do our extra pixel scroll adjustment

ldx #0 ; Move player sprite left one pixel
jsr MoveSpriteLeft ; Eventually we’ll scroll all the active sprites


Subroutine (JSR): MoveSpriteLeft

; Moves a sprite left one pixel – using the whole screen (with the X extended bit) ; X = number of hardware sprite to move (07) – Register is left intact ; ; NOTE : to move a sprite multiple pixels, you call this multiple times. One day I might have a crack ; at doing one for multiple pixels, but at this point I don’t think I could do one that would ; justify the extra code and provide a performance boost to make it worthwhile. ;————————————————————————————————— ; Fixed bug in the strange extended bit behavior. Flipping the bit on negative flag sets it on $FF ; but also flips it on every other change from 128 ($80) to 255 ($FF) making it flicker. ; Checking instead for 0 corrects this.

File Name: [sprite_routines.asm]

MoveSpriteLeft
lda SPRITE_POS_X,x ; First check for 0 (NOT negative)
bne @decNoChange ; branch if NOT 0

dec SPRITE_POS_X,x ; Decrement Sprite X position by 1 (to $FF)

lda BIT_TABLE,x ; fetch the bit needed to change for this sprite
eor SPRITE_POS_X_EXTEND ; use it as a mask to flip the correct X extend bit
sta SPRITE_POS_X_EXTEND ; store teh data then save it in the VIC II register
sta VIC_SPRITE_X_EXTEND ; $D011 – Sprite extended X bits (one bit per sprite)
jmp @noChangeInExtendedFlag ; Jump to saving the X position

@decNoChange ; Not zero X so we decrement
dec SPRITE_POS_X,x
@noChangeInExtendedFlag
txa ; copy X to the accumulator (sprite number)
asl ; shift it left (multiply by 2)
tay ; save it in Y (to calculate the register to save to)

lda SPRITE_POS_X,x ; Load our variable saved X position
sta VIC_SPRITE_X_POS,y ; save it in $D000 offset by Y to the correct VIC
; sprite register

; Here we decrement the Sprite delta – we moved
; a pixel so the delta goes down by one
dec SPRITE_POS_X_DELTA,x
bmi @resetDelta ; test for change to negative
rts ; if delta is still > 0 we’re done

@resetDelta
lda #$07 ; if delta falls below 0
sta SPRITE_POS_X_DELTA,x ; reset it to #$07 – one char
dec SPRITE_CHAR_POS_X,x ; delta has reset – so decrement character position
rts


Return from Subroutine: [scrolling.asm]

ldx #1
jsr MoveSpriteLeft

lda SCROLL_MOVING
jmp @start
rts
;———————————————— FIX SCROLL LEFT
@fix_left
cmp #SCROLL_LEFT
bne @fix_up
; Abort if needed
lda SCROLL_FIX_SKIP
bne @fix_done

@check_left
lda SCROLL_COUNT_X
cmp #4
beq @fix_done

;————————————————————————
; Do our extra pixel scroll adjustment

ldx #0
jsr MoveSpriteRight


Subroutine (JSR): MoveSpriteRight

File Name: [sprite_routines.asm]

; Moves a sprite right one pixel and adjusts the extended X bit if needed to carry to all the way
; across the screen.
; X = the number of the hardware sprite to move – this register is left intact
;
; NOTE : to move a sprite multiple pixels, this routine must be called multiple times
;—————————————————————————————————

MoveSpriteRight
inc SPRITE_POS_X,x ; increase Sprite X position by 1
; lda SPRITE_POS_X,x ; load the sprite position
bne @noChangeInExtendedFlag ; if not #$00 then no change in x flag

lda BIT_TABLE,x ; get the correct bit to set for this sprite
eor SPRITE_POS_X_EXTEND ; eor in the extended bit (toggle it on or off)
sta SPRITE_POS_X_EXTEND ; store the new flags
sta VIC_SPRITE_X_EXTEND ; set it in the VIC register

@noChangeInExtendedFlag
txa ; transfer the sprite # to A
asl ; multiply it by 2
tay ; transfer the result to Y

lda SPRITE_POS_X,x ; copy the new position to our variable
sta VIC_SPRITE_X_POS,y ; update the correct X position register in the VIC

; Our X position is now incremented, so delta also
; increases by 1
inc SPRITE_POS_X_DELTA,x
lda SPRITE_POS_X_DELTA,x
and #%0111 ; Mask it to 0-7
; cmp #$08 ; if it’s crossed over to 8, we reset it to 0
beq @reset_delta
rts ; if it hasn’t we’re done
@reset_delta
sta SPRITE_POS_X_DELTA,x ; reset delta to 0 – this means we’ve crossed a
inc SPRITE_CHAR_POS_X,x ; a character boundry, so increase our CHAR position

rts


Return from Subroutine: [scrolling.asm]

ldx #1
jsr MoveSpriteRight

lda SCROLL_MOVING
jmp @start
rts

@fix_up
cmp #SCROLL_UP
bne @fix_down

lda SCROLL_COUNT_Y
cmp #3
beq @fix_done

;———————————————————————–
; Do extra pixel scroll adjustment for up

ldx #0
jsr MoveSpriteDown


Subroutine (JSR): MoveSpriteDown:

File Name: [sprite_routines.asm]

; Much the same
; X = number of hardware sprite to move
;—————————————————————————————————
#region “MoveSpriteDown”

MoveSpriteDown
inc SPRITE_POS_Y,x ; increment the y pos variable for this sprite
; sans comments it looks kinda naked…….
txa
asl
tay

lda SPRITE_POS_Y,x
sta VIC_SPRITE_Y_POS,y

inc SPRITE_POS_Y_DELTA,x
lda SPRITE_POS_Y_DELTA,x
cmp #$08
beq @reset_delta
rts

@reset_delta
lda #$00
sta SPRITE_POS_Y_DELTA,x
inc SPRITE_CHAR_POS_Y,x
rts


Return from Subroutine: [scrolling.asm]

ldx #1
jsr MoveSpriteDown

lda SCROLL_MOVING
jmp @start

@fix_down
cmp #SCROLL_DOWN
bne @fix_done

lda SCROLL_COUNT_Y
cmp #3
beq @fix_done

ldx #0
jsr MoveSpriteUp


Subroutine (JSR): MoveSpriteUp

File Name: [sprite_routines.asm]

; Up and down have no special considerations. They wrap at 255
; X = number of hardware sprite to move
;—————————————————————————————————
#region “MoveSpriteUp”

MoveSpriteUp
dec SPRITE_POS_Y,x ; decrement the sprite position variable

txa ; copy the sprite number to A
asl ; multiply it by 2
tay ; transfer it to Y

lda SPRITE_POS_Y,x ; load the sprite position for this sprite
sta VIC_SPRITE_Y_POS,y ; send it to the correct VIC register – $D001 + y

; Y position has decreased, so our delta decreases
dec SPRITE_POS_Y_DELTA,x
bmi @reset_delta ; test to see if it drops to negative
rts ; if not we’re done
@reset_delta
lda #$07 ; reset the delta to 0
sta SPRITE_POS_Y_DELTA,x
dec SPRITE_CHAR_POS_Y,x ; if delta resets, we’ve crossed a character border
rts


Return from Subroutine: [scrolling.asm]

ldx #1
jsr MoveSpriteUp

lda SCROLL_MOVING
jmp @start

@fix_done
lda #SCROLL_STOP
sta SCROLL_MOVING

@start
;——————————– Simple testing at this point

; lda TIMER ; solid timed scroll
; and #%1 ; slow timer to test the screen

; bne @end

; lda SCROLL_DIRECTION ; Fetch scroll direction

;——————————– A = SCROLL_DIRECTION

cmp #SCROLL_RIGHT ; SCROLL RIGHT
bne @left
jmp ScrollRight
@left
cmp #SCROLL_LEFT ; SCROLL LEFT
bne @down
jmp ScrollLeft

@down cmp #SCROLL_DOWN ; SCROLL DOWN
bne @up
jmp ScrollDown

@up
cmp #SCROLL_UP
bne @end
jmp ScrollUp

@end

rts


Return from Subroutine: [scrolling.asm]

; Scroll one pixel up and take care of the screen/color wrap if needed
;—————————————————————————————————
ScrollUp
inc SCROLL_COUNT_Y
lda SCROLL_COUNT_Y
and #%00000111
sta SCROLL_COUNT_Y
;—————————————- FRAME 4
@frame4
cmp #4
bne @frame5
rts
;—————————————- FRAME 5
@frame5
cmp #5
bne @frame6
jsr CopyVerticalBuffer


Subroutine (JSR): CopyVerticalBuffer

File Name: [scrolling.asm]

; Copy the data needed to the VERTICAL_BUFFER and VERTICAL_COLOR_BUFFER for edge drawing
; of new characters on the ‘jump frame’
;
; Note : this will have to be rewritten to combine vert and horiz scrolling and delta values
;—————————————————————————————————
CopyVerticalBuffer

; VARIABLES:
; PARAM1 = Map X position (for the tile to be read from)
; PARAM2 = Adjusted Map Y position for the tile to be read from
; PARAM3 = Adjusted Map X Delta
; PARAM4 = Adjusted Map Y Delta

lda MAP_X_POS ; load the MAP X position
sta PARAM1
lda MAP_X_DELTA ; load the MAP X Delta (position within tile)
sta PARAM3

lda MAP_Y_POS

; What direction are we scrolling? Up or Down?
; The direction will dictate how we calculate what tile
; we need to read from
ldx SCROLL_DIRECTION
cpx #SCROLL_DOWN
beq @scrollingDown
;——————————————————————————————-
; DIRECTION UP
; Scrolling up. The tile we need is the same if MAP_Y_DELTA
; is > 0. If it’s equal to 0, we need MAP_Y – 1.
; The delta is obviously current delta – 1
@scrollingUp

; A currently holds MAP_Y_POS
sta PARAM2 ; store it in PARAM2

ldx MAP_Y_DELTA
dex
txa
and #%0011
sta PARAM4 ; store adjusted Y delta
cmp #3 ; original delta was 0
bne @fetchTile ; if != 3 tile doesn’t decrease

dec PARAM2 ; decrement MAP_Y (in PARAM2)
jmp @fetchTile

;——————————————————————————————
; DIRECTION DOWN
; Scrolling down, the tile we need is MAP_Y + 4
; and the character line within the tile is MAP_Y_DELTA + 2
; This means we may be reading from tile MAP_Y + 5 if the
; MAP_Y_DELTA >= 2
; So if we add 2 then and %0011 (3) to it, to mask it
; back to a 0-3 value.. if our value > MAP_Y_DELTA we are in the
; same tile. Else we advance to the next tile down
@scrollingDown

clc ; A still contains MAP_POS_Y
adc #4 ; add 4 to get the correct tile
sta PARAM2 ; PARAM 2 contains the the ‘adjusted MAP Y POSITION’

ldx MAP_Y_DELTA ; Fetch Delta Y in X
inx
inx ; increment by 2
txa ; transfer to A
and #%0011 ; Mask to 0-3 value
sta PARAM4 ; Save the adjusted Delta Value

clc
cmp MAP_Y_DELTA ; compare – if carry is set, value is >= than delta (same tile)
bcs @fetchTile ; so we take PARAM2 and continue to fetch the tile

inc PARAM2 ; increment our adjusted MAP_Y to the next tile down
;————————- TO DO insert a bounds check / wrap here

;————————————————————————————–
; Next we have to fetch the correct tile from the map
; OPTIMIZATION NOTE : store the map Y line address so
; we don’t have to look it up every loop iteration

@fetchTile
lda #0
sta buffer_index ; reset to the start of the buffer
; sta tile_counter ; and reset the tile counter for the loop

; Later we need to use the adjusted Y delta value
; to get the correct line in the tile. Each line is
; 4 tiles, so we need to multiply the value by 4
; Once added to the tile address, we can then pull
; values from the correct line
asl PARAM4
asl PARAM4

ldx PARAM2 ; get our adjusted MAP_Y_POS to get the map line
lda MAP_LINE_LOOKUP_LO,x ; and store the address for that line in
sta ZEROPAGE_POINTER_2 ; ZEROPAGE_POINTER_1
lda MAP_LINE_LOOKUP_HI,x
sta ZEROPAGE_POINTER_2 + 1

@tileloop ; We then use the MAP_X pos (PARAM1) to get the tile
ldy PARAM1
lda (ZEROPAGE_POINTER_2),y ; Fetch the tile number in A

tax ; using the tile number we can lookup the address
lda TILE_NUMBER_LOOKUP_LO,x ; of the tile itself and store it in ZEROPAGE_POINTER_1
sta ZEROPAGE_POINTER_1
lda TILE_NUMBER_LOOKUP_HI,x
sta ZEROPAGE_POINTER_1 + 1
; Next we have to adjust the address by delta value
; to get the correct line
clc
lda ZEROPAGE_POINTER_1
adc PARAM4
sta ZEROPAGE_POINTER_1
lda ZEROPAGE_POINTER_1 + 1
adc #0
sta ZEROPAGE_POINTER_1 + 1

;——————————————————————————
; ; From here we loop through the tile and copy
; ; the character data and the color data to the
; ; VERTICAL_BUFFER and VERTICAL_COLOR_BUFFER

; Use the adjusted X delta as our start point
; because we won’t always be on a tile boundry
; NOTE: this is only for the FIRST tile – afterwards
; we will reset it to 0 for a full tile

; ldy #0

@copyloop
ldy PARAM3

ldx buffer_index ; load the buffer index in x
lda (ZEROPAGE_POINTER_1),y ; Copy character from tile data
sta VERTICAL_BUFFER,x ; to buffer + x

tax ; copy tile number to x
lda ATTRIBUTE_MEM,x ; copy attribute for tile x
ldx buffer_index ; reload the buffer_index in x
sta VERTICAL_COLOR_BUFFER,x ; store attribute data in color buffer + x

inc buffer_index ; increment the buffer_index
lda #40 ; check for the end of the buffer
cmp buffer_index
beq @done

; iny ; increment position in tile data
; cpy #4 ; test for edge of tile

inc PARAM3 ; increment X delta (adjusted)
lda #4
cmp PARAM3
; lda PARAM3 ; mask it to 0-3 value
; and #%0011
bne @copyloop ; branch if not 0

lda #0
sta PARAM3
inc PARAM1 ; increment PARAM1 to the next tile
jmp @tileloop
@done
rts


Return from Subroutine: [scrolling.asm]

rts
;—————————————- FRAME 6
@frame6
cmp #6
bne @frame7
jsr ShiftCharsUp


Subroutine (JSR): ShiftCharsUp

File Name: [scrolling.asm]

; Shift the characters for a scroll going up (Characters are moving down)
;—————————————————————————————————
#region “ShiftCharsUp”
ShiftCharsUp
lda CURRENT_SCREEN + 1
cmp #>SCREEN2_MEM
beq @screen2
jmp @copyfm1
@screen2
jmp @copyfm2
rts

@copyfm1
ldx #0
@copyloop1

lda SCREEN1_MEM,x
sta SCREEN2_MEM + 40,x

lda SCREEN1_MEM + 40,x
sta SCREEN2_MEM + 80,x

lda SCREEN1_MEM + 80,x
sta SCREEN2_MEM + 120,x

lda SCREEN1_MEM + 120,x
sta SCREEN2_MEM + 160,x

lda SCREEN1_MEM + 160,x
sta SCREEN2_MEM + 200,x

lda SCREEN1_MEM + 200,x
sta SCREEN2_MEM + 240,x

lda SCREEN1_MEM + 240,x
sta SCREEN2_MEM + 280,x

lda SCREEN1_MEM + 280,x
sta SCREEN2_MEM + 320,x

lda SCREEN1_MEM + 320,x
sta SCREEN2_MEM + 360,x

lda SCREEN1_MEM + 360,x
sta SCREEN2_MEM + 400,x

lda SCREEN1_MEM + 400,x
sta SCREEN2_MEM + 440,x

lda SCREEN1_MEM + 440,x
sta SCREEN2_MEM + 480,x

lda SCREEN1_MEM + 480,x
sta SCREEN2_MEM + 520,x

lda SCREEN1_MEM + 520,x
sta SCREEN2_MEM + 560,x

lda SCREEN1_MEM + 560,x
sta SCREEN2_MEM + 600,x

lda SCREEN1_MEM + 600,x
sta SCREEN2_MEM + 640,x

lda SCREEN1_MEM + 640,x
sta SCREEN2_MEM + 680,x

lda SCREEN1_MEM + 680,x
sta SCREEN2_MEM + 720,x

lda SCREEN1_MEM + 720,x
sta SCREEN2_MEM + 760,x

lda SCREEN1_MEM + 760,x
sta SCREEN2_MEM + 800,x

inx
cpx #40
bne @copyloop1
rts

@copyfm2
ldx #0
@copyloop2

lda SCREEN2_MEM,x
sta SCREEN1_MEM + 40,x

lda SCREEN2_MEM + 40,x
sta SCREEN1_MEM + 80,x

lda SCREEN2_MEM + 80,x
sta SCREEN1_MEM + 120,x

lda SCREEN2_MEM + 120,x
sta SCREEN1_MEM + 160,x

lda SCREEN2_MEM + 160,x
sta SCREEN1_MEM + 200,x

lda SCREEN2_MEM + 200,x
sta SCREEN1_MEM + 240,x

lda SCREEN2_MEM + 240,x
sta SCREEN1_MEM + 280,x

lda SCREEN2_MEM + 280,x
sta SCREEN1_MEM + 320,x

lda SCREEN2_MEM + 320,x
sta SCREEN1_MEM + 360,x

lda SCREEN2_MEM + 360,x
sta SCREEN1_MEM + 400,x

lda SCREEN2_MEM + 400,x
sta SCREEN1_MEM + 440,x

lda SCREEN2_MEM + 440,x
sta SCREEN1_MEM + 480,x

lda SCREEN2_MEM + 480,x
sta SCREEN1_MEM + 520,x

lda SCREEN2_MEM + 520,x
sta SCREEN1_MEM + 560,x

lda SCREEN2_MEM + 560,x
sta SCREEN1_MEM + 600,x

lda SCREEN2_MEM + 600,x
sta SCREEN1_MEM + 640,x

lda SCREEN2_MEM + 640,x
sta SCREEN1_MEM + 680,x

lda SCREEN2_MEM + 680,x
sta SCREEN1_MEM + 720,x

lda SCREEN2_MEM + 720,x
sta SCREEN1_MEM + 760,x

lda SCREEN2_MEM + 760,x
sta SCREEN1_MEM + 800,x

inx
cpx #40
bne @copyloop2
rts


Return from Subroutine: [scrolling.asm]

jsr DrawUpBuffer


Subroutine (JSR): DrawUpBuffer

File Name: [scrolling.asm]

; Draw the characters in the vertical buffer to the top line of the scrolling screen
;—————————————————————————————————
#region “DrawUpBuffer”
DrawUpBuffer

loadPointer ZEROPAGE_POINTER_1, VERTICAL_BUFFER

lda CURRENT_BUFFER + 1
cmp #>SCREEN2_MEM
beq @screen2

lda #<SCREEN1_MEM
sta ZEROPAGE_POINTER_2
lda #>SCREEN1_MEM
sta ZEROPAGE_POINTER_2 + 1

ldy #0
@copyloop1
lda (ZEROPAGE_POINTER_1),y
sta (ZEROPAGE_POINTER_2),y

iny
cpy #40
bne @copyloop1

rts

@screen2

lda #<SCREEN2_MEM
sta ZEROPAGE_POINTER_2
lda #>SCREEN2_MEM
sta ZEROPAGE_POINTER_2 + 1

ldy #0
@copyloop2
lda (ZEROPAGE_POINTER_1),y
sta (ZEROPAGE_POINTER_2),y

iny
cpy #40
bne @copyloop2

rts


Return from Subroutine: [scrolling.asm]

rts
;—————————————- FRAME 7 – PREJUMP
@frame7
cmp #7
bne @frame0
rts
;—————————————- FRAME 0 – JUMP FRAME
@frame0
cmp #0
bne @done

jsr SwapScreens
jsr ColorShiftUp


Subroutine (JSR): ColorShiftUp

File Name: [scrolling.asm]

ColorShiftUp
ldx #0

@copyloop
lda COLOR_MEM + 680,x
sta COLOR_MEM + 720,x

lda COLOR_MEM + 640,x
sta COLOR_MEM + 680,x

lda COLOR_MEM + 600,x
sta COLOR_MEM + 640,x

lda COLOR_MEM + 560,x
sta COLOR_MEM + 600,x

lda COLOR_MEM + 520,x
sta COLOR_MEM + 560,x

lda COLOR_MEM + 480,x
sta COLOR_MEM + 520,x

lda COLOR_MEM + 440,x
sta COLOR_MEM + 480,x

lda COLOR_MEM + 400,x
sta COLOR_MEM + 440,x

lda COLOR_MEM + 360,x
sta COLOR_MEM + 400,x

lda COLOR_MEM + 320,x
sta COLOR_MEM + 360,x

lda COLOR_MEM + 280,x
sta COLOR_MEM + 320,x

lda COLOR_MEM + 240,x
sta COLOR_MEM + 280,x

lda COLOR_MEM + 200,x
sta COLOR_MEM + 240,x

lda COLOR_MEM + 160,x
sta COLOR_MEM + 200,x

lda COLOR_MEM + 120,x
sta COLOR_MEM + 160,x

lda COLOR_MEM + 80,x
sta COLOR_MEM + 120,x

lda COLOR_MEM + 40,x
sta COLOR_MEM + 80,x

lda COLOR_MEM,x
sta COLOR_MEM + 40,x

inx
cpx #40
bne @copyloop

rts


Return from Subroutine: [scrolling.asm]

jsr DrawUpColor


Subroutine (JSR): DrawUpColor

File Name: [scrolling.asm]

; Write the contents of the VERTICAL_COLOR_BUFFER to the top ‘off screen’ line in color ram
;—————————————————————————————————
DrawUpColor

loadPointer ZEROPAGE_POINTER_1, VERTICAL_COLOR_BUFFER

lda #<COLOR_MEM
sta ZEROPAGE_POINTER_2
lda #>COLOR_MEM
sta ZEROPAGE_POINTER_2 + 1

ldy #0
@copyloop
lda (ZEROPAGE_POINTER_1),y
sta (ZEROPAGE_POINTER_2),y

iny
cpy #40
bne @copyloop

rts


Return from Subroutine: [scrolling.asm]

lda MAP_Y_DELTA
sec
sbc #1
and #%0011
sta MAP_Y_DELTA

cmp #3
beq @newtile
rts

@newtile

dec MAP_Y_POS
@done
rts

; Scroll one pixel down and take care of screen/color wrap if needed
;—————————————————————————————————
ScrollDown
dec SCROLL_COUNT_Y ; increment the scroll y value
lda SCROLL_COUNT_Y
and #%00000111 ; Mask it to a 0-7 count
sta SCROLL_COUNT_Y ; store it for the next raster IRQ to update
;———————————————- FRAME 4
@frame4
;———————————————- FRAME 3
@frame3
;———————————————- FRAME 2
@frame2
cmp #2
bne @frame1
jsr CopyVerticalBuffer
rts
;———————————————- FRAME 1
@frame1
cmp #1
bne @frame0

jsr ShiftCharsDown


Subroutine (JSR): ShiftCharsDown

File Name: scrolling.asm]

; Shift the characters for a scroll going down (characters are moving up)
;—————————————————————————————————
ShiftCharsDown
lda CURRENT_SCREEN + 1
cmp #>SCREEN2_MEM
beq @screen2
jmp @copyfm1
@screen2
jmp @copyfm2
rts

@copyFm1
ldx #0
@copyloop1
lda SCREEN1_MEM + 40,x ; Tile 1
sta SCREEN2_MEM,x

lda SCREEN1_MEM + 80,x
sta SCREEN2_MEM + 40,x

lda SCREEN1_MEM + 120,x
sta SCREEN2_MEM + 80,x

lda SCREEN1_MEM + 160,x
sta SCREEN2_MEM + 120,x

lda SCREEN1_MEM + 200,x ; Tile 2
sta SCREEN2_MEM + 160,x

lda SCREEN1_MEM + 240,x
sta SCREEN2_MEM + 200,x

lda SCREEN1_MEM + 280,x
sta SCREEN2_MEM + 240,x

lda SCREEN1_MEM + 320,x
sta SCREEN2_MEM + 280,x

lda SCREEN1_MEM + 360,x ; Tile 3
sta SCREEN2_MEM + 320,x

lda SCREEN1_MEM + 400,x
sta SCREEN2_MEM + 360,x

lda SCREEN1_MEM + 440,x
sta SCREEN2_MEM + 400,x

lda SCREEN1_MEM + 480,x
sta SCREEN2_MEM + 440,x

lda SCREEN1_MEM + 520,x ; Tile 4
sta SCREEN2_MEM + 480,x

lda SCREEN1_MEM + 560,x
sta SCREEN2_MEM + 520,x

lda SCREEN1_MEM + 600,x
sta SCREEN2_MEM + 560,x

lda SCREEN1_MEM + 640,x
sta SCREEN2_MEM + 600,x

lda SCREEN1_MEM + 680,x
sta SCREEN2_MEM + 640,x

lda SCREEN1_MEM + 720,x
sta SCREEN2_MEM + 680,x

lda SCREEN1_MEM + 760,x
sta SCREEN2_MEM + 720,x

lda SCREEN1_MEM + 800,x
sta SCREEN2_MEM + 760,x

inx
cpx #40
bne @copyloop1

rts

@copyFm2
ldx #0
@copyloop2
lda SCREEN2_MEM + 40,x ; Tile 1
sta SCREEN1_MEM,x

lda SCREEN2_MEM + 80,x
sta SCREEN1_MEM + 40,x

lda SCREEN2_MEM + 120,x
sta SCREEN1_MEM + 80,x

lda SCREEN2_MEM + 160,x
sta SCREEN1_MEM + 120,x

lda SCREEN2_MEM + 200,x ; Tile 2
sta SCREEN1_MEM + 160,x

lda SCREEN2_MEM + 240,x
sta SCREEN1_MEM + 200,x

lda SCREEN2_MEM + 280,x
sta SCREEN1_MEM + 240,x

lda SCREEN2_MEM + 320,x
sta SCREEN1_MEM + 280,x

lda SCREEN2_MEM + 360,x ; Tile 3
sta SCREEN1_MEM + 320,x

lda SCREEN2_MEM + 400,x
sta SCREEN1_MEM + 360,x

lda SCREEN2_MEM + 440,x
sta SCREEN1_MEM + 400,x

lda SCREEN2_MEM + 480,x
sta SCREEN1_MEM + 440,x

lda SCREEN2_MEM + 520,x ; Tile 4
sta SCREEN1_MEM + 480,x

lda SCREEN2_MEM + 560,x
sta SCREEN1_MEM + 520,x

lda SCREEN2_MEM + 600,x
sta SCREEN1_MEM + 560,x

lda SCREEN2_MEM + 640,x
sta SCREEN1_MEM + 600,x

lda SCREEN2_MEM + 680,x ; Tile 5
sta SCREEN1_MEM + 640,x

lda SCREEN2_MEM + 720,x
sta SCREEN1_MEM + 680,x

lda SCREEN2_MEM + 760,x
sta SCREEN1_MEM + 720,x

lda SCREEN2_MEM + 800,x
sta SCREEN1_MEM + 760,x

inx
cpx #40
bne @copyloop2

rts


Return from Subroutine: [scrolling.asm]

jsr DrawDownBuffer


Subroutine (JSR): DrawDownBuffer

File Name: [scrolling.asm]

; Draw the characters in the vertical buffer to the bottom line of the scrolling screen
;—————————————————————————————————

BOTTOM_DRAW_LINE = 17

#region “DrawDownBuffer”
DrawDownBuffer

loadPointer ZEROPAGE_POINTER_1, VERTICAL_BUFFER

ldx #BOTTOM_DRAW_LINE ; line to draw to

lda CURRENT_BUFFER + 1
cmp #>SCREEN2_MEM
beq @screen2

; TO DO – UNWRAP IF REQUIRED

lda SCREEN1_LINE_OFFSET_TABLE_LO,x
sta ZEROPAGE_POINTER_2
lda SCREEN1_LINE_OFFSET_TABLE_HI,x
sta ZEROPAGE_POINTER_2 + 1

ldy #0
@copyloop
lda (ZEROPAGE_POINTER_1),y
sta (ZEROPAGE_POINTER_2),y

iny
cpy #40
bne @copyloop

rts

@screen2
lda SCREEN2_LINE_OFFSET_TABLE_LO,x
sta ZEROPAGE_POINTER_2
lda SCREEN2_LINE_OFFSET_TABLE_HI,x
sta ZEROPAGE_POINTER_2 + 1

ldy #0
@copyloop2
lda (ZEROPAGE_POINTER_1),y
sta (ZEROPAGE_POINTER_2),y

iny
cpy #40
bne @copyloop2

rts


Return from Subroutine: [scrolling.asm]

rts
;———————————————- FRAME 0
@frame0
cmp #0
bne @frame7

rts
;———————————————- FRAME 7 – JUMP FRAME
@frame7
cmp #7
bne @done
jsr SwapScreens ; Swap Back / Front screens
jsr ColorShiftDown ; Shift color down one character


Subroutine (JSR): ColorShiftDown

File Name: [scrolling.asm]

ColorShiftDown
ldx #0
@copyloop
lda COLOR_MEM + 40,x ; Tile 1
sta COLOR_MEM,x

lda COLOR_MEM + 80,x
sta COLOR_MEM + 40,x

lda COLOR_MEM + 120,x
sta COLOR_MEM + 80,x

lda COLOR_MEM + 160,x
sta COLOR_MEM + 120,x

lda COLOR_MEM + 200,x ; Tile 2
sta COLOR_MEM + 160,x

lda COLOR_MEM + 240,x
sta COLOR_MEM + 200,x

lda COLOR_MEM + 280,x
sta COLOR_MEM + 240,x

lda COLOR_MEM + 320,x
sta COLOR_MEM + 280,x

lda COLOR_MEM + 360,x ; Tile 3
sta COLOR_MEM + 320,x

lda COLOR_MEM + 400,x
sta COLOR_MEM + 360,x

lda COLOR_MEM + 440,x
sta COLOR_MEM + 400,x

lda COLOR_MEM + 480,x
sta COLOR_MEM + 440,x

lda COLOR_MEM + 520,x ; Tile 4
sta COLOR_MEM + 480,x

lda COLOR_MEM + 560,x
sta COLOR_MEM + 520,x

lda COLOR_MEM + 600,x
sta COLOR_MEM + 560,x

lda COLOR_MEM + 640,x
sta COLOR_MEM + 600,x

lda COLOR_MEM + 680,x ; Tile 5
sta COLOR_MEM + 640,x

lda COLOR_MEM + 720,x
sta COLOR_MEM + 680,x

; lda COLOR_MEM + 760,x
; sta COLOR_MEM + 720,x

; lda COLOR_MEM + 800,x
; sta COLOR_MEM + 760,x

inx
cpx #40
bne @copyloop

rts


Return from Subroutine: [scrolling.asm]

jsr DrawDownColor ; Draw new colors in bottom line


Subroutine (JSR): DrawDownColor

File Name: [scrolling.asm]

; Write the contents of the VERTICAL_COLOR_BUFFER to the bottom ‘off screen’ line in color ram
;—————————————————————————————————
DrawDownColor

ldx #BOTTOM_DRAW_LINE

loadPointer ZEROPAGE_POINTER_1, VERTICAL_COLOR_BUFFER

lda COLOR_LINE_OFFSET_TABLE_LO,x
sta ZEROPAGE_POINTER_2
lda COLOR_LINE_OFFSET_TABLE_HI,x
sta ZEROPAGE_POINTER_2 + 1

ldy #0

@copyloop
lda (ZEROPAGE_POINTER_1),y
sta (ZEROPAGE_POINTER_2),y

iny
cpy #40
bne @copyloop

rts


Return from Subroutine: [scrolling.asm]

lda MAP_Y_DELTA ; increment the MAP_Y_DELTA
clc
adc #1
and #%0011 ; Mask to a value between 0-3
sta MAP_Y_DELTA

cmp #0 ; check for crossover to a new tile
beq @newtile
rts
@newtile
inc MAP_Y_POS ; increment MAP Y POS on a new tile

; TODO – increment MAP_ADDRESS to next map line

@done
rts

; Scroll one pixel left and take care of screen/color wrap if needed
;—————————————————————————————————
ScrollLeft
inc SCROLL_COUNT_X ; increment the scroll x value
lda SCROLL_COUNT_X
and #%00000111 ; Mask it to a 0-7 count
sta SCROLL_COUNT_X ; store it for the raster IRQ to update
;———————————————– FRAME 4
@frame4
;———————————————– FRAME 5
@frame5
cmp #5
bne @frame6
; jsr CopyHorizontalBuffer ; copy new characters from the map to the buffers
jsr CopyLeftBuffer


Subroutine (JSR): CopyLeftBuffer

File Name: [scrolling.asm]

; Split from CopyHorizontal Buffer due to a bug that I just can’t seem to track down.
; Hopefully a rewrite of the routine will yeild a cleaner way of doing things
; Copy Horizontal Buffer was a much cleaner routine, so we’ll look at that as well
;
; Actually it turned out so well that I decided to rewrite the original CopyHorizontalBuffer
; routine, as this version will be much easier to integrate handling MAP_Y_DELTA when I
; include vertical scrolling
;—————————————————————————————————
#region “CopyLeftBuffer”
CopyLeftBuffer

; VARIABLES
; PARAM1 = Adjusted Map X Position
; PARAM2 = Adjusted Map Y Position
; PARAM3 = Adjusted Map X Delta
; PARAM4 = Adjusted Map Y Delta

;——————————————————————————
; First we need to know what map tile we are using – moving left that is
; MAP_X_POS or MAP_X_POS – 1 depending on the MAP_X_DELTA.
; On a delta value of 1 – 3 we will be on the same tile.
; On a delta value of 0 we will need one tile over.

lda MAP_Y_POS ; store map Y pos, this won’t change
sta PARAM2
lda MAP_Y_DELTA ; store map Y delta
sta PARAM4

lda MAP_X_POS ; store map X pos, now work on the adjusted value
sta PARAM1 ; based on the MAP_X_DELTA

ldx MAP_X_DELTA ; Fetch the delta value
dex ; decrement it by 1
txa ; transfer it to A
and #%0011 ; mask it to a value of 0 – 3
sta PARAM3 ; store this as the adjusted x delta (current – 1)

cmp #3 ; if our new delta is 3, we are on a new tile
bne @fetchtile ; if not, we fetch the current tile

dec PARAM1 ; adjust our Map X position by -1

;——————————————————————————–
; PARAM1 should hold the correct MAP_X_POS we need to look up
; PARAM2 should hold the (unchanged) MAP_Y_POS we need to look up
; PARAM3 should hold the correct MAP_X_DELTA of the tile to look up
; PARAM4 should hold the (unchanged) MAP_Y_DELTA for the tile

; Now we need to get the fetch the tile

@fetchtile

lda #0 ; reset to the start of the buffer
sta buffer_index

@tileloop
ldx PARAM2 ; fetch MAP_Y_POS to lookup the Map line address
lda MAP_LINE_LOOKUP_LO,x
sta ZEROPAGE_POINTER_2 ; Store the map line in ZEROPAGE_POINTER_2
lda MAP_LINE_LOOKUP_HI,x
sta ZEROPAGE_POINTER_2 + 1

; Next use the adjusted MAP_X_POS to get the tile
ldy PARAM1
lda (ZEROPAGE_POINTER_2),y ; Fetch the tile number in A

tax ; Use the tile number to lookup the address of the tile data
lda TILE_NUMBER_LOOKUP_LO,x
sta ZEROPAGE_POINTER_1 ; and store it in ZEROPAGE_POINTER_1
lda TILE_NUMBER_LOOKUP_HI,x
sta ZEROPAGE_POINTER_1 + 1

;——————————————————————————
; We now have the address to the tile in ZEROPAGE_POINTER_1 – so now we have
; to loop through it and copy the data at our delta X value (PARAM3) for all
; 4 lines of the tile into the buffer. We also need the color attribute data
; for that character (as we’ve packed info into the upper half of the color byte)

; Our first block lookup depends on the map Y delta
; so test to see if it’s 0 – which is should unless it’s
; the first lookup

ldy PARAM3 ; load the adjusted X Delta in Y
lda PARAM4 ; load the adjusted Y Delta
beq @copyloop ; if it’s 0, continue

asl ; else multiply it by 4 (tile line)
asl
clc
adc PARAM3 ; add PARAM3 (adjusted X delta)
tay ; transfer it to Y as our new start index

@copyloop
ldx buffer_index ; load the buffer index in X

lda (ZEROPAGE_POINTER_1),y ; fetch the character #
sta HORIZONTAL_BUFFER,x ; store it in the buffer

tax ; save character number in X
lda ATTRIBUTE_MEM,x ; lookup the attribute for that character
ldx buffer_index ; reload the buffer index in X
sta HORIZONTAL_COLOR_BUFFER,x ; so we can save the attribute in the color buffer

iny ; Add 4 to Y to get to the next tile line
iny
iny
iny

inc buffer_index ; increment the buffer index
lda buffer_index ; if the buffer is full, we’re done
cmp #19 ; length of buffer_index
beq @done

inc PARAM4 ; use the delta Y as a counter to the tiles end
lda PARAM4
cmp #4
bne @copyloop
; Setup for the next tile
lda #0
sta PARAM4 ; Set our Delta Y to 0 – because it’s a new tile
inc PARAM2 ; increment our MAP Y position to the next tile line
jmp @tileloop

@done
rts

#endregion
;===================================================================================================
; SCROLL DATA + TABLES
;===================================================================================================

SCROLL_FIX_SKIP ; Check to see if we can ‘skip’ scroll fixing on stop
byte $0
;———————————————————————— EDGE BUFFERS FOR MAP DRAW
HORIZONTAL_BUFFER
byte 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
HORIZONTAL_COLOR_BUFFER
byte 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0

VERTICAL_BUFFER
byte 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
VERTICAL_COLOR_BUFFER
byte 3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3


Return from Subroutine: [scrolling.asm]

rts
;———————————————– FRAME 6
@frame6
cmp #6
bne @frame7
jsr ShiftCharsLeft ; shift the backscreen over by one character


Subroutine (JSR): ShiftCharsLeft

File Name: [scrolling.asm]

; Shift the characters for a scroll in the left direction (characters are moving right)
; routines are unrolled for speed
;—————————————————————————————————
ShiftCharsLeft
lda CURRENT_SCREEN + 1
cmp #>SCREEN2_MEM
beq @screen2
jmp @copyFm1
rts
@screen2
jmp @copyFm2
rts
@copyFm1
ldx #0
@copyloop1
lda SCREEN1_MEM,x
sta SCREEN2_MEM + 1,x

lda SCREEN1_MEM + 40,x
sta SCREEN2_MEM + 41,x

lda SCREEN1_MEM + 80,x
sta SCREEN2_MEM + 81,x

lda SCREEN1_MEM + 120,x
sta SCREEN2_MEM + 121,x

lda SCREEN1_MEM + 160,x
sta SCREEN2_MEM + 161,x

lda SCREEN1_MEM + 200,x
sta SCREEN2_MEM + 201,x

lda SCREEN1_MEM + 240,x
sta SCREEN2_MEM + 241,x

lda SCREEN1_MEM + 280,x
sta SCREEN2_MEM + 281,x

lda SCREEN1_MEM + 320,x
sta SCREEN2_MEM + 321,x

lda SCREEN1_MEM + 360,x
sta SCREEN2_MEM + 361,x

lda SCREEN1_MEM + 400,x
sta SCREEN2_MEM + 401,x

lda SCREEN1_MEM + 440,x
sta SCREEN2_MEM + 441,x

lda SCREEN1_MEM + 480,x
sta SCREEN2_MEM + 481,x

lda SCREEN1_MEM + 520,x
sta SCREEN2_MEM + 521,x

lda SCREEN1_MEM + 560,x
sta SCREEN2_MEM + 561,x

lda SCREEN1_MEM + 600,x
sta SCREEN2_MEM + 601,x

lda SCREEN1_MEM + 640,x
sta SCREEN2_MEM + 641,x

lda SCREEN1_MEM + 680,x
sta SCREEN2_MEM + 681,x

lda SCREEN1_MEM + 720,x
sta SCREEN2_MEM + 721,x

inx
cpx #39
bne @copyloop1
rts

@copyFm2
ldx #0
@copyloop2
lda SCREEN2_MEM,x
sta SCREEN1_MEM + 1,x

lda SCREEN2_MEM + 40,x
sta SCREEN1_MEM + 41,x

lda SCREEN2_MEM + 80,x
sta SCREEN1_MEM + 81,x

lda SCREEN2_MEM + 120,x
sta SCREEN1_MEM + 121,x

lda SCREEN2_MEM + 160,x
sta SCREEN1_MEM + 161,x

lda SCREEN2_MEM + 200,x
sta SCREEN1_MEM + 201,x

lda SCREEN2_MEM + 240,x
sta SCREEN1_MEM + 241,x

lda SCREEN2_MEM + 280,x
sta SCREEN1_MEM + 281,x

lda SCREEN2_MEM + 320,x
sta SCREEN1_MEM + 321,x

lda SCREEN2_MEM + 360,x
sta SCREEN1_MEM + 361,x

lda SCREEN2_MEM + 400,x
sta SCREEN1_MEM + 401,x

lda SCREEN2_MEM + 440,x
sta SCREEN1_MEM + 441,x

lda SCREEN2_MEM + 480,x
sta SCREEN1_MEM + 481,x

lda SCREEN2_MEM + 520,x
sta SCREEN1_MEM + 521,x

lda SCREEN2_MEM + 560,x
sta SCREEN1_MEM + 561,x

lda SCREEN2_MEM + 600,x
sta SCREEN1_MEM + 601,x

lda SCREEN2_MEM + 640,x
sta SCREEN1_MEM + 641,x

lda SCREEN2_MEM + 680,x
sta SCREEN1_MEM + 681,x

lda SCREEN2_MEM + 720,x
sta SCREEN1_MEM + 721,x

inx
cpx #39
bne @copyloop2
rts


Return from Subroutine: [scrolling.asm]

jsr DrawLeftBuffer ; draw the new characters to the screen


Subroutine (JSR): DrawLeftBuffer

File Name: [scrolling.asm]

; Draw the contents of the horizontal buffer to the backbuffer – unrolled for speed
;—————————————————————————————————
#region “DrawLeftBuffer”
DrawLeftBuffer
lda CURRENT_BUFFER + 1
cmp #>SCREEN2_MEM
beq @screen2

lda HORIZONTAL_BUFFER
sta SCREEN1_MEM
lda HORIZONTAL_BUFFER + 1
sta SCREEN1_MEM + 40
lda HORIZONTAL_BUFFER + 2
sta SCREEN1_MEM + 80
lda HORIZONTAL_BUFFER + 3
sta SCREEN1_MEM + 120

lda HORIZONTAL_BUFFER + 4
sta SCREEN1_MEM + 160
lda HORIZONTAL_BUFFER + 5
sta SCREEN1_MEM + 200
lda HORIZONTAL_BUFFER + 6
sta SCREEN1_MEM + 240
lda HORIZONTAL_BUFFER + 7
sta SCREEN1_MEM + 280

lda HORIZONTAL_BUFFER + 8
sta SCREEN1_MEM + 320
lda HORIZONTAL_BUFFER + 9
sta SCREEN1_MEM + 360
lda HORIZONTAL_BUFFER + 10
sta SCREEN1_MEM + 400
lda HORIZONTAL_BUFFER + 11
sta SCREEN1_MEM + 440

lda HORIZONTAL_BUFFER + 12
sta SCREEN1_MEM + 480
lda HORIZONTAL_BUFFER + 13
sta SCREEN1_MEM + 520
lda HORIZONTAL_BUFFER + 14
sta SCREEN1_MEM + 560
lda HORIZONTAL_BUFFER + 15
sta SCREEN1_MEM + 600

lda HORIZONTAL_BUFFER + 16
sta SCREEN1_MEM + 640
lda HORIZONTAL_BUFFER + 17
sta SCREEN1_MEM + 680
lda HORIZONTAL_BUFFER + 18
sta SCREEN1_MEM + 720
lda HORIZONTAL_BUFFER + 19
sta SCREEN1_MEM + 760

rts
@screen2
lda HORIZONTAL_BUFFER
sta SCREEN2_MEM
lda HORIZONTAL_BUFFER + 1
sta SCREEN2_MEM + 40
lda HORIZONTAL_BUFFER + 2
sta SCREEN2_MEM + 80
lda HORIZONTAL_BUFFER + 3
sta SCREEN2_MEM + 120

lda HORIZONTAL_BUFFER + 4
sta SCREEN2_MEM + 160
lda HORIZONTAL_BUFFER + 5
sta SCREEN2_MEM + 200
lda HORIZONTAL_BUFFER + 6
sta SCREEN2_MEM + 240
lda HORIZONTAL_BUFFER + 7
sta SCREEN2_MEM + 280

lda HORIZONTAL_BUFFER + 8
sta SCREEN2_MEM + 320
lda HORIZONTAL_BUFFER + 9
sta SCREEN2_MEM + 360
lda HORIZONTAL_BUFFER + 10
sta SCREEN2_MEM + 400
lda HORIZONTAL_BUFFER + 11
sta SCREEN2_MEM + 440

lda HORIZONTAL_BUFFER + 12
sta SCREEN2_MEM + 480
lda HORIZONTAL_BUFFER + 13
sta SCREEN2_MEM + 520
lda HORIZONTAL_BUFFER + 14
sta SCREEN2_MEM + 560
lda HORIZONTAL_BUFFER + 15
sta SCREEN2_MEM + 600

lda HORIZONTAL_BUFFER + 16
sta SCREEN2_MEM + 640
lda HORIZONTAL_BUFFER + 17
sta SCREEN2_MEM + 680
lda HORIZONTAL_BUFFER + 18
sta SCREEN2_MEM + 720
lda HORIZONTAL_BUFFER + 19
sta SCREEN2_MEM + 760

rts


Return from Subroutine: [scrolling.asm]

rts
;———————————————– FRAME 7
@frame7
cmp #7
bne @frame0

rts
;———————————————– FRAME 0 – JUMP FRAME
@frame0
cmp #0
bne @done
jsr SwapScreens ; Swap the buffer screen and displayed screen
jsr ColorShiftLeft ; Shift color ram one character to the left


Subroutine (JSR): ColorShiftLeft

File Name: [scrolling.asm]

ColorShiftLeft
ldx #38
@copyloop
lda COLOR_MEM,x
sta COLOR_MEM + 1,x

lda COLOR_MEM + 40,x
sta COLOR_MEM + 41,x

lda COLOR_MEM + 80,x
sta COLOR_MEM + 81,x

lda COLOR_MEM + 120,x
sta COLOR_MEM + 121,x

lda COLOR_MEM + 160,x
sta COLOR_MEM + 161,x

lda COLOR_MEM + 200,x
sta COLOR_MEM + 201,x

lda COLOR_MEM + 240,x
sta COLOR_MEM + 241,x

lda COLOR_MEM + 280,x
sta COLOR_MEM + 281,x

lda COLOR_MEM + 320,x
sta COLOR_MEM + 321,x

lda COLOR_MEM + 360,x
sta COLOR_MEM + 361,x

lda COLOR_MEM + 400,x
sta COLOR_MEM + 401,x

lda COLOR_MEM + 440,x
sta COLOR_MEM + 441,x

lda COLOR_MEM + 480,x
sta COLOR_MEM + 481,x

lda COLOR_MEM + 520,x
sta COLOR_MEM + 521,x

lda COLOR_MEM + 560,x
sta COLOR_MEM + 561,X

lda COLOR_MEM + 600,x
sta COLOR_MEM + 601,X

lda COLOR_MEM + 640,x
sta COLOR_MEM + 641,x

lda COLOR_MEM + 680,x
sta COLOR_MEM + 681,x

lda COLOR_MEM + 720,x
sta COLOR_MEM + 721,x

dex
bpl @copyloop
rts


Return from Subroutine: [scrolling.asm]

jsr DrawLeftColor ; Draw in the leftmost column in color ram


Subroutine (JSR): DrawLeftColor

File Name: [scrolling.asm]

DrawLeftColor
lda HORIZONTAL_COLOR_BUFFER
sta COLOR_MEM
lda HORIZONTAL_COLOR_BUFFER + 1
sta COLOR_MEM + 40
lda HORIZONTAL_COLOR_BUFFER + 2
sta COLOR_MEM + 80
lda HORIZONTAL_COLOR_BUFFER + 3
sta COLOR_MEM + 120

lda HORIZONTAL_COLOR_BUFFER + 4
sta COLOR_MEM + 160
lda HORIZONTAL_COLOR_BUFFER + 5
sta COLOR_MEM + 200
lda HORIZONTAL_COLOR_BUFFER + 6
sta COLOR_MEM + 240
lda HORIZONTAL_COLOR_BUFFER + 7
sta COLOR_MEM + 280

lda HORIZONTAL_COLOR_BUFFER + 8
sta COLOR_MEM + 320
lda HORIZONTAL_COLOR_BUFFER + 9
sta COLOR_MEM + 360
lda HORIZONTAL_COLOR_BUFFER + 10
sta COLOR_MEM + 400
lda HORIZONTAL_COLOR_BUFFER + 11
sta COLOR_MEM + 440

lda HORIZONTAL_COLOR_BUFFER + 12
sta COLOR_MEM + 480
lda HORIZONTAL_COLOR_BUFFER + 13
sta COLOR_MEM + 520
lda HORIZONTAL_COLOR_BUFFER + 14
sta COLOR_MEM + 560
lda HORIZONTAL_COLOR_BUFFER + 15
sta COLOR_MEM + 600

lda HORIZONTAL_COLOR_BUFFER + 16
sta COLOR_MEM + 640
lda HORIZONTAL_COLOR_BUFFER + 17
sta COLOR_MEM + 680
lda HORIZONTAL_COLOR_BUFFER + 18
sta COLOR_MEM + 720
lda HORIZONTAL_COLOR_BUFFER + 19
sta COLOR_MEM + 760

rts


Return from Subroutine: [scrolling.asm]

sec
lda MAP_X_DELTA ; decrement map X delta by 3 (character within tile)
sbc #1
and #%00000011 ; mask it’s value to 0-7
sta MAP_X_DELTA ; save the new delta
cmp #3 ; if the delta is 3, we hit a new tile
beq @new_tile
rts
@new_tile
dec MAP_X_POS ; so decrement the map X position
sec
lda MAP_ADDRESS ; subtract 1 from Map address
sbc #1 ; (address of the top – right corner of the map)
sta MAP_ADDRESS
lda MAP_ADDRESS + 1
sbc #0
sta MAP_ADDRESS + 1

@done
rts

; Scroll one pixel right and take care of screen/color wrap if needed
; Tasks are broken down and performed on each ‘frame’
;—————————————————————————————————
ScrollRight
dec SCROLL_COUNT_X ; decrement to Scroll Right
lda SCROLL_COUNT_X ; load into A
and #%00000111 ; Mask lower 3 bits to make a 0 – 7 count
sta SCROLL_COUNT_X ; store the new count – Raster IRQ does the ScrollRight

; A holds the count, from here we can test against it
; to split the workload depending on what ‘frame’ we are at

;—————————————————- FRAME 2
@frame2
cmp #2
bne @frame1
jsr CopyHorizontalBuffer ; fetch the next column to draw


Subroutine (JSR): CopyHorizontalBuffer

File Name: [scrolling.asm]

; Copy the data needed to the HORIZONTAL_BUFFER and HORIZONTAL_COLOR_BUFFER for edge drawing
; of new chars on the ‘jump frame’
;
; V2 of this routine – rewritten and split from CopyHorizontalBuffer to stay consistant with the
; new CopyLeftBuffer routine
;—————————————————————————————————
#region “CopyHorizBuffer”
CopyHorizontalBuffer
CopyRightBuffer

; VARIABLES
; PARAM1 = Adjusted Map X Position
; PARAM2 = Adjusted Map Y Position
; PARAM3 = Adjusted Map X Delta
; PARAM4 = Adjusted Map Y Delta

;——————————————————————————-
; First we need to know what tile to fetch. Scrolling right this will be
; MAP_POS_X + 10 (the map is 10 tiles wide)

lda MAP_Y_POS ; Setup the variables we don’t need to adjust
sta PARAM2
lda MAP_X_DELTA
sta PARAM3
lda MAP_Y_DELTA
sta PARAM4

lda MAP_X_POS ; add 10 to MAP_X_POS and store in PARAM1
clc
adc #10
sta PARAM1

;——————————————————————————–
; Variables are setup – now we need to fetch the tile to read

@fetchtile
lda #0
sta buffer_index ; reset the buffer index

@tileloop
ldx PARAM2 ; fetch adjusted MAP_Y_POSITION
lda MAP_LINE_LOOKUP_LO,x
sta ZEROPAGE_POINTER_2 ; store the map line address in ZEROPAGE_POINTER_2
lda MAP_LINE_LOOKUP_HI,x
sta ZEROPAGE_POINTER_2 + 1

; Next use the adjusted MAP_X_POS to get the tile
ldy PARAM1
lda (ZEROPAGE_POINTER_2),y ; Fetch the tile number in A

tax
lda TILE_NUMBER_LOOKUP_LO,x ; use the tile number to lookup the address of the tile data
sta ZEROPAGE_POINTER_1
lda TILE_NUMBER_LOOKUP_HI,x
sta ZEROPAGE_POINTER_1 + 1

;———————————————————————————
; We now have the address to the tile in ZEROPAGE_POINTER_1, so now we have to
; loop through the tile at our delta X value (PARAM3) for all 4 lines and copy
; the character and color attribute data to their buffers.

; Our first block lookup depends on the map Y delta
; so test to see if it’s 0 – which is should unless it’s
; the first lookup

ldy PARAM3 ; load the adjusted X Delta in Y
lda PARAM4 ; load the adjusted Y Delta
beq @copyloop ; if it’s 0, continue

asl ; else multiply it by 4 (tile line)
asl
clc
adc PARAM3 ; add PARAM3 (adjusted X delta)
tay ; transfer it to Y as our new start index

@copyloop
ldx buffer_index ; load the buffer index
lda (ZEROPAGE_POINTER_1),y ; fetch the character #
sta HORIZONTAL_BUFFER,x ; store it in the horizontal buffer

tax ; save the character number in X
lda ATTRIBUTE_MEM,x ; use it to fetch that characters attribute data
ldx buffer_index ; restore buffer_index to x
sta HORIZONTAL_COLOR_BUFFER,x ; save the attribute data in the color buffer

iny ; Add 4 to Y to get to the next tile line
iny
iny
iny

inc buffer_index ; increment the buffer index
lda buffer_index ; if the buffer is full, we’re done
cmp #19
beq @done

inc PARAM4 ; use delta Y as a counter to the tiles end
lda PARAM4
cmp #4
bne @copyloop
; Setup for the next tile
lda #0
sta PARAM4 ; reset delta Y to 0 (PARAM4)
inc PARAM2 ; increment MAP Y position to the next tile line
jmp @tileloop

@done

rts

buffer_index
byte 0


Return from Subroutine: [scrolling.asm]

rts
;—————————————————- FRAME 1
@frame1
cmp #1
bne @frame0
jsr ShiftCharsRight ; shift characters to the buffer screen


Subroutine (JSR): ShiftCharsRight

File Name: [scrolling.asm]

; Shift the characters for a scroll in the right direction (the characters are actually moving left)
; These routines are unrolled for speed
;—————————————————————————————————
ShiftCharsRight

lda CURRENT_SCREEN + 1 ; Detect our front screen / backscreen
cmp #>SCREEN2_MEM ; check for screen2
beq @screen2
jmp @copyFm1 ; we use jmp because it WILL be > 256 bytes
rts
@screen2
jmp @copyFm2
rts

;——————————— Copy from screen1 to screen2
@copyFm1
ldx #0
@copyloop1
lda SCREEN1_MEM + 1,x
sta SCREEN2_MEM,x

lda SCREEN1_MEM + 41,x
sta SCREEN2_MEM + 40,x

lda SCREEN1_MEM + 81,x
sta SCREEN2_MEM + 80,x

lda SCREEN1_MEM + 121,x
sta SCREEN2_MEM + 120,x

lda SCREEN1_MEM + 161,x
sta SCREEN2_MEM + 160,x

lda SCREEN1_MEM + 201,x
sta SCREEN2_MEM + 200,x

lda SCREEN1_MEM + 241,x
sta SCREEN2_MEM + 240,x

lda SCREEN1_MEM + 281,x
sta SCREEN2_MEM + 280,x

lda SCREEN1_MEM + 321,x
sta SCREEN2_MEM + 320,x

lda SCREEN1_MEM + 361,x
sta SCREEN2_MEM + 360,x

lda SCREEN1_MEM + 401,x
sta SCREEN2_MEM + 400,x

lda SCREEN1_MEM + 441,x
sta SCREEN2_MEM + 440,x

lda SCREEN1_MEM + 481,x
sta SCREEN2_MEM + 480,x

lda SCREEN1_MEM + 521,x
sta SCREEN2_MEM + 520,x

lda SCREEN1_MEM + 561,x
sta SCREEN2_MEM + 560,x

lda SCREEN1_MEM + 601,x
sta SCREEN2_MEM + 600,x

lda SCREEN1_MEM + 641,x
sta SCREEN2_MEM + 640,x

lda SCREEN1_MEM + 681,x
sta SCREEN2_MEM + 680,x

lda SCREEN1_MEM + 721,x
sta SCREEN2_MEM + 720,x

inx
cpx #39
bne @copyloop1
rts

;——————————— Copy from screen2 to screen1
@copyFm2
ldx #0
@copyloop2
lda SCREEN2_MEM + 1,x
sta SCREEN1_MEM,x

lda SCREEN2_MEM + 41,x
sta SCREEN1_MEM + 40,x

lda SCREEN2_MEM + 81,x
sta SCREEN1_MEM + 80,x

lda SCREEN2_MEM + 121,x
sta SCREEN1_MEM + 120,x

lda SCREEN2_MEM + 161,x
sta SCREEN1_MEM + 160,x

lda SCREEN2_MEM + 201,x
sta SCREEN1_MEM + 200,x

lda SCREEN2_MEM + 241,x
sta SCREEN1_MEM + 240,x

lda SCREEN2_MEM + 281,x
sta SCREEN1_MEM + 280,x

lda SCREEN2_MEM + 321,x
sta SCREEN1_MEM + 320,x

lda SCREEN2_MEM + 361,x
sta SCREEN1_MEM + 360,x

lda SCREEN2_MEM + 401,x
sta SCREEN1_MEM + 400,x

lda SCREEN2_MEM + 441,x
sta SCREEN1_MEM + 440,x

lda SCREEN2_MEM + 481,x
sta SCREEN1_MEM + 480,x

lda SCREEN2_MEM + 521,x
sta SCREEN1_MEM + 520,x

lda SCREEN2_MEM + 561,x
sta SCREEN1_MEM + 560,x

lda SCREEN2_MEM + 601,x
sta SCREEN1_MEM + 600,x

lda SCREEN2_MEM + 641,x
sta SCREEN1_MEM + 640,x

lda SCREEN2_MEM + 681,x
sta SCREEN1_MEM + 680,x

lda SCREEN2_MEM + 721,x
sta SCREEN1_MEM + 720,x

inx
cpx #39
bne @copyloop2
rts


Return from Subroutine: [scrolling.asm]

jsr DrawRightBuffer ; draw in the new characters from the buffer


Subroutine (JSR): DrawRightBuffer

File Name: [scrolling.asm]

; Draw the contents of the right buffer to the backbuffer – unrolled for speed
;—————————————————————————————————
#region “DrawRightBuffer”
DrawRightBuffer
lda CURRENT_BUFFER + 1
cmp #>SCREEN2_MEM
beq @screen2

lda HORIZONTAL_BUFFER
sta SCREEN1_MEM + 39
lda HORIZONTAL_BUFFER + 1
sta SCREEN1_MEM + 79
lda HORIZONTAL_BUFFER + 2
sta SCREEN1_MEM + 119
lda HORIZONTAL_BUFFER + 3
sta SCREEN1_MEM + 159

lda HORIZONTAL_BUFFER + 4
sta SCREEN1_MEM + 199
lda HORIZONTAL_BUFFER + 5
sta SCREEN1_MEM + 239
lda HORIZONTAL_BUFFER + 6
sta SCREEN1_MEM + 279
lda HORIZONTAL_BUFFER + 7
sta SCREEN1_MEM + 319

lda HORIZONTAL_BUFFER + 8
sta SCREEN1_MEM + 359
lda HORIZONTAL_BUFFER + 9
sta SCREEN1_MEM + 399
lda HORIZONTAL_BUFFER + 10
sta SCREEN1_MEM + 439
lda HORIZONTAL_BUFFER + 11
sta SCREEN1_MEM + 479

lda HORIZONTAL_BUFFER + 12
sta SCREEN1_MEM + 519
lda HORIZONTAL_BUFFER + 13
sta SCREEN1_MEM + 559
lda HORIZONTAL_BUFFER + 14
sta SCREEN1_MEM + 599
lda HORIZONTAL_BUFFER + 15
sta SCREEN1_MEM + 639

lda HORIZONTAL_BUFFER + 16
sta SCREEN1_MEM + 679
lda HORIZONTAL_BUFFER + 17
sta SCREEN1_MEM + 719
lda HORIZONTAL_BUFFER + 18
sta SCREEN1_MEM + 759
lda HORIZONTAL_BUFFER + 19
sta SCREEN1_MEM + 799

rts
@screen2

lda HORIZONTAL_BUFFER
sta SCREEN2_MEM + 39
lda HORIZONTAL_BUFFER + 1
sta SCREEN2_MEM + 79
lda HORIZONTAL_BUFFER + 2
sta SCREEN2_MEM + 119
lda HORIZONTAL_BUFFER + 3
sta SCREEN2_MEM + 159

lda HORIZONTAL_BUFFER + 4
sta SCREEN2_MEM + 199
lda HORIZONTAL_BUFFER + 5
sta SCREEN2_MEM + 239
lda HORIZONTAL_BUFFER + 6
sta SCREEN2_MEM + 279
lda HORIZONTAL_BUFFER + 7
sta SCREEN2_MEM + 319

lda HORIZONTAL_BUFFER + 8
sta SCREEN2_MEM + 359
lda HORIZONTAL_BUFFER + 9
sta SCREEN2_MEM + 399
lda HORIZONTAL_BUFFER + 10
sta SCREEN2_MEM + 439
lda HORIZONTAL_BUFFER + 11
sta SCREEN2_MEM + 479

lda HORIZONTAL_BUFFER + 12
sta SCREEN2_MEM + 519
lda HORIZONTAL_BUFFER + 13
sta SCREEN2_MEM + 559
lda HORIZONTAL_BUFFER + 14
sta SCREEN2_MEM + 599
lda HORIZONTAL_BUFFER + 15
sta SCREEN2_MEM + 639

lda HORIZONTAL_BUFFER + 16
sta SCREEN2_MEM + 679
lda HORIZONTAL_BUFFER + 17
sta SCREEN2_MEM + 719
lda HORIZONTAL_BUFFER + 18
sta SCREEN2_MEM + 759
lda HORIZONTAL_BUFFER + 19
sta SCREEN2_MEM + 799

rts


Return from Subroutine: [scrolling.asm]

rts
;—————————————————- FRAME 0 – PREJUMP
@frame0
cmp #0
bne @frame7
rts
;—————————————————- FRAME 7 – JUMP FRAME
@frame7
cmp #7
bne @done
jsr SwapScreens ; bring the buffer to the foreground
jsr ColorShiftRight ; shift the color ram one character


Subroutine (JSR): ColorShiftRight

File Name: [scrolling.asm]

; Shift the color for right scrolling by one character
;—————————————————————————————————
ColorShiftRight
ldx #0
@copyloop
lda COLOR_MEM + 1,x
sta COLOR_MEM,x

lda COLOR_MEM + 41,x
sta COLOR_MEM + 40,x

lda COLOR_MEM + 81,x
sta COLOR_MEM + 80,x

lda COLOR_MEM + 121,x
sta COLOR_MEM + 120,x

lda COLOR_MEM + 161,x
sta COLOR_MEM + 160,x

lda COLOR_MEM + 201,x
sta COLOR_MEM + 200,x

lda COLOR_MEM + 241,x
sta COLOR_MEM + 240,x

lda COLOR_MEM + 281,x
sta COLOR_MEM + 280,x

lda COLOR_MEM + 321,x
sta COLOR_MEM + 320,x

lda COLOR_MEM + 361,x
sta COLOR_MEM + 360,x

lda COLOR_MEM + 401,x
sta COLOR_MEM + 400,x

lda COLOR_MEM + 441,x
sta COLOR_MEM + 440,x

lda COLOR_MEM + 481,x
sta COLOR_MEM + 480,x

lda COLOR_MEM + 521,x
sta COLOR_MEM + 520,x

lda COLOR_MEM + 561,x
sta COLOR_MEM + 560,X

lda COLOR_MEM + 601,x
sta COLOR_MEM + 600,X

lda COLOR_MEM + 641,x
sta COLOR_MEM + 640,x

lda COLOR_MEM + 681,x
sta COLOR_MEM + 680,x

lda COLOR_MEM + 721,x
sta COLOR_MEM + 720,x

inx
cpx #39
bne @copyloop
rts


; Shift the color for right scrolling by one character
;—————————————————————————————————
#region “ColorShiftRight”
ColorShiftRight
ldx #0
@copyloop
lda COLOR_MEM + 1,x
sta COLOR_MEM,x

lda COLOR_MEM + 41,x
sta COLOR_MEM + 40,x

lda COLOR_MEM + 81,x
sta COLOR_MEM + 80,x

lda COLOR_MEM + 121,x
sta COLOR_MEM + 120,x

lda COLOR_MEM + 161,x
sta COLOR_MEM + 160,x

lda COLOR_MEM + 201,x
sta COLOR_MEM + 200,x

lda COLOR_MEM + 241,x
sta COLOR_MEM + 240,x

lda COLOR_MEM + 281,x
sta COLOR_MEM + 280,x

lda COLOR_MEM + 321,x
sta COLOR_MEM + 320,x

lda COLOR_MEM + 361,x
sta COLOR_MEM + 360,x

lda COLOR_MEM + 401,x
sta COLOR_MEM + 400,x

lda COLOR_MEM + 441,x
sta COLOR_MEM + 440,x

lda COLOR_MEM + 481,x
sta COLOR_MEM + 480,x

lda COLOR_MEM + 521,x
sta COLOR_MEM + 520,x

lda COLOR_MEM + 561,x
sta COLOR_MEM + 560,X

lda COLOR_MEM + 601,x
sta COLOR_MEM + 600,X

lda COLOR_MEM + 641,x
sta COLOR_MEM + 640,x

lda COLOR_MEM + 681,x
sta COLOR_MEM + 680,x

lda COLOR_MEM + 721,x
sta COLOR_MEM + 720,x

inx
cpx #39
bne @copyloop
rts


Return from Subroutine: [scrolling.asm]

jsr DrawRightColor ; draw in the new colors from the buffer


Subroutine (JSR): DrawRightColor

File Name: [scrolling.asm]

DrawRightColor
lda HORIZONTAL_COLOR_BUFFER
sta COLOR_MEM + 39
lda HORIZONTAL_COLOR_BUFFER + 1
sta COLOR_MEM + 79
lda HORIZONTAL_COLOR_BUFFER + 2
sta COLOR_MEM + 119
lda HORIZONTAL_COLOR_BUFFER + 3
sta COLOR_MEM + 159

lda HORIZONTAL_COLOR_BUFFER + 4
sta COLOR_MEM + 199
lda HORIZONTAL_COLOR_BUFFER + 5
sta COLOR_MEM + 239
lda HORIZONTAL_COLOR_BUFFER + 6
sta COLOR_MEM + 279
lda HORIZONTAL_COLOR_BUFFER + 7
sta COLOR_MEM + 319

lda HORIZONTAL_COLOR_BUFFER + 8
sta COLOR_MEM + 359
lda HORIZONTAL_COLOR_BUFFER + 9
sta COLOR_MEM + 399
lda HORIZONTAL_COLOR_BUFFER + 10
sta COLOR_MEM + 439
lda HORIZONTAL_COLOR_BUFFER + 11
sta COLOR_MEM + 479

lda HORIZONTAL_COLOR_BUFFER + 12
sta COLOR_MEM + 519
lda HORIZONTAL_COLOR_BUFFER + 13
sta COLOR_MEM + 559
lda HORIZONTAL_COLOR_BUFFER + 14
sta COLOR_MEM + 599
lda HORIZONTAL_COLOR_BUFFER + 15
sta COLOR_MEM + 639

lda HORIZONTAL_COLOR_BUFFER + 16
sta COLOR_MEM + 679
lda HORIZONTAL_COLOR_BUFFER + 17
sta COLOR_MEM + 719
lda HORIZONTAL_COLOR_BUFFER + 18
sta COLOR_MEM + 759
lda HORIZONTAL_COLOR_BUFFER + 19
sta COLOR_MEM + 799
rts


Return from Subroutine: [scrolling.asm]

clc
lda MAP_X_DELTA ; Add one to delta
adc #1
and #%00000011 ; if delta wraps we go up a tile
sta MAP_X_DELTA
cmp #0
beq @newTile
rts
@newTile
inc MAP_X_POS ; inc map pos to next tile
clc
lda MAP_ADDRESS ; inc map address to next position
adc #1
sta MAP_ADDRESS
lda MAP_ADDRESS + 1
adc #0
sta MAP_ADDRESS + 1

rts
;————————————————————————-
@done
rts

:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::

jsr UpdatePlayer


Subroutine (JSR): UpdatePlayer

File Name: [Player.asm]

; Only update the player if it’s active

; Update the player. Joystick controls are updated via interrupt so we read the values from JOY_X
; and JOY_Y
;—————————————————————————————————
#region “Update Player”

PLAYER_RIGHT_CAP = $1c ; Sprite movement caps – at this point we don’t
PLAYER_LEFT_CAP = $09 ; Move the sprite, we scroll the screen
PLAYER_UP_CAP = $04
PLAYER_DOWN_CAP = $0F

UpdatePlayer

lda SPRITE_IS_ACTIVE ; check against sprite #0 – is it active?
bne @update
rts
@update
ldx #0
jsr AnimateSprite ; Animate sprite
jsr UpdatePlayerState ; Update player by state
rts


jsr CONSOLE_DISPLAY ; Display simple debug info

lda #COLOR_BLACK ; Restore the border to black – this gives a visual
sta VIC_BORDER_COLOR ; on how much ‘raster time’ you have to work with

jmp MainLoop

incAsm “raster.asm” ; raster interrupts
incAsm “core_routines.asm” ; core framework routines
incAsm “sprite_routines.asm” ; sprite handling
incAsm “collision_routines.asm” ; sprite collision routines
incAsm “screen_routines.asm” ; screen drawing and handling
incAsm “Scrolling.asm” ; Screen scrolling routines
incAsm “CharPadTools.asm” ; Tools for CharPad levels
incAsm “Player.asm”