ti 84 plus C se : new assembly game called frogger !

Today I noticed a new game for the ti 84 plus C se calculator.

~AssemblyBandit~ which just have released IViewer now release Frogger .

Frogger is a game where you need to pass through the screen to join the upper side of the screen. See frogger old game wikipedia page.

This game is an application. Ok let’s start looking the code :

.org $c000
 
FrogY:
.db 0
FrogX:
.db 0
FrogDir:
.db 0
FrogFrame:
.dw 0
 
FrameCounter:
.db 0
 
TextRow:
.dw 0
TextCol:
.dw 0
 
KeyRepeat: ;stores counter to slow down repeating presses
.dw 0
KeyFlag: ;read to get keypresses
.db 0
OldKey: ;prevents buttons from being held down
.db 0
 
XIndex: ;current x position for doublesize draw
.dw 0
ImageWidth: ;image width
.dw 0
 
DrawRowIndex: ;holds offset in drawrow
.dw 0
DrawRowData: ;160 bytes for double size line
.dw 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
.dw 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
.dw 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
.dw 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
BackgroundData:
.dw 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
.dw 0,0,0,0,0,0,0,0,0,0,0,0,0
 
Lane1Y: ;y location for lane
.db 0
Lane1Timer: ;inc every int, on overflow car X is inc/dec then reset to speed
.db 0
Lane2Y: ;y location for lane
.db 0
Lane2Timer: ;inc every int, on overflow car X is inc/dec then reset to speed
.db 0
Lane3Y: ;y location for lane
.db 0
Lane3Timer: ;inc every int, on overflow car X is inc/dec then reset to speed
.db 0
 
Lane1Count: ;5 max number of vehicles per Lane
.db 0
Lane1Dir: ;defines Lane left/right direction
.db 0
Lane1Speed: ;timer is reset to this value
.db 0
Lane1Car1Sprite: ;pointer to car sprite
.dw 0
Lane1Car2Sprite: ;pointer to car sprite
.dw 0
Lane1Car3Sprite: ;pointer to car sprite
.dw 0
Lane1Car4Sprite: ;pointer to car sprite
.dw 0
Lane1Car5Sprite: ;pointer to car sprite
.dw 0
Lane1Car1X: ;inc/dec on timeout, 0-255, car is only drawn when on screen
.db 0
[...]
.end

Ok this is the ram.asm file.
It contains every variables declarations.
We could see sprites, lifebar, tables, timers…

Then we have ram.inc :

Lane2Car1Sprite = $c111
Lane3Car2Sprite = $c125
Stream1Object1Sprite = $c148
Lane1Car4Sprite = $c105
Stream3Object2Sprite = $c169
Level = $c1c6
Stream1AnimSpeed = $c146
Life3BG = $c25b
RandOld = $c29d
Lane2Car5Sprite = $c119
Stream2Object6Sprite = $c15d
Stream2Object1Sprite = $c158
FrogMoving = $c177
[...]

This file simply statically choose adresses for the vars defined in ram.asm !

Ok in frogger.asm we have equates at the start :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
; ~AssemblyBandit~ http://briandm82.com
 
;Frogger v1.0
;created using Assembly Studio 8x v4.0
 
;Program size: 16,302 bytes
;This code is public and can be used by anyone
;Not fully commented, not easy to follow, slightly rushed to make deadline
;Email any bugs, questions, comments, suggestions to briandm82@briandm82.com
;Next version will feature more graphics and use at least 32k bytes
 
;Macro definitions
#define bcall(xxxx) rst 28h \ .dw xxxx
#define bjump(xxxx) call 50h \ .dw xxxx
 
;Ports
LinkPort      equ $00 ;bit 0=tip, bit 1=ring.
KeypadPort    equ $01
StatusPort    equ $02 ;Batt/Calc Type: bit 0 set if batteries good
IntPort       equ $03 ;Ack/Enable int: bit 0=On, bit 1=HW1, bit 2=HW2, bit 3=low power, bit 4=link
IntStatusPort equ $04 ;use to determine int device/ map memory/ set timer speeds/ check if on pressed
MemoryCPort   equ $05 ;maps ram page to c ($c000-$ffff) ram only, bit 7 set
MemoryAPort   equ $06 ;maps memory bank a ($4000-$7fff) rom bits 0-6 ram if bit 7 is set
MemoryBPort   equ $07 ;maps memory bank b ($8000-$bfff)
LinkAssist    equ $08 ;sets up link assist interrupts
LinkStatus    equ $09 ;read link assist status
LinkInBuffer  equ $0a ;link assist input buffer
LinkOutBuffer equ $0d ;link assist output buffer
MemoryAHPort  equ $0e ;sets bit 7 for rom page in a
MemoryBHPort  equ $0f ;sets bit 7 for rom page in b
 
[...]
 
;Addresses
Stack    equ $ffff
#include "Ram.inc"
 
;Keys
KDown            equ $00
KLeft            equ $01
KRight           equ $02
KUp              equ $03
KF5              equ $04
KF4              equ $05
KF2              equ $06
KF1              equ $07

Then the real code starts :

.org $4000

The flash app header :

;Header (128 bytes)
.db 080h, 00Fh
.db 000h, 000h, 000h, 000h
.db 080h, 012h
.db 001h, 00Fh
.db 080h, 021h
.db 001h
.db 080h, 031h
.db 0A1h
.db 080h, 048h
.db "Frogger",0 ;make sure this is 8 bytes
.db 080h, 081h
.db 001h
.db 080h, 090h
.db 003h, 026h, 009h, 004h
.db 01Eh, 0FFh, 02Bh, 057h
.db 002h, 00Dh, 040h, 0A1h, 06Bh, 099h, 0F6h, 059h, 0BCh, 067h
.db 0F5h, 085h, 09Ch, 009h, 06Ch, 00Fh, 0B4h, 003h, 09Bh, 0C9h
.db 003h, 032h, 02Ch, 0E0h, 003h;, 020h, 0E3h, 02Ch
 
IntJump: ;4040 ;fit the jump right in here
 jp Interrupt
 
.db 0F4h, 02Dh
.db 073h, 0B4h, 027h, 0C4h, 0A0h, 072h, 054h, 0B9h, 0EAh, 07Ch
.db 03Bh, 0AAh, 016h, 0F6h, 077h, 083h, 07Ah, 0EEh, 01Ah, 0D4h
.db 042h, 04Ch, 06Bh, 08Bh, 013h, 01Fh, 0BBh, 093h, 08Bh, 0FCh
.db 019h, 01Ch, 03Ch, 0ECh, 04Dh, 0E5h, 075h
.db 080h, 07Fh
.db 000h, 000h, 000h, 000h
.db 000h, 000h, 000h, 000h
.db 000h, 000h, 000h, 000h
.db 000h, 000h, 000h, 000h
.db 000h, 000h, 000h, 000h

The title screen and wait :

Title:
 call ClearScreen
 call HighRes
 call HideImage
 ld bc,$0d00
 call Pause
 ld hl,BlogStr
 call ShowString
 ld hl,CreditStr
 call ShowString
 call DisplayTitle
 ld hl,StartStr
 call ShowString
 call WriteHighScores
 call ShowImage
 call Wait

The game init (set vars to init values) :

GameInit:
 call SetScreen
 ld bc,$0d00
 call Pause
 ld hl,BackgroundImage
 call DrawScreen
 call CopyLivesBG
 call DrawLives
 call SetUpLevel
 call DFInit
 call UpdateScreen
 call ShowScreen
 ld hl,KeyStart

Later we could find the key press handling :

MainKeyLoop:
 ei
 halt
 di
 ld a,(FrameCounter)
 or a
 jp z,KeyStart
 dec a
 ld (FrameCounter),a
 jp nz,KeyDone
 jp (hl)
KeyStart:
 ld a,(KeyFlag)
 bit KUp,a
 jp z,UpPress ;use calls to read simultaneous presses must save a!
 bit KDown,a
 jp z,DownPress
 bit KLeft,a
 jp z,LeftPress ;use calls to read simultaneous presses must save a!
 bit KRight,a
 jp z,RightPress
 ld hl,KeyStart
KeyDone:
 in a,(IntStatusPort) ;have on button exit immediately out of program
 bit 3,a
 jp z,Exit
 push hl
 call UpdateScreen
 pop hl
 jp MainKeyLoop

The actions when user press something :

UpPress:
 set KUp,a
 ld (KeyFlag),a ;acknowledge key press
 ld a,(FrogY)
 cp 97
 jp c,UpWater
 ld a,30
 ld (FrameCounter),a
 ld (FrogMoving),a
 ld a,(FrogY)
 ld b,8
 sub b
 ld (FrogY),a
 xor a
 ld (FrogDir),a
 ld hl,FrogJumpImage
 ld (FrogFrame),hl
 call DrawFrog
 ld hl,UpFinish
 jp KeyDone
UpFinish:
 ld a,20
 ld (FrameCounter),a
 ld a,(FrogY)
 ld b,8
 sub b
 ld (FrogY),a
 ld hl,FrogSitImage
 ld (FrogFrame),hl
 call DrawFrog
 xor a
 ld (FrogMoving),a
 ld hl,KeyStart
 jp KeyDone
 
UpWater:
 cp 24
 jp nz,NoUpAlign
 ld a,(FrogX)
 sub 16
 and $f8
 add a,20
 cp 20
 jp nc,CheckUpRight
 ld a,20
 jp UpXGood
CheckUpRight
 cp 165
 jp c,UpXGood
 ld a,164
UpXGood:
 ld (FrogX),a
NoUpAlign:
 ld a,30
 ld (FrameCounter),a
 ld (FrogMoving),a
 ld a,(FrogY)
 ld b,12
 sub b
 ld (FrogY),a
 xor a
 ld (FrogDir),a
 ld hl,FrogJumpImage
 ld (FrogFrame),hl
 call DrawFrog
 ld hl,UpWaterFinish
 jp KeyDone
UpWaterFinish:
 ld a,20
 ld (FrameCounter),a
 ld a,(FrogY)
 ld b,12
 sub b
 ld (FrogY),a
 ld hl,FrogSitImage
 ld (FrogFrame),hl
 call DrawFrog
 xor a
 ld (FrogMoving),a
 ld hl,KeyStart
 jp KeyDone
 
[...]

The huge big draw routine :

UpdateScreen: ;moves sprites on screen
 ld hl,(FlyTimer)
 dec hl
 ld (FlyTimer),hl
 ld a,h
 or l
 jp nz,CheckAlligator
 ld a,(FrogMoving)
 or a
 jp z,UpdateFly
 inc hl
 ld (FlyTimer),hl
 jp CheckAlligator
UpdateFly:
 call FlyTimerExpire
CheckAlligator:
 ld hl,(AlligatorTimer)
 dec hl
 ld (AlligatorTimer),hl
 ld a,h
 or l
 jp nz,SkipSmallUpdate
 ld a,(FrogMoving)
 or a
 jp z,UpdateAlligator
 inc hl
 ld (AlligatorTimer),hl
 jp SkipSmallUpdate
UpdateAlligator:
 call AlligatorTimerExpire
SkipSmallUpdate:
 ld a,(Lane1Count) ;get number of vehicles for lane
 or a
 jp z,Lane2Update ;if none, skip
 ld b,a
 ld a,(Lane1Timer)
 inc a
 ld (Lane1Timer),a
 jp nz,Lane2Update ;skip if timer hasn't expired
 ld a,(Lane1Speed)
 ld (Lane1Timer),a ;reset timer with speed
 push bc
 ld hl,Lane1Car1X ;inc/dec each vehicles x location
 ld a,(Lane1Dir)
 cp 3 ;left
 jp z,Lane1DecXLoop
Lane1IncXLoop:
 ld a,(hl)
 inc a
 ld (hl),a
 inc hl
 djnz Lane1IncXLoop
 jp L1XDone
Lane1DecXLoop:
 ld a,(hl)
 dec a
 ld (hl),a
 inc hl
 djnz Lane1DecXLoop
L1XDone:
 pop bc
 ld ix,Lane1Car1X
 ld hl,Lane1Car1Sprite ;draw vehicle if on screen
L1DrawLoop:
 push bc
 push hl
 ld a,(hl)
 inc hl
 ld h,(hl)
 ld l,a
 ld a,(ix)
 cp 177
 jp nc,SkipL1Draw
 ld b,a
 ld a,(Lane1Y)
 ld c,a
 ld a,(Lane1Dir)
 call DrawVehicle
SkipL1Draw:
 pop hl
 inc hl
 inc hl
 inc ix
 pop bc
 djnz L1DrawLoop
 ld a,(CollisionOccured)
 or a
 jp nz, Collision
Lane2Update:
 ld a,(Lane2Count)
 or a
 jp z,Lane3Update
 ld b,a
 ld a,(Lane2Timer)
 inc a
 ld (Lane2Timer),a
 jp nz,Lane3Update ;skip if timer hasn't expired
 ld a,(Lane2Speed)
 ld (Lane2Timer),a ;reset timer with speed
 push bc
 ld hl,Lane2Car1X ;inc/dec each vehicles x location
 ld a,(Lane2Dir)
 cp 3 ;left
 jp z,Lane2DecXLoop
Lane2IncXLoop:
 ld a,(hl)
 inc a
 ld (hl),a
 inc hl
 djnz Lane2IncXLoop
 jp L2XDone
Lane2DecXLoop:
 ld a,(hl)
 dec a
 ld (hl),a
 inc hl
 djnz Lane2DecXLoop
L2XDone:
 pop bc
 ld ix,Lane2Car1X
 ld hl,Lane2Car1Sprite ;draw vehicle if on screen
L2DrawLoop:
 push bc
 push hl
 ld a,(hl)
 inc hl
 ld h,(hl)
 ld l,a
 ld a,(ix)
 cp 177
 jp nc,SkipL2Draw
 ld b,a
 ld a,(Lane2Y)
 ld c,a
 ld a,(Lane2Dir)
 call DrawVehicle
SkipL2Draw:
 pop hl
 inc hl
 inc hl
 inc ix
 pop bc
 djnz L2DrawLoop
 ld a,(CollisionOccured)
 or a
 jp nz, Collision
Lane3Update:
 ld a,(Lane3Count)
 or a
 jp z,Stream1Update
 ld b,a
 ld a,(Lane3Timer)
 inc a
 ld (Lane3Timer),a
 jp nz,Stream1Update ;skip if timer hasn't expired
 ld a,(Lane3Speed)
 ld (Lane3Timer),a ;reset timer with speed
 push bc
 ld hl,Lane3Car1X ;inc/dec each vehicles x location
 ld a,(Lane3Dir)
 cp 3 ;left
 jp z,Lane3DecXLoop
Lane3IncXLoop:
 ld a,(hl)
 inc a
 ld (hl),a
 inc hl
 djnz Lane3IncXLoop
 jp L3XDone
Lane3DecXLoop:
 ld a,(hl)
 dec a
 ld (hl),a
 inc hl
 djnz Lane3DecXLoop
L3XDone:
 pop bc
 ld ix,Lane3Car1X
 ld hl,Lane3Car1Sprite ;draw vehicle if on screen
L3DrawLoop:
 push bc
 push hl
 ld a,(hl)
 inc hl
 ld h,(hl)
 ld l,a
 ld a,(ix)
 cp 177
 jp nc,SkipL3Draw
 ld b,a
 ld a,(Lane3Y)
 ld c,a
 ld a,(Lane3Dir)
 call DrawVehicle
SkipL3Draw:
 pop hl
 inc hl
 inc hl
 inc ix
 pop bc
 djnz L3DrawLoop
 ld a,(CollisionOccured)
 or a
 jp nz, Collision
Stream1Update:
 ld a,(Stream1Count) ;get number of objects for stream
 ld b,a
 ld de,1
 ld hl,(Stream1AnimTimer)
 add hl,de
 ld (Stream1AnimTimer),hl
 jp nc,SkipS1AnimUpdate
 ld a,(FrogMoving)
 or a
 jp z,S1AnimFNM
 ld a,(FrogY)
 cp 84
 jp z,S1AnimFM
 cp 72
 jp z,S1AnimFM
 cp 60
 jp z,S1AnimFM
 jp S1AnimFNM
S1AnimFM: ;frog is moving so dont update now, but set to update after move
 dec hl
 ld (Stream1AnimTimer),hl
 jp SkipS1AnimUpdate 
S1AnimFNM:
 ld hl,(Stream1AnimSpeed)
 ld (Stream1AnimTimer),hl
 ld a,(Stream1Anim)
 inc a
 ld (Stream1Anim),a
 call S1AnimRedraw ;redraw turtles and frog if necessary
SkipS1AnimUpdate:
 ld a,(Stream1Timer)
 inc a
 ld (Stream1Timer),a
 jp nz,Stream2Update ;skip if no timer expiration
 ld a,(FrogMoving)
 or a
 jp z,S1FNM
 ld a,(FrogY)
 cp 84
 jp z,S1FM
 cp 72
 jp z,S1FM
 cp 60
 jp z,S1FM
 jp S1FNM
S1FM: ;frog is moving so dont update
 ld a,(Stream1Timer)
 dec a
 ld (Stream1Timer),a
 jp Stream2Update
S1FNM:
 ld a,(Stream1Speed)
 ld (Stream1Timer),a ;reset timer with speed
 ld a,(Stream1Count) ;get number of objects for stream
 ld b,a
 ld hl,Stream1Object1X ;inc/dec each objects x location
 ld a,(Stream1Dir)
 cp 3 ;left
 jp z,Stream1DecXLoop
Stream1IncXLoop:
 ld a,(hl)
 inc a
 ld (hl),a
 inc hl
 djnz Stream1IncXLoop
 jp S1XDone
Stream1DecXLoop:
 ld a,(hl)
 dec a
 ld (hl),a
 inc hl
 djnz Stream1DecXLoop
S1XDone:
 ld a,(FrogY)
 cp 72
 jp nz,SkipFC1
 call WaterFrog
SkipFC1:
 ld a,(Stream1Count) ;get number of objects for stream
 ld b,a
 ld ix,Stream1Object1X
 ld hl,Stream1Object1Sprite ;draw object if on screen
S1DrawLoop:
 push bc
 push hl
 ld a,(hl)
 ld hl,LogImage
 or a
 jp z,S1Log
 ld hl,TurtleAnim
 ld a,(Stream1Anim)
 and $07
 rla
 ld d,0
 ld e,a
 add hl,de
 ld a,(hl)
 inc hl
 ld h,(hl)
 ld l,a
S1Log:
 ld a,(ix)
 cp 177
 jp nc,SkipS1Draw
 ld b,a
 ld a,(Stream1Y)
 ld c,a
 ld a,(Stream1Dir)
 call DrawFloat
SkipS1Draw:
 pop hl
 inc hl
 inc ix
 pop bc
 djnz S1DrawLoop
 ld a,(FrogY)
 cp 72
 jp nz,Stream2Update
 ld a,(Stream1Dir)
 cp 3 ;left
 ld a,(FrogX)
 jp z,S1FrogDec
 inc a
 jp S1FrogDone
S1FrogDec:
 dec a
S1FrogDone:
 ld hl,FrogSitImage
 ld (FrogFrame),hl
 ld (FrogX),a
 call DFInit
 jp Stream2Update
S1AnimRedraw:
 ld a,(FrogY)
 cp 72
 jp nz,SkipFCAR1
 push bc
 call WaterFrog
 pop bc
SkipFCAR1:
 ld ix,Stream1Object1X
 ld hl,Stream1Object1Sprite ;draw object if on screen
S1ARDrawLoop:
 push bc
 push hl
 ld a,(hl)
 ld hl,LogImage
 or a
 jp z,S1ARLog
 ld hl,TurtleAnim
 ld a,(Stream1Anim)
 and $07
 rla
 ld d,0
 ld e,a
 add hl,de
 ld a,(hl)
 inc hl
 ld h,(hl)
 ld l,a
S1ARLog:
 ld a,(ix)
 cp 177
 jp nc,SkipS1ARDraw
 ld b,a
 ld a,(Stream1Y)
 ld c,a
 ld a,(Stream1Dir)
 call DrawFloat
SkipS1ARDraw:
 pop hl
 inc hl
 inc ix
 pop bc
 djnz S1ARDrawLoop
 ld a,(FrogY)
 cp 72
 ret nz
 jp DFInit
Stream2Update:
 ld a,(Stream2Count) ;get number of objects for stream
 ld b,a
 ld de,1
 ld hl,(Stream2AnimTimer)
 add hl,de
 ld (Stream2AnimTimer),hl
 jp nc,SkipS2AnimUpdate
 ld a,(FrogMoving)
 or a
 jp z,S2AnimFNM
 ld a,(FrogY)
 cp 36
 jp z,S2AnimFM
 cp 48
 jp z,S2AnimFM
 cp 60
 jp z,S2AnimFM
 jp S2AnimFNM
S2AnimFM: ;frog is moving so dont update
 dec hl
 ld (Stream2AnimTimer),hl
 jp SkipS2AnimUpdate 
S2AnimFNM:
 ld hl,(Stream2AnimSpeed)
 ld (Stream2AnimTimer),hl
 ld a,(Stream2Anim)
 inc a
 ld (Stream2Anim),a
 call S2AnimRedraw ;redraw turtles
SkipS2AnimUpdate:
 ld a,(Stream2Timer)
 inc a
 ld (Stream2Timer),a
 jp nz,Stream3Update ;skip if no timer expiration
 ld a,(FrogMoving)
 or a
 jp z,S2FNM
 ld a,(FrogY)
 cp 36
 jp z,S2FM
 cp 48
 jp z,S2FM
 cp 60
 jp z,S2FM
 jp S2FNM
S2FM: ;frog is moving so dont update
 ld a,(Stream2Timer)
 dec a
 ld (Stream2Timer),a
 jp Stream3Update
S2FNM:
 ld a,(Stream2Speed)
 ld (Stream2Timer),a ;reset timer with speed
 ld a,(Stream2Count) ;get number of objects for stream
 ld b,a
 ld hl,Stream2Object1X ;inc/dec each objects x location
 ld a,(Stream2Dir)
 cp 3 ;left
 jp z,Stream2DecXLoop
Stream2IncXLoop:
 ld a,(hl)
 inc a
 ld (hl),a
 inc hl
 djnz Stream2IncXLoop
 jp S2XDone
Stream2DecXLoop:
 ld a,(hl)
 dec a
 ld (hl),a
 inc hl
 djnz Stream2DecXLoop
S2XDone:
 ld a,(FrogY)
 cp 48
 jp nz,SkipFC2
 call WaterFrog
SkipFC2:
 ld a,(Stream2Count) ;get number of objects for stream
 ld b,a
 ld ix,Stream2Object1X
 ld hl,Stream2Object1Sprite ;draw object if on screen
S2DrawLoop:
 push bc
 push hl
 ld a,(hl)
 ld hl,LogImage
 or a
 jp z,S2Log
 ld hl,TurtleAnim
 ld a,(Stream2Anim)
 and $07
 rla
 ld d,0
 ld e,a
 add hl,de
 ld a,(hl)
 inc hl
 ld h,(hl)
 ld l,a
S2Log:
 ld a,(ix)
 cp 177
 jp nc,SkipS2Draw
 ld b,a
 ld a,(Stream2Y)
 ld c,a
 ld a,(Stream2Dir)
 call DrawFloat
SkipS2Draw:
 pop hl
 inc hl
 inc ix
 pop bc
 djnz S2DrawLoop
 ld a,(FrogY)
 cp 48
 jp nz,Stream3Update
 ld a,(Stream2Dir)
 cp 3 ;left
 ld a,(FrogX)
 jp z,S2FrogDec
 inc a
 jp S2FrogDone
S2FrogDec:
 dec a
S2FrogDone:
 ld hl,FrogSitImage
 ld (FrogFrame),hl
 ld (FrogX),a
 call DFInit
 jp Stream3Update
S2AnimRedraw:
 ld a,(FrogY)
 cp 48
 jp nz,SkipFCAR2
 push bc
 call WaterFrog
 pop bc
SkipFCAR2:
 ld ix,Stream2Object1X
 ld hl,Stream2Object1Sprite ;draw object if on screen
S2ARDrawLoop:
 push bc
 push hl
 ld a,(hl)
 ld hl,LogImage
 or a
 jp z,S2ARLog
 ld hl,TurtleAnim
 ld a,(Stream2Anim)
 and $07
 rla
 ld d,0
 ld e,a
 add hl,de
 ld a,(hl)
 inc hl
 ld h,(hl)
 ld l,a
S2ARLog:
 ld a,(ix)
 cp 177
 jp nc,SkipS2ARDraw
 ld b,a
 ld a,(Stream2Y)
 ld c,a
 ld a,(Stream2Dir)
 call DrawFloat
SkipS2ARDraw:
 pop hl
 inc hl
 inc ix
 pop bc
 djnz S2ARDrawLoop
 ld a,(FrogY)
 cp 48
 ret nz
 jp DFInit
Stream3Update:
 ld a,(Stream3Count) ;get number of objects for stream
 ld b,a
 ld de,1
 ld hl,(Stream3AnimTimer)
 add hl,de
 ld (Stream3AnimTimer),hl
 jp nc,SkipS3AnimUpdate
 ld a,(FrogMoving)
 or a
 jp z,S3AnimFNM
 ld a,(FrogY)
 cp 36
 jp z,S3AnimFM
 cp 24
 jp z,S3AnimFM
 cp 12
 jp z,S3AnimFM
 jp S3AnimFNM
S3AnimFM: ;frog is moving so dont update
 dec hl
 ld (Stream3AnimTimer),hl
 jp SkipS3AnimUpdate 
S3AnimFNM:
 ld hl,(Stream3AnimSpeed)
 ld (Stream3AnimTimer),hl
 ld a,(Stream3Anim)
 inc a
 ld (Stream3Anim),a
 call S3AnimRedraw ;redraw turtles
SkipS3AnimUpdate:
 ld a,(Stream3Timer)
 inc a
 ld (Stream3Timer),a
 ret nz ;return if no timer expiration
 ld a,(FrogMoving)
 or a
 jp z,S3FNM
 ld a,(FrogY)
 cp 36
 jp z,S3FM
 cp 24
 jp z,S3FM
 cp 12
 jp z,S3FM
 jp S3FNM
S3FM: ;frog is moving so dont update
 ld a,(Stream3Timer)
 dec a
 ld (Stream3Timer),a
 ret
S3FNM:
 ld a,(Stream3Speed)
 ld (Stream3Timer),a ;reset timer with speed
 ld a,(Stream3Count) ;get number of objects for stream
 ld b,a
 ld hl,Stream3Object1X ;inc/dec each objects x location
 ld a,(Stream3Dir)
 cp 3 ;left
 jp z,Stream3DecXLoop
Stream3IncXLoop:
 ld a,(hl)
 inc a
 ld (hl),a
 inc hl
 djnz Stream3IncXLoop
 jp S3XDone
Stream3DecXLoop:
 ld a,(hl)
 dec a
 ld (hl),a
 inc hl
 djnz Stream3DecXLoop
S3XDone:
 ld a,(FrogY)
 cp 24
 jp nz,SkipFC3
 call WaterFrog
SkipFC3:
 ld a,(Stream3Count) ;get number of objects for stream
 ld b,a
 ld ix,Stream3Object1X
 ld hl,Stream3Object1Sprite ;draw object if on screen
S3DrawLoop:
 push bc
 push hl
 ld a,(hl)
 ld hl,LogImage
 or a
 jp z,S3Log
 ld hl,TurtleAnim
 ld a,(Stream3Anim)
 and $07
 rla
 ld d,0
 ld e,a
 add hl,de
 ld a,(hl)
 inc hl
 ld h,(hl)
 ld l,a
S3Log:
 ld a,(ix)
 cp 177
 jp nc,SkipS3Draw
 ld b,a
 ld a,(Stream3Y)
 ld c,a
 ld a,(Stream3Dir)
 call DrawFloat
SkipS3Draw:
 pop hl
 inc hl
 inc ix
 pop bc
 djnz S3DrawLoop
 ld a,(FrogY)
 cp 24
 ret nz
 ld a,(Stream3Dir)
 cp 3 ;left
 ld a,(FrogX)
 jp z,S3FrogDec
 inc a
 jp S3FrogDone
S3FrogDec:
 dec a
S3FrogDone:
 ld hl,FrogSitImage
 ld (FrogFrame),hl
 ld (FrogX),a
 jp DFInit
S3AnimRedraw:
 ld a,(FrogY)
 cp 24
 jp nz,SkipFCAR3
 push bc
 call WaterFrog
 pop bc
SkipFCAR3:
 ld ix,Stream3Object1X
 ld hl,Stream3Object1Sprite ;draw object if on screen
S3ARDrawLoop:
 push bc
 push hl
 ld a,(hl)
 ld hl,LogImage
 or a
 jp z,S3ARLog
 ld hl,TurtleAnim
 ld a,(Stream3Anim)
 and $07
 rla
 ld d,0
 ld e,a
 add hl,de
 ld a,(hl)
 inc hl
 ld h,(hl)
 ld l,a
S3ARLog:
 ld a,(ix)
 cp 177
 jp nc,SkipS3ARDraw
 ld b,a
 ld a,(Stream3Y)
 ld c,a
 ld a,(Stream3Dir)
 call DrawFloat
SkipS3ARDraw:
 pop hl
 inc hl
 inc ix
 pop bc
 djnz S3ARDrawLoop
 ld a,(FrogY)
 cp 24
 ret nz
 jp DFInit

And associated draw and color routines :

SetScreen: ;sets up 160 mode
 ld a,LCDOutput
 call DisplayReadHL
 set LCDHSM,h ;set interlace
 call DisplayWriteHL
 ld hl,0
 ld a,LCDP1Display
 call DisplayWriteHL ;set display 1
 ld hl,16
 ld de,175
 ld a,LCDP1Start
 call DisplayWriteHLDE ;set start and end 1
 ld hl,160
 ld a,LCDP2Display
 call DisplayWriteHL ;set display 2
 ld hl,16
 ld de,175
 ld a,LCDP2Start
 call DisplayWriteHLDE ;set start and end 2
 ld a,LCDControl
 call DisplayReadHL
 res LCDHBASEE,h
 jp DisplayWriteHL 
 
DrawScreen: ;draws full screen double size h=l image
 push hl
 ld hl,0
 ld (DrawRowIndex),hl ;reset drawrowindex
 call SetFullLowResWindow
 call SetCursor
 xor a ;set ir
 out (LCDInstPort),a
 ld a,LCDGRAM
 out (LCDInstPort),a
 pop hl
 push hl
 pop ix
 inc hl ;first byte points to signal which has been copied
CD:
 ld a,(hl)
 inc hl
 cp (ix) ;if not signal, drawcolor
 jp z,DecompressDS ;if signal wait for other 2 bytes then loop draw
 push hl
 call DrawColor
 pop hl
 jp CD 
DecompressDS: ;gets next two bytes to loop draw
 ld a,(hl)
 or a
 ret z ;finished drawing image
 inc hl
 ld b,a ;save loop count
 ld a,(hl)
 ld c,a
 inc hl
 push hl ;save hl for main loop
DCDSLoop:
 ld a,c
 push bc
 call DrawColor ;draw color b times
 pop bc
 djnz DCDSLoop
 pop hl
 jp CD
 
DrawColor: ;a= byte of data
 ld hl,DrawRowData
 ld de,(DrawRowIndex)
 add hl,de
 ld (hl),a
 inc de
 ld (DrawRowIndex),de
 out (LCDDataPort),a
 out (LCDDataPort),a
 ld a,(DrawRowIndex)
 cp 160
 ret nz
 ld hl,0
 ld (DrawRowIndex),hl ;reset drawrowindex
 ld b,160
 ld hl,DrawRowData
DRLoop:
 ld a,(hl)
 inc hl
 ld e,a
 out (LCDDataPort),a
 out (LCDDataPort),a
 djnz DRLoop
 ret
 
DrawFrog: ;8x8, transparent
;FrogX, FrogY, FrogDir, FrogFrame should be set
 call DrawBackground
DFInit:
 ld h,0 ;set frog window
 ld de,15
 ld a,(FrogY)
 ld l,a
 add a,e
 ld e,a
 ld a,LCDWinYStart
 call DisplayWriteHLDE ;set y
 ld h,0
 ld de,7
 ld a,(FrogX)
 ld l,a
 add a,e
 ld e,a
 ld a,LCDWinXStart
 call DisplayWriteHLDE ;set x
;if x>=104 and <201 check for collision in road
 ld a,(CollisionOccured)
 or a
 jp nz,OutOfRoad ; if collision has occured don't check else infinite loop
 ld a,(FrogY)
 cp 73
 jp nc,CheckRoad
 cp 72
 jp z,CheckWater
 cp 48
 jp z,CheckWater
 cp 24
 jp z,CheckWater
 cp 0
 jp z,CheckWin
 jp OutOfRoad
CheckRoad:
 cp 104
 jp c,OutOfRoad
 cp 201
 jp nc,OutOfRoad
 call CopyBackgroundCheckCollision
 jp DFSC
CheckWater:
 call CopyBackgroundCheckWaterCollision
 jp DFSC
OutOfRoad:
 call CopyBackground ;get erase data for frog
DFSC:
 call SetCursor
 ld a,(FrogDir)
 or a
 jp z,DFVertical
 cp 1
 jp nz,CheckDown
 call SetID01
 call ResetAM
 jp DFHorizontal
CheckDown:
 cp 2
 jp nz,FFaceLeft
 call SetID10
 jp DFVertical
FFaceLeft:
 call SetID10
 call ResetAM
 jp DFHorizontal
DFVertical:
 xor a ;set ir
 out (LCDInstPort),a
 ld a,LCDGRAM
 out (LCDInstPort),a
 ld hl,(FrogFrame)
 ld de,8
 ld b,8
DFLoop:
 ld c,b
 ld b,e
DFRowLoop:
 ld a,(hl) ;draw row
 inc hl
 or a
 jp nz,NotTransparent1
 in a,(LCDDataPort)
 in a,(LCDDataPort) ;dummy reads
 in a,(LCDDataPort)
NotTransparent1:
 out (LCDDataPort),a
 out (LCDDataPort),a
 djnz DFRowLoop
 or a
 sbc hl,de
 ld b,e
DFDupRowLoop:
 ld a,(hl) ;draw dup row
 inc hl
 or a
 jp nz,NotTransparent2
 in a,(LCDDataPort)
 in a,(LCDDataPort) ;dummy reads
 in a,(LCDDataPort)
NotTransparent2:
 out (LCDDataPort),a
 out (LCDDataPort),a
 djnz DFDupRowLoop
 ld b,c
 djnz DFLoop
 jp SetID11
DFHorizontal:
 xor a ;set ir
 out (LCDInstPort),a
 ld a,LCDGRAM
 out (LCDInstPort),a
 ld hl,(FrogFrame)
 ld b,64
DFColLoop:
 ld a,(hl) ;draw col
 inc hl
 or a
 jp nz,NotTransparent3
 in a,(LCDDataPort)
 in a,(LCDDataPort) ;dummy reads
 in a,(LCDDataPort)
NotTransparent3:
 out (LCDDataPort),a
 out (LCDDataPort),a
 out (LCDDataPort),a
 out (LCDDataPort),a
 djnz DFColLoop
 call SetAM
 jp SetID11
 
WaterFrog: ;clears frog over water
 ld h,0 ;set frog window
 ld de,15
 ld a,(FrogY)
 ld l,a
 add a,e
 ld e,a
 ld a,LCDWinYStart
 call DisplayWriteHLDE ;set y
 ld h,0
 ld de,7
 ld a,(FrogX)
 ld l,a
 add a,e
 ld e,a
 ld a,LCDWinXStart
 call DisplayWriteHLDE ;set x
 call SetCursor
 xor a ;set ir
 out (LCDInstPort),a
 ld a,LCDGRAM
 out (LCDInstPort),a
 ld b,128
WFLoop:
 ld a,$1a
 out (LCDDataPort),a
 out (LCDDataPort),a
 djnz WFLoop
 ret
 
DrawSmall: ;8x8 small sprite at a,2
;a=x
;hl points to sprite
 push hl
 ld h,0
 ld de,7
 ld l,a
 add a,e
 ld e,a
 ld a,LCDWinXStart
 call DisplayWriteHLDE ;set x
 ld hl,2 ;set window
 ld de,17
 ld a,LCDWinYStart
 call DisplayWriteHLDE ;set y
 call SetCursor
 xor a ;set ir
 out (LCDInstPort),a
 ld a,LCDGRAM
 out (LCDInstPort),a
 pop hl
 ld de,8
 ld b,8
DSLoop:
 ld c,b
 ld b,e
DSRowLoop:
 ld a,(hl) ;draw row
 inc hl
 out (LCDDataPort),a
 out (LCDDataPort),a
 djnz DSRowLoop
 or a
 sbc hl,de
 ld b,e
DSDupRowLoop:
 ld a,(hl) ;draw dup row
 inc hl
 out (LCDDataPort),a
 out (LCDDataPort),a
 djnz DSDupRowLoop
 ld b,c
 djnz DSLoop
 ret

Then you have timers, live checking, string routines (I don’t put the code tldr :p).
And finally animation data and sprites data. Here’s a sample :

TurtleAnim:
.dw Turtle5Image,Turtle3Image,Turtle2Image,Turtle1Image
.dw Turtle1Image,Turtle2Image,Turtle3Image,Turtle4Image
 
Turtle1Image:
.db $1A, $1A, $1A, $F5, $F5, $2E, $2E, $2E, $2E, $2E, $2E, $1A, $1A, $F5, $F5, $1A
.db $1A, $1A, $1A, $F5, $2E, $2E, $2D, $2E, $2D, $2D, $2E, $2E, $F5, $F5, $F5, $1A
.db $1A, $1A, $1A, $2E, $2E, $2D, $2D, $2E, $2E, $2D, $2E, $2E, $2E, $1A, $1A, $1A
.db $00, $1A, $2E, $2E, $2E, $2D, $2E, $2E, $2E, $2E, $2E, $2D, $2E, $2E, $1A, $1A
.db $F5, $F5, $2E, $2D, $2E, $2E, $2E, $2E, $2D, $2D, $2E, $2D, $2D, $2E, $2E, $1A
.db $F5, $F5, $2E, $2D, $2D, $2E, $2E, $2E, $2D, $2D, $2E, $2D, $2E, $2E, $2E, $1A
.db $00, $1A, $2E, $2E, $2E, $2E, $2D, $2E, $2E, $2E, $2E, $2E, $2E, $2E, $1A, $1A
.db $1A, $1A, $1A, $2E, $2E, $2D, $2D, $2E, $2E, $2D, $2D, $2D, $2E, $1A, $1A, $1A
.db $1A, $1A, $1A, $F5, $2E, $2E, $2D, $2E, $2E, $2D, $2E, $2E, $F5, $F5, $F5, $1A
.db $1A, $1A, $1A, $F5, $F5, $2E, $2E, $2E, $2E, $2E, $2E, $1A, $1A, $F5, $F5, $1A

Ok this is a cool piece of code. I don’t reviewed deeply this code, I will test it on calc asap.
Have fun with it, nice job assembly bandit !

Leave a Reply

© 2021 ti-84-plus.com. All Rights Reserved. Theme WP Castle by Saeed Salam.