Frogger from AssemblyBandit

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 :
; ~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 !