;
; If you make any changes to the source, please tell me what and why.
; And you are NOT allowed to distribute a modified source, nor the
; compiled version of it. Any changes should be made for personal use only.
;
; //Jimmy M†rdell <yarin@acc.umu.se>
; //James Vernon <jamesv82@live.com.au> (TI-84+CE port)
;

#include "ti84pce.inc"
#include "get_key.inc"

#define VERSION "1.0.0"
#define HUFF84PCE

#include "sqrxzs.inc"                           ; equates for location of certain sprites inside SQRXZS appvar

freemem         = pixelshadow                   ; start of 69090 bytes of available ram for use

vars            = freemem                       ; 1000 total space allowed for general variables, broken down as below
XScr            = vars+0                        ; 3, X scroller
XBlock          = vars+3                        ; 1, XScr SHR 3 (1 block = 8x8 pixels)
LevelSize       = vars+4                        ; 2, Length of level (x blocks)
x               = vars+6                        ; 3, X location of Sqrxz
y               = vars+9                        ; 1, Y location of Sqrxz
sqflags         = vars+10                       ; 1, bit 1 - HScroll, bit 2 - Sqrxz has moved
                                                ;    bit 3 - Died this frame, bit 4 - Was under portcullis
                                                ;    bit 5 - Update enemies
jump            = vars+11                       ; 1, Where in the jump arc (backwards)
fall            = vars+12                       ; 1, Same as above, except when falling (and not bw)
sqrxzcnt        = vars+13                       ; 1, Sqrxz counter
sqrxzdir        = vars+14                       ; 1, Sqrxz direction (0 = Right, 1 = Left)
jump_button     = vars+15                       ; 1, The jump button status
dead            = vars+16                       ; 1, 0 = alive, >0 = dead
counter         = vars+17                       ; 1, Main counter
animcnt         = vars+18                       ; 1, Animation counter, counts 0-2
hmovelast       = vars+19                       ; 1, 1 = Update Sqrxz
deadcnt         = vars+20                       ; 1, Death pause counter - starts after death
timer           = vars+21                       ; 1, Timer - increased every interrupt call (200 hz)
tmp_x           = vars+22                       ; 3, Temporary storage for Sqrxz X
tmp_y           = vars+25                       ; 1, Temporary storage for Sqrxz Y
start_y         = vars+26                       ; 1, Sqrxz start Y
start_x         = vars+27                       ; 2, Sqrxz start X (0=start of level) (2nd byte is just scrap to protect next variable)
statusCnt       = vars+29                       ; 1, Counter for updating status text at bottom of screen
enemytable      = vars+30                       ; 64, Enemy table [0..7] type,dir,x(l),y,fall,spec

world           = vars+94                       ; 3, Address to selected world
lvlptrs         = vars+97                       ; 3, Pointer to level pointers, alt level info
worldT          = vars+100                      ; 1, 0=uncompressed levels, 1=compressed levels
noLevels        = vars+101                      ; 1, Number of levels on the selected world
lvl             = vars+102                      ; 1, Current level
lives           = vars+103                      ; 1, Lives left
timeleft        = vars+104                      ; 3, Time left
sausages        = vars+107                      ; 1, Sausages taken
gaming          = vars+108                      ; 1, 0=not playing, 1=playing
waterbar        = vars+109                      ; 3, The water bar (0-479)

animptr         = vars+112                      ; 1, Next change in the table (0-7)
AnimTable       = vars+113                      ; 40, Anim table. 8 entries, 5 byte each (x,y,addr)
portc           = vars+153                      ; 5, A portcullis that will go down (x,y,addr)

noWorlds        = vars+158                      ; 1, Number of worlds found on calc
top             = vars+159                      ; 1, Top of the world list
noItems         = vars+160                      ; 1, Number of worlds in the current list
curItem         = vars+161                      ; 1, Current world choice
curOpt          = vars+162                      ; 1, Current option
worlds          = vars+163                      ; 768, Pointers to different worlds
strBuffer       = vars+931                      ; 6, buffer used to display HL value (5 chars. plus NULL terminator)
worldN          = vars+937                      ; 3, pointer to world name
treeaddr        = vars+940                      ; 3, pointer to Huffman tree
pWorld          = vars+943                      ; 3, pointer to selected world
fileWorld       = vars+946                      ; 10, place to store world file data type and name will NULL terminator


LevelPtr        = vars+1000                     ; 2048, after vars
huffLib         = LevelPtr+2048                 ; 1000, space for HuffExtr
sprites         = huffLib+1000                  ; 51200, space for up to 200 16x16 sprites to be loaded
sprChars        = sprites+51200                 ; 13500, space for up to 60 15x15 font sprites to be loaded
pVbuf           = sprChars+13500                ; 3
pVbufWin        = pVbuf+3                       ; 3


vbuf1           = vram
vbuf2           = vram+(320*240)


SAVE_FILE_DATA          = 956+2048
SAVE_FILE_REQD          = SAVE_FILE_DATA+15

OP_OR_A                 = $B7
OP_SCF                  = $37

COL_BLACK               = 0
COL_WHITE               = 1
COL_ORANGE              = 2
COL_LIGHTGREY           = 3
COL_BLUE                = 4
COL_GREEN               = 5
COL_RED                 = 6
COL_PALEBLUE            = 11

COL_TIME_LIGHT          = 6
COL_TIME_DARK           = 7
COL_AIR_LIGHT           = 8
COL_AIR_DARK            = 9

        ;----------------------------------------
        ; program start
        ;----------------------------------------
.org    userMem-2
.db     tExtTok,tAsm84CeCmp
Start:
        call    _clrLCD
        call    _runindicoff                    ; Turn busy indicator off
        call    vramClear
        ; load title palette
        ld      hl,pal_title
        ld      bc,PAL_TITLE_SIZE
        call    loadPalette
        ; prepare lcd
        call    vram8bpp
        ld      hl,vbuf2
        ld      (pVbuf),hl
        ; load font
        ld      hl,fontSmall
        call    loadFont
        ; find title image appvar
        ld      ix,fileTitle
        call    findFile
        jp      c,missingTitleFile
        ; decompress title image
        ld      de,sprites
        ld      ix,huffLib
        call    HuffExtr
        ; and draw it to vbuf
        ld      ix,sprites
        ld      de,66
        ld      l,40
        call    drawImage
        ; draw text
        ld      hl,Coder
        ld      de,96
        ld      c,160
        call    drawString                      ; show coder
        ld      de,68
        ld      c,175
        call    drawString                      ; show porter
        push    hl
        ld      hl,fontArcade
        call    loadFont
        pop     hl
        ld      a,COL_LIGHTGREY
        ld      (__dcColour),a
        ld      de,272
        ld      c,230
        call    drawString                      ; show version
        ld      a,COL_WHITE
        ld      (__dcColour),a
        ; find sprites appvar and setup pointers as required
        ld      ix,fileSprites
        call    findFile
        jp      c,missingSpritesFile
        ld      de,sprites
        push    de
        ld      bc,51200
        ldir
        pop     de
        ld      hl,pal_sprites
        add     hl,de
        ld      (__palSprites),hl
        ld      hl,Sqrxz
        add     hl,de
        ld      (__sqrxz),hl
        ld      hl,Blob
        add     hl,de
        ld      (__blob),hl
        ld      hl,BlobDead
        add     hl,de
        ld      (__blobDead),hl
        ld      hl,Hedgehog
        add     hl,de
        ld      (__hedgehog),hl
        ld      hl,Bat
        add     hl,de
        ld      (__bat),hl
        ld      hl,BatSplash
        add     hl,de
        ld      (__batSplash),hl
        ld      hl,GreenMan
        add     hl,de
        ld      (__greenman),hl
        ld      hl,GreenManHide
        add     hl,de
        ld      (__greenmanHide),hl
        ld      hl,Fish
        add     hl,de
        ld      (__fish),hl
        ld      hl,Sausage
        add     hl,de
        ld      (__sausage),hl
        ld      hl,Lever
        add     hl,de
        ld      (__lever),hl
        ; check for saved state
        ld      hl,fileSave
        call    _Mov9toOp1
        call    _ChkFindSym
        jp      nc,TeacherKeyResume
        ; show vbuf
        call    vramFlip
        call    waitKey                         ; wait until a key is pressed
        call    vramClear
        call    loadGamePalette
        ; Clear some main variables
        xor     a
        ld      (lvl),a                         ; Start at the first level (0, although displayed as 1)
        ld      (sausages),a                    ; Number of sausages eaten
        ld      (gaming),a                      ; A flag telling the game hasn't started yet
        ld      (start_x),a                     ; If >0, Sqrxz has pulled a lever. 0 = not pulled
        ld      a,3
        ld      (lives),a                       ; And start with 3 lives

        call    ChooseWorld                     ; And a call to select world to play on

PlayWorld:
        call    GetReady
        
        ld      a,1
        ld      (gaming),a                      ; Now the game has started
        inc     a
        ld      (statusCnt),a                   ; update status bar text for first 2 frames
        ld      hl,(pWorld)                     ; HL now points to World data
        ld      a,(hl)                          ; A = format; 0=uncompressed; 1=compressed
        ld      (worldT),a                      ; Save it for later
        inc     hl
        inc     hl                              ; Skip this byte, it's always 0 (Fargo compatibility)
        ld      a,(hl)                          ; A = number of levels in this world
        ld      (noLevels),a                    ; Store that in the memory as well
        inc     hl                              ; HL -> name of world
        ld      (worldN),hl                     ; save it
        call    drawString                      ; fake drawString to get to next string
        inc     hl                              ; HL -> author
        call    drawString                      ; another fake to get to level data
        ld      (lvlptrs),hl                    ; Save HL for later, which points to level data

        ld      hl,LevelPtr                     ; HL -> Future storage of temporary level
        ld      bc,2047                         ; Prepare clear 2k memory. Max lev size is 255*8=2040
        call    ClearMem                        ; Clear it. Last 8 bytes are necessary to be cleared

        ld      hl,(lvlptrs)                    ; HL -> level info
        ld      a,(worldT)
        or      a                               ; Check if the world was compressed or not
        ld      a,(noLevels)                    ; A = number of levels (will be used in both cases)
        jr      z,UncompWorld                   ; If uncompressed, the data is stored differently
        ld      e,a                             ; Mul noLevels with 4 to find the beginning of the
        ld      d,4                             ; huffman compressed data
        mlt     de                              ; DE=offset to skip
        push    hl                              ; But HL must preserved
        add     hl,de                           ; Now HL -> hufflib data
        ex      (sp),hl                         ; Push that addr to the stack while popping the prev HL
        ld      a,(lvl)
        ld      b,a                             ; B=level number='file' number in the huffman archive
        ld      e,a
        ld      d,4
        mlt     de
        add     hl,de                           ; HL -> levelsize (word) and time (word)
        inc     hl                              ; Levelsize is always<256, thus this byte=0 (Big Indian)
        ld      a,(hl)
        ld      (LevelSize),a                   ; Save the level size
        inc     hl                              ; HL -> time (word)
        ld      de,0
        ld      d,(hl)
        inc     hl
        ld      e,(hl)                          ; DE = time to complete the level
        ld      (timeleft),de                   ; Save it
        pop     hl                              ; HL -> hufflib compressed data
        ld      de,LevelPtr                     ; DE -> where to store the data (temp level storage)
        ld      ix,huffLib                      ; IX -> 1024 bytes of free memory to be used by hufflib
        call    HuffExtr                        ; Extract 'file' B with the above parameters
        jr      LoadL                           ; The level is extracted, initialize the first part
UncompWorld:
        ld      e,a
        ld      d,e
        mlt     de                              ; Multiply noLevels with 2
        push    hl                              ; Save HL for later (-> array of offsets)
        add     hl,de                           ; HL -> start of level data (incl level info)
        ex      (sp),hl                         ; Exchange stack contents with HL
        ld      a,(lvl)
        add     a,a                             ; Multiply level with two,
        ld de,0 \ ld e,a
        add     hl,de                           ; thus getting the offset to the offset pointer
        ld      de,0
        ld      d,(hl)
        inc     hl
        ld      e,(hl)                          ; Read that offset
        pop     hl
        add     hl,de                           ; Add it with the pointer to raw level data
        inc     hl                              ; Increase with 1. HL -> level to play info
        ld      a,(hl)                          ; which starts with the level size
        ld      (LevelSize),a
        inc     hl                              ; HL -> time to complete level
        push    hl
        ld hl,0 \ ld l,a                        ; Prepare to calculate how many bytes to copy
        add     hl,hl
        add     hl,hl                           ; Multiply level size with 8, since there 8 tiles
        add     hl,hl                           ; in one column
        ex      (sp),hl                         ; Exchange stack contents
        ld      de,0
        ld      d,(hl)
        inc     hl
        ld      e,(hl)                          ; Read the time
        ld      (timeleft),de                   ; and store it
        inc     hl                              ; HL -> raw level data
        ld      de,LevelPtr                     ; DE -> temporary level storage
        pop     bc                              ; BC = bytes to copy (levelsize*8)
        ldir                                    ; And copy it
LoadL:
        ld      bc,300000
        call    waitBC
        call    LoadLevel                       ; Initialize the current level
        call    vramClear
        call    drawStats                       ; write stats text
        call    vramFlip
        call    drawStats                       ; on both vbufs
        ld      hl,fontLarge
        call    loadFont                        ; load large font (will be used during MainLoop)

; The mainloop contains many steps which are (in order):

;  1. Animate all tiles that should, according to the AnimTable.
;  2. Check if Sqrxz is doing his death jump. If so, update him.
;  3. Check if Sqrxz is jumping. If so, try move him up, and skip next step.
;  4. Check if there is ground below him. If there isn't, fall down.
;  5. Check if there is bridge below Sqrxz. If there is, start bridge anim.
;  6. Check the down key. If it is held down, don't check other keys
;     if the last bit in the counter is set (jump to step 9).
;  7. Check the up key. Start the jump if possible.
;  8. Check the left and right keys, and move Sqrxz if possible.
;  9. Check the square(s) Sqrxz is/are at for portcullis, spikes, traps.
; 10. A short check if a portcullis should close.
; 11. Move all enemies if 'even' frame
; 12. Update the timebar (decrease) and the waterbar (decrease if in water,
;     else increase).
; 13. Draw background, enemies, Sqrxz, time/water bars, update stats if required, and flip vram
; 14. If Sqrxz is alive and the player hasn't aborted the game, jump to step 1.

MainLoop:
        xor     a                               ; A lot of vars should be cleared each frame
        ld      (timer),a
        ld      (sqflags),a
        call    keyScan                         ; do a key scan
        ld      hl,counter
        inc     (hl)                            ; Increase the overall counter
        ld      hl,animcnt
        inc     (hl)                            ; Increase the anim counter, which is modulo 3
        ld      a,(hl)
        cp      3
        jr      nz,CheckDead                    ; Only animate 1/3
        ld      (hl),0                          ; Clear the anim counter
        ld      hl,AnimTable                    ; HL -> animation table
        ld      b,8                             ; Max 8 animations at the same time
RepCheckAnim:
        push    bc
        ld      b,(hl)                          ; b = x coord
        inc     hl
        push    hl
        ld      a,(hl)                          ; a = y coord
        cp      255                             ; If 255 then no animation
        jr      z,NextAnim
        inc     hl
        call    _LoadDEInd                      ; DE loaded with (HL)
        ex      de,hl                           ; HL = address in level to animating object
        ld      c,a                             ; B,C = coordinates
        ld      a,(hl)                          ; Check which the new object should be
        cp      99                              ; A lever being pulled?
        jr      z,NextAnimStep                  ;  Pull it down
        cp      14                              ; Portcullis halfway down?
        jr      z,NextAnimStep                  ;  Then next should be port down
        cp      96                              ; Spikes halfway up?
        jr      z,NextAnimStep                  ;  Then next should be spikes up
        and     %11111100                       ; Mask off the two last bits
        cp      20                              ; Check if A was 20-23 (Steel brick animation)
        jr      nz,NA1                          ;  If not, check other
        ld      a,(hl)
        inc     a
        ld      (hl),a                          ; Increase so it becomes the next animation tile
        cp      24                              ; Check if end of steel brick animation
        jr      nz,ShowChange                   ;  Not end - show the change
        ld      a,2                             ; End of animation - reset to steel brick
        jr      EndOfAnim                       ; Mark end of animation
NextAnimStep:
        inc     a                               ; Increase to the next animation step
        jr      EndOfAnim                       ; EOA
NA1:
        cp      24                              ; Check if A was 24-27 (Brick crush animation)
        jr      nz,NA2                          ;  Nope - check other
        inc     (hl)                            ; Increase so it becomes the next animation tile
        ld      a,(hl)
        cp      28                              ; Check if EOA
        jr      nz,ShowChange                   ;  Nope - show change
        xor     a                               ; Reset to space
        jr      EndOfAnim                       ; And EOA
NA2:
        cp      28                              ; Last check - was A 28-31 (Bridge animation)
        jr      nz,NextAnim                     ; No, skip to next entry (this jump shouldn't occur)
        inc     (hl)                            ; Increase so it becomes the next animation tile
        ld      a,(hl)
        cp      32                              ; Check if EOA
        jr      nz,ShowChange                   ;  Nope - show change
        xor     a                               ; Reset to space
EndOfAnim:
        ld      (hl),a                          ; Store the change in memory
        pop     hl                              ; HL -> that animation table entries Y addr
        ld      (hl),255                        ; Store 255 which means no animation
        push hl
ShowChange:
NextAnim:
        pop     hl                              ; Restore HL
        inc     hl
        inc     hl
        inc     hl
        inc     hl                              ; Increase HL with 4 so it points to the next entry
        pop     bc                              ; Pop the counter
        djnz    RepCheckAnim                    ; And check for new animation

CheckDead:                                      ; This updates Sqrxz "death jump"
        ld      a,(deadcnt)
        or      a                               ; Check if Sqrxz is dead and has disappeard from screen
        jp      nz,CheckEnemies                 ; If so, skip a lot of stuff and update enemies
        ld      hl,dead
        ld      a,(hl)
        or      a                               ; Check if dead
        jr      z,CheckJump                     ;  No - check other stuff
        inc     a                               ; Increase the dead counter
        ld      (hl),a
        ld      hl,ArcData                      ; HL -> The precalculated "gravity" table
        ld      de,0
        sub     22
        jr      nc,DDown                        ; If counter>=22, Sqrxz is falling down
        inc     a
        neg
        ld e,a
        add     hl,de                           ; Add with offset
        ld      a,(hl)                          ; Now A = no pixels to move Sqrxz up
        neg                                     ; Make that negative since up is negative
        jr      DMoveVert                       ; Do the vertical move
DDown:
        cp      27                              ; The counter has a limit.
        jr      c,DDown2                        ; If above,
        ld      a,26                            ;  set it to max
DDown2:
        ld      e,a
        add     hl,de                           ; Add with offset
        ld      a,(hl)                          ; Now A = no pixel to move Sqrxz (down)
DMoveVert:
        ld      hl,y
        add     a,(hl)                          ; Add the Y coordinate with A
        ld      (hl),a
        ld      hl,(x)
        ld      a,(sqrxzdir)                    ; The horizontal death move is made in the opposite
        or      a                               ; direction of which Sqrxz faces
        jr      z,DLeft
        inc     hl                              ; If facing left, increase X coordinate
        jr      PutDeadSqrxz
DLeft:
        dec     hl                              ; Else decrease it
PutDeadSqrxz:
        ld      (x),hl                          ; Store the change
        ld      hl,sqflags
        set     2,(hl)                          ; Set a flag telling that Sqrxz has moved this frame
        jp      CheckEnemies                    ; Check if enemies are to move - skip the user control

CheckJump:
        ld      a,(jump)                        ; Check the jump counter
        or      a                               ; Is it 0 (no jump in progress)?
        jr      z,CheckFall                     ; Yup - check if falling (or if Sqrxz should fall)
        ld      hl,jump
        dec     (hl)                            ; Decrease the jump counter (it counts backwards)
        ld      a,(hl)
        or      a
        jr      z,CheckFall                     ; If 0, the jump is over. Check if he should start fall
        ld      hl,ArcData                      ; HL -> The precalculated "gravity" table
        ld de,0 \ ld e,a
        add     hl,de                           ; Add with the offset (the jumping counter)
        ld      a,(hl)                          ; Now A = number of pixels to move Sqrxz up this frame
        or      a                               ; If no pixels,
        jp      z,CheckBridge                   ;  skip it for this time and skip the fall checking
        ld      b,a                             ; Load the counter register with number of steps
RepJump:
        push    bc
        call    MoveUp                          ; Try to move up one pixel
        pop     bc
        jr      c,HitHead                       ; Ouch! Something is in the way - check what it is
        djnz    RepJump                         ; Else repeat the loop
        call    CheckUp                         ; Check if anything above
        jp      nc,CheckBridge                  ;  If not, end of jump routine
HitHead:
        xor     a
        ld      (jump),a                        ; Clear the jump counter - the jump is over
        ld      a,(y)
        dec     a                               ; A = y position of tile above
        ld      hl,(x)
        ld      de,4
        add     hl,de                           ; HL = Sqrxz x position+4 (middle of Sqrxz)
        call    GetBlock                        ; Find out what tile is above
        dec     a                               ; Is it a brick wall (1)?
        jr      z,HeadInBrick                   ;  Yes - start a Brick crush animation
        dec     a                               ; Is it a steel brick (2)?
        jr      nz,CheckFall                    ;  Nope - check if Sqrxz should start fall instead
        ld a,21                                 ; It was a steel brick above - start an animation
NewA:
        call    NewAnim                         ; Add a new animation (A) to the AnimTable
        jr      CheckFall                       ; And check if Sqrxz should start fall
HeadInBrick:
        ld      a,24                            ; Prepare to start a Brick Crush animation
        jr      NewA                            ; Start it

CheckFall:
        call    CheckDown                       ; Try to move Sqrxz down one step
        jp      c,NoFall                        ;  There's ground below! Don't fall
        ld      hl,fall                         ; No ground below,
        inc     (hl)                            ; increase the fall counter
        ld      a,(hl)
        cp      27                              ; The counter ranges from 0-26
        jr      nz,FallDown                     ; (you can't fall faster than a max value)
        ld      a,26                            ; If >26, set to 26
        ld      (hl),a
FallDown:
        ld      hl,ArcData                      ; HL -> precalculated "gravity" table
        ld de,0 \ ld e,a
        add     hl,de                           ; Add with offset (the falling counter)
        ld      a,(hl)                          ; Now A = number of pixels to move Sqrxz down this frame
        or      a                               ; If 0 pixels,
        jp      z,CheckBridge                   ;  no moving down this frame
        push    af
        ld      c,a
        ld      ix,enemytable                   ; IX -> enemy table
        ld      b,8                             ; Max 8 enemies to check
CheckEnemyKill:
        ld      a,(ix)
        or      a                               ; Check if there is an enemy at this table entry
        jr      z,CEKNext                       ;  If not, check next entry
        cp      2                               ; Check if hedgehog (can't kill those)
        jr      z,CEKNext                       ;  If yes, then check next entry
        cp      5                               ; Check if fish (can't kill those either)
        jr      z,CEKNext                       ;  If yes, then check next entry
        ld      a,(ix+7)
        or      a                               ; Check if something is happening to the enemy
        jr      nz,CEKNext                      ;  If so, then he can't be killed
        ld      hl,(x)
        ld      de,0
        ld      d,(ix+3)
        ld      e,(ix+2)                        ; DE = enemy x coordinate
        sbc     hl,de                           ; Subtract Sqrxz X coordinate with the enemies
        ld      a,l
        jr      nc,AbsX                         ; If the result <0
        neg                                     ;  then negatate it (the absolute result is important)
AbsX:
        cp      8                               ; If the difference between the coordinates>=8
        jr      nc,CEKNext                      ;  Then no kill is possible - check next entry
        ld      a,(ix+5)
        ld      hl,y
        sub     (hl)                            ; Subtract enemies Y coord with Sqrxz
        sub     8                               ; Subtract with 8 (the creature size)
        bit     7,a                             ; Check if result <0
        jr      nz,CEKNext                      ;  If so, Sqrxz is not above enemy - check next entry
        sub     c                               ; Subtract with number of steps to fall
        jr      nc,CEKNext                      ;  If not carry, then Sqrxz doesn't fall enough
        ld      (ix+7),1                        ; Set a flag indicating a hit
        xor     a
        ld      (fall),a                        ; Clear fall counter
        ld      a,(ix+5)
        sub     8
        ld      (y),a                           ; Set Sqrxz Y coordinate equal to enemy-8
        pop     bc                              ; Just removing the unncessary data from the stack
        ld      a,6
        ld      (jump),a                        ; A small jump is made when hitting an enemy
        ld      a,(jump_button)
        or      a                               ; Has the jump button been released since last jump?
        jr      nz,CheckBridge                  ;  If not, don't check if jump button is pressed
        ld      a,(kbdG7)
        bit     kbitUp,a                        ; Check if the up key is being pressed
        jr      nz,JumpOnEnemy                  ;  If so, start a jump
        ld      a,(kbdG1)
        bit     kbit2nd,a                       ; Check if the 2nd key is being pressed
        jr      z,CheckBridge                   ;  If not, do other stuff
JumpOnEnemy:
        ld      a,20
        ld      (jump),a                        ; Else set the jumpcounter to 20 (max value)
        ld      a,1
        ld      (jump_button),a                 ; And set the flag indicating jump button is down
        jr      CheckBridge
CEKNext:
        ld      de,8
        add     ix,de                           ; Add with 8 to get the next entry
        dec     b
        jp      nz,CheckEnemyKill               ; And loop
        pop     bc                              ; Pop the counter register with no steps
RepFallDown:
        push    bc
        call    MoveDown                        ; Move down one step. No checking for ground below
        pop     bc                              ; is needed, because the way the "gravity" table is made
        djnz    RepFallDown                     ; Repeat the falling
        jr      CheckBridge                     ; And check some other stuff
NoFall:
        xor     a
        ld      (fall),a                        ; Reset the fall counter

CheckBridge:                                    ; This routine checks if there is a bridge below you
        ld      a,(y)
        and     $07                             ; Check if the last three bits in Y is 0.
        jr      nz,CheckKeys                    ;  Nope, Sqrxz is between (in y dir) two tiles
        call    CheckDown                       ; Get the two tiles below Sqrxz. B = leftD, C = rightD
        ld      a,b
        cp      16                              ; Check if bridge below Sqrxz to the left
        jr      z,BridgeBelow                   ;  Yup - check if it should start animating
        ld      a,c
        cp      16                              ; Else check if bridge below Sqrxz to the right
        jr      nz,CheckKeys                    ;  No, no bridge. Check if user has pressed any keys
BridgeBelow:
        ld      a,b                             ; Since B or C = 16 (bridge), and if B+C=16, it would
        add     a,c                             ; mean that Sqrxz his his centre of gravity on the
        cp      16                              ; bridge (since B or C would be 0 then)
        jr      nz,CNearB                       ;  No, that wasn't the case - check where CoG is
        ld      hl,(x)                          ; HL = Sqrxz X coordinate
        ld      a,b
        or      a
        jr      nz,BGetB                        ; Check if it was B=16. If so find out what's below CoG
        ld      de,7
        add     hl,de                           ; Make HL point to Sqrxz's right side
        jr      BGetB                           ; And find out what tile is below there
CNearB:
        ld      hl,(x)                          ; So, there is a bridge below combined with something
        inc     hl                              ; else. The centre of gravity is in the middle of
        inc     hl                              ; Sqrxz (since both tiles are ground tiles), so add
        inc     hl                              ; HL with 4 so it contains the X position to middle
        inc     hl                              ; of Sqrxz
BGetB:                                          ; Here, HL = Sqrxz Centry Of Gravity (xdir)
        ld      a,(y)
        add     a,8                             ; A = y location below Sqrxz
        call    GetBlock                        ; Find out what block is below
        cp      16                              ; Check if it is a bridge
        jr      nz,CheckKeys                    ; If not, the CoG stands on a brick or earth - no anim
BridgeBroken:
        ld      a,28                            ; Else that bridge should start animating (crashing)
        call    NewAnim                         ; Start a new animation (28 = first bridge crush anim)

CheckKeys:
        ld      a,(kbdG1)
        ld      c,a                             ; save to check for 2nd later
        ld      a,(kbdG2)
        bit     kbitAlpha,a                     ; check for alpha
        ld      a,(kbdG7)                       ; check arrow keys
        jr      nz,SlowMovement
        bit     kbitDown,a                      ; Check if the down key is held down
        jr      z,CheckMovement                 ;  If not, check for movement
SlowMovement:
        ld      hl,counter                      ; When the down key is held down, you only move
        bit     0,(hl)                          ; if the main counter is an even number - check that
        jr      nz,CheckSquare                  ; No it was an odd number - skip movement checking

CheckMovement:
        push    af
        bit     kbitUp,a                        ; Is the up key held down?
        jr      nz,TryJump                      ;  Yup - check if it's possible to jump
        bit     kbit2nd,c                       ; Is the 2nd key held down?
        jr      nz,TryJump                      ;  Yup - check if it's possible to jump
        xor     a
        ld      (jump_button),a                 ; Clear the "jump button not pressed" flag
        jr      CheckHorzKeys                   ; Check for horizontal movement
TryJump:
        call    StartJump                       ; Start the jump is possible
CheckHorzKeys:
        pop     af

        push    af
        bit     kbitLeft,a                      ; Left key held down?
        call    nz,MoveLeft                     ;  Yup - move left if possible
        pop     af
        bit     kbitRight,a                     ; Right key held down?
        call    nz,MoveRight                    ;  Yup - move right if possible
        ld      hl,sqflags
        bit     2,(hl)                          ; Check if Sqrxz has moved this frame
        jr      nz,MovedHor                     ;  Yes Sqrxz has moved
        ld      a,(hmovelast)
        or      a                               ; Did Sqrxz move last frame?
        jr      z,CheckSquare                   ;  Nope - check some other stuff
        ld      hl,Sqrxzcnt                     ; Prepare to clear the Sqrxz anim counter
        xor     a                               ; This will prevent Sqrxz from having his legs
        ld      (hl),a                          ; in the air if he's not moving (would look stupid)
        ld      (hmovelast),a                   ; Clear the flag - Sqrxz didn't move this frame
        jr      SetUpdateSqrxz                  ; Sqrxz is to be updated (the Sqrxz animation changed)

MovedHor:
        ld      a,1
        ld      (hmovelast),a                   ; Set the Sqrxz Moved Last Frame flag
SetUpdateSqrxz:
        ld      hl,sqflags
        set     2,(hl)                          ; And set the Sqrxz Moved This Frame flag

CheckSquare:
        ld      a,(y)
        add     a,3                             ; A = y coordinate at Sqrxz feet
        ld      hl,(x)                          ; HL = Sqrxz x coordinate
        call    GetBlock                        ; Find out what block is to the left of Sqrxz
        cp      93                              ; Extra life?
        call    z,ExtraLife                     ;  Increase life
        cp      64                              ; Sausage?
        call    z,EatSausage                    ;  Eat it
        cp      98                              ; Lever?
        call    z,PullLever                     ;  Pull it
        cp      97                              ; Visible spike?
        jr      z,SqrxzDied                     ;  DIE!
        cp      94                              ; An open portcullis?
        call    z,PCFound                       ;  Yup - prepare for it to fall down
        cp      95                              ; A spike trap?
        jr      nz,CSNext                       ;  No - check for block where Sqrxz is to the right
        ld      a,96                            ; Prepare to start a spike animation
        call    NewAnim                         ; Start it
        ld      hl,sqflags
        set     3,(hl)                          ; Set a flag indicating Sqrxz died this frame
CSNext:
        ld      a,(y)
        add     a,3                             ; A = y coordinate at Sqrxz feet
        ld      hl,(x)
        ld      de,7
        add     hl,de                           ; HL = Sqrxz x coordinate+7 (the right part of him)
        call    GetBlock                        ; Find out what block is to the right of Sqrxz
        cp      104                             ; Is it the bottomright corner of an exit door?
        jp      z,LevelFinished                 ;  YES! Level finished!
        cp      93                              ; Extra life?
        call    z,ExtraLife                     ;  Increase life
        cp      64                              ; Sausage?
        call    z,EatSausage                    ;  Eat it
        cp      98                              ; Lever?
        call    z,PullLever                     ;  Pull it
        cp      97                              ; Visible spike?
        jr      z,SqrxzDied                     ;  DIE!
        cp      94                              ; An open portcullis?
        call    z,PCFound                       ;  Yup - prepare for it to fall down
        cp      95                              ; A spike trap?
        jr      nz,CheckIfDied                  ;  No - check if Sqrxz has died this frame
        ld      a,96                            ; Prepare to start a spike animation
        call    NewAnim                         ; Start it
        ld      hl,sqflags
        set     3,(hl)                          ; Set a flag indicating Sqrxz died this frame

CheckIfDied:
        ld      hl,sqflags
        bit     3,(hl)                          ; Check if Sqrxz died this frame
        jr      z,CheckPort                     ;  No, he didn't - check if a portcullis should close
SqrxzDied:
        call    KillSqrxz

CheckPort:
        ld      hl,(portc)                      ; HL = x,y of portcullis to close
        ld      a,h
        or      l                               ; Check if HL=0
        jr      z,CheckEnemies                  ;  It was - no such port. Check for enemies
        ld      hl,sqflags
        bit     4,(hl)                          ; Check if Sqrxz was below a portcullis this frame
        jr      nz,CheckEnemies                 ;  Yes he was - can't close the portcullis now.
        ld      hl,portc
        xor     a
        ld      b,(hl)                          ; B = x coor of portcullis to go down
        ld      (hl),a                          ; Clear that byte
        inc     hl
        ld      c,(hl)                          ; C = y coor of portcullis to go down
        ld      (hl),a                          ; Clear that byte as well
        inc     hl
        call    _LoadDEInd
        ex      de,hl                           ; HL = address in level where the portcullis is
        ld      a,14                            ; Prepare to start a closing portcullis animation
        call    NewAnim                         ; Start it

CheckEnemies:
        call    MoveEnemies                     ; Move all active enemies
        ld      a,(dead)
        or      a                               ; Check if dead
        jr      nz,NoScroll                     ;  Never scroll if Sqrxz is doing his death jump
        ld      hl,(x)
        ld      de,(XScr)
        or      a
        sbc     hl,de                           ; HL = Sqrxz X position on screen
        ld      a,l
        cp      85                              ; Check if >=85
        jr      nc,Scroll                       ;  If so, scroll
        cp      70                              ; Check if <70
        jr      c,NoScroll                      ;  If so, no scroll
        ld      hl,counter
        bit     0,(hl)                          ; If between 70-84, scroll every second frame
        jr      z,NoScroll                      ; If an "even" frame, skip scrolling
Scroll:
        call    ScrollLeft                      ; Scroll screen!
NoScroll:
        ld      hl,(x)                          ; Now check if Sqrxz nose is in water
        ld      a,(sqrxzdir)
        or      a
        jr      nz,SqLeft                       ; If Sqrxz is faced to the right,
        ld      de,7                            ;  the nose is to the right part of the sprite,
        add     hl,de                           ; thus adding 7 to the x coodrinate
SqLeft:
        ld      a,(y)
        add     a,5                             ; Always add 5 to the y coordinate
        call    GetBlock                        ; Find out what tile is at that location
        ld      hl,(waterbar)                   ; HL=water bar
        cp      109                             ; Check if tile is <109
        jr      c,IncreaseWaterBar              ;  If so, increase the water bar
        dec     hl                              ; Else decrease it
        ld      (waterbar),hl                   ; Save the new value
        ld      a,l
        or      h                               ; Check if HL has reached zero
        jp      z,Drowned                       ;  If so, Sqrxz has drowned :(
        jr      UpdateTime                      ; Jump to the timer update routine
IncreaseWaterBar:
        ld      de,479
        call    _cphlde                         ; Check if top of bar is reached
        jr      z,UpdateTime                    ;  If so, don't increase the bar
        inc     hl                              ; Increase bar
        call    _cphlde                         ; Check if top reached
        jr      z,UpdateWaterBar                ;  If so, don't increase another step
        inc     hl                              ; Increase again - the bar increase faster than decr
UpdateWaterBar:
        ld      (waterbar),hl                   ; Update the new value
UpdateTime:
        ld      hl,(timeleft)
        dec     hl                              ; Always decrease the time with two - the time "format"
        dec     hl                              ; is from the Fargo version
        ld      (timeleft),hl                   ; Save the updated time
        ld      a,h                             ; A = time/256
        or      l                               ; Check if out of time
        jp      z,TimeOut                       ;  If so, time is out - show message

        ld      a,(kbdG1)
        bit     kbitDel,a
        jr      nz,LoseLife
        bit     kbitMode,a
        jp      nz,PauseGame
        
        ld      a,(kbdG2)
        bit     kbitMath,a
        jp      nz,TeacherKey
        
        ld      a,(kbdG6)
        bit     kbitClear,a
        jp      nz,QuickQuit
        bit     kbitAdd,a
        jr      z,PauseResume
        bit     kbitPower,a
        jp      nz,LevelFinished

PauseResume:
        di
        call    drawBG
        call    drawEnemies
        call    drawSqrxz
        call    updateBars
        call    updateStats
        call    vramFlip
        
        ld      a,(y)                           ; Prepare to find out if Sqrxz y coordinate is >=64
        cp      150                             ; If it's bigger than 150, then it's negative
        jp      nc,MainLoop                     ;  It was >150, restart frame from beginning
        cp      64                              ; Else check if >=64
        jp      c,MainLoop                      ;  Nope it wasn't. Sqrxz is alive - jump to mainloop

        call    _getcsc
        or      a                               ; Check if any key has been pressed
        jr      nz,LoseLife                     ; If so, skip the pause
        ld      hl,deadcnt
        inc     (hl)                            ; Increase death pause counter
        ld      a,(hl)
        cp      200                             ; Check if 200 frames has passed since Sqrxz disappeard
        jp      c,MainLoop                      ; If not, repeat
LoseLife:
        call    _getcsc                         ; Clear keybuffer
        xor     a
        ld      (sausages),a                    ; Reset sausages counter
        ld      hl,lives
        dec     (hl)                            ; Lose a life
        jp      z,QuickQuit                     ; If no lifes left, quit game
        jp      PlayWorld                       ; And restart the level

TimeOut:
        ld      hl,OutOfTimeTxt                 ; HL -> "OUT OF TIME"
ShowDeathMess:
        ld      de,72
        ld      c,104
        call    drawString
        call    vramFlip
WaitK:
        call    _getcsc
        cp      5
        jr      c,WaitK                         ; Wait until a key (except arrowkeys) gets pressed
        jr      LoseLife                        ; Jump to label above which decrease your life

Drowned:
        ld      hl,DrownedTxt                   ; HL -> "YOU DROWNED"
        jr      ShowDeathMess                   ; Same procedure as above, so use that code

LevelFinished:
        call    drawBG
        call    drawSqrxz
        ld      hl,LevelFinTxt
        ld      de,42
        ld      c,104
        call    drawString
        call    vramFlip
        ld      bc,300000
        call    waitBC
        xor     a
        ld      (start_x),a                     ; If you've pulled a lever, clear that
        ld      hl,lvl
        inc     (hl)                            ; Increase the level
        ld      a,(noLevels)
        cp      (hl)                            ; Check if the previous level was the last one
        jp      nz,PlayWorld                    ;  If not, play this level
        call    vramClear
        ld      hl,fontLarge
        call    loadFont
        ld      hl,GameFinTxt
        ld      de,40
        ld      c,80
        call    drawString
        push    hl
        ld      hl,fontSmall
        call    loadFont
        pop     hl
        ld      de,84
        ld      c,120
        call    drawString
        ld      de,76
        ld      c,130
        call    drawString
        call    vramFlip
        call    waitEnter
        jp      QuickQuit

PCFound:
        ex      de,hl                           ; Prepare to store info about the portcullis
        ld      hl,portc
        ld      (hl),b                          ; Store the x coordinate
        inc     hl
        ld      (hl),c                          ; Store the y coordinate
        inc     hl
        ld      (hl),e                          ; And finally store the address
        inc     hl
        ld      (hl),d
        inc     hl
        call    _SetAToDEU
        ld      (hl),a
        ld      hl,sqflags
        set     4,(hl)                          ; Set the flag "Sqrxz is below a portcullis"
        ret

EatSausage:
        push    af                              ; The A register must be preserved when returning
        xor     a
        ld      (hl),a                          ; Remove the sausage from temp level storage
        ld      a,2
        ld      (statusCnt),a                   ; Update status text next 2 frames
        ld      hl,sausages
        ld      a,(hl)
        inc     a                               ; Increase the number of sausages
        and     $0F                             ; The counter is modulo 16
        ld      (hl),a
        jr      nz,NoExtraLife                  ; If the counter didn't wrap, don't increase lives
        ld      hl,lives
        inc     (hl)                            ; Else increase number of lives
NoExtraLife:
        pop af
        ret

ExtraLife:
        push    af                              ; The A register must be preserved when returning
        xor     a
        ld      (hl),a                          ; Remove the extra life from temp level storage
        ld      a,2
        ld      (statusCnt),a                   ; Update status text next 2 frames
        ld      hl,lives
        inc     (hl)                            ; Increase number of lives
        pop     af
        ret

PullLever:
        push    af
        ld      (start_y),bc                    ; Update the startcoord so Sqrxz starts here next life
        ld      a,99
        call    NewAnim                         ; Start the lever animation
        pop     af
        ret

PauseGame:
        ld      hl,PausedTxt
        ld      de,112
        ld      c,104
        call    drawString
        call    vramFlip
        call    waitModeUp
PauseWait:
        call    waitKey
        cp      GK_CLEAR
        jp      z,QuickQuit
        cp      GK_MODE
        jr      nz,PauseWait
        call    waitModeUp
        jp      PauseResume

MoveLeft:
        call    CheckLeft                       ; Check if possible to move left
        ret     c                               ;  Nope, it wasn't
        ld      hl,(x)
        ld      de,(XScr)
        call    _cphlde                         ; Check if Sqrxz is at the left edge of screen
        ret     z                               ;  Yes he is - then you can't move left
        dec     hl                              ; Decrease Sqrxz x coordinate
        ld      (x),hl                          ; Store it
        ld      hl,sqflags
        set     2,(hl)                          ; Set the "Sqrxz has moved" flag
        ld      a,1
        ld      (sqrxzdir),a                    ; Change Sqrxz direction to Left
        ld      hl,Sqrxzcnt
        inc     (hl)                            ; Update the Sqrxz animation counter
        ret

MoveRight:
        call    CheckRight                      ; Check if possible to move right
        ret     c                               ;  Nope, it wasn't
        ld      hl,(XScr)
        ld      de,112                          ; (15-1)*8=112
        add     hl,de
        ld      de,(x)
        call    _cphlde                         ; Check if Sqrxz has reached the right edge of screen
        ret     z                               ;  Yes he has - then you can't move right
        inc     de                              ; Else increase the x coordinate
        ld      (x),de                          ; And store it
        ld      hl,sqflags
        set     2,(hl)                          ; Set the "Sqrxz has moved" flag
        xor     a
        ld      (sqrxzdir),a                    ; Change Sqrxz direction to Right
        ld      hl,Sqrxzcnt
        inc     (hl)                            ; Update the Sqrxz animation counter
        ret

StartJump:
        ld      a,(fall)                        ; Now a procedure to check if Sqrxz is falling...
        ld      hl,jump
        or      (hl)                            ; ...or jumping...
        ld      hl,jump_button
        or      (hl)                            ; ...or haven't released the jump button since last jump
        ret     nz                              ; If that's the case, no jumping is allowed.
        ld      a,20
        ld      (jump),a                        ; Else set the jumpcounter to 20 (max value)
        ld      a,1                             ; Set the flag indicating that the jump button
        ld      (jump_button),a                 ; has been pressed this frame
        ret

MoveDown:
        call    CheckDown                       ; Check if it's possible to move down
        ret     c                               ;  Return if not possible
        ld      hl,sqflags
        set     2,(hl)                          ; Set the flag indicating that Sqrxz has moved
        ld      hl,y
        inc     (hl)                            ; Increase the y coordinate
        ret

MoveUp:
        call    CheckUp                         ; Check if it's possible to move up
        ret     c                               ;  Return if not possible
        ld      hl,sqflags
        set     2,(hl)                          ; Set the flag indicating that Sqrxz has moved
        ld      hl,y
        dec     (hl)                            ; Decrease the y coordinate
        ret

MoveEnemies:
        ld      hl,counter
        bit     0,(hl)                          ; Check if even frame number
        ret     z                               ;  If so, don't move enemies this frame
        ld      hl,(x)
        ld      (tmp_x),hl                      ; Save Sqrxz coordinates into temporary position
        ld      a,(y)                           ; The enemies will use the same detecting routines
        ld      (tmp_y),a                       ; as Sqrxz, so they need to borrow Sqrxz variables
        ld      ix,enemytable                   ; IX -> at the beginning of the enemy table
        ld      b,8                             ; There can be at most 8 enemies moving at the same time
ControlEnemy:
        push    bc
        ld      a,(ix)                          ; Find out which enemy
        or      a
        jp      z,NextEnemy                     ; If none, check next enemy
        ld      a,(ix+7)
        or      a
        jp      nz,NextEnemy                    ; If spec>0, the enemy is not moving at the moment
        call    _SetHLUTo0
        ld      h,(ix+3)
        ld      l,(ix+2)                        ; HL = enemy x location
        ld      (x),hl
        ld      a,(ix+5)                        ; A = enemy y location
        ld      (y),a
        ld      de,(XScr)
        ld      bc,64
        add     hl,bc
        sbc     hl,de                           ; Check if enemy is too far left of Sqrxz
        jr      c,EnemyGone                     ;  If so, the enemy will be 'gone'
        ld      de,(LevelSize)
        ld      d,8
        mlt     de
        ld      hl,(x)
        call    _cphlde                         ; Check if enemy has disappeared to the right of level
        jr      nc,EnemyGone                    ;  If so, enemy gone
        ld      a,(ix)
        cp      3                               ; Check if bat
        jr      z,EHorMove                      ;  If so, skip the vertical movement
        cp      5                               ; Check if fish
        jr      z,EHorMove                      ;  If so, skip the vertical movement
        call    CheckDown                       ; Check if enemy can fall down
        jr      c,EResetFall                    ;  If it can't, reset fall counter
        ld      a,(ix+6)
        inc     a                               ; Increase fall counter
        cp      3
        jr      nc,EFallS
        ld      a,(ix)
        cp      4
        ld      a,3                             ; If the enemy is a green man, he should start
        jr      nz,EFallS                       ; falling slower so he doesn't fall down into
        ld      a,2                             ; small holes
EFallS:
        cp      14
        jr      nz,EFallDown                    ; Can't fall faster than a certain value
        ld      a,13
EFallDown:
        ld      (ix+6),a                        ; Update the fall counter
        ld      hl,EArc
        ld de,0 \ ld e,a
        add     hl,de
        ld      a,(hl)                          ; Now A = number of steps to fall this frame
        or      a
        jr      z,EHorMove                      ; If no steps, check vertical movement
        ld      b,a
EFalling:
        push    bc
        call    CheckDown                       ; Try move down one step
        pop     bc
        jr      c,EStopFall                     ; If not possible, stop the fall
        ld      hl,y
        inc     (hl)                            ; Increase y coordinate
        djnz    EFalling                        ; Repeat the fall
EStopFall:
        ld      a,(y)
        cp      64                              ; Check if Y>=64
        jr      c,EHorMove                      ; If not, move enemy in horizontal direction
EnemyGone:
        ld      (ix),0                          ; Clear current position in enemy table
        jp      NextEnemy
EResetFall:
        ld      (ix+6),0                        ; Resets the fall counter
EHorMove:
        ld      b,1
        ld      a,(ix)
        cp      4
        jr      nz,ERepHorMove
        ld      b,2                             ; If green man, the enemy should move two pixels
ERepHorMove:
        push    bc
        bit     0,(ix+1)                        ; Check direction
        jr      z,EDirRight                     ; If (ix+1)=0, the enemy should try right direction
        call    CheckLeft                       ; Check if possible to move left
        jr      c,ELeftStop                     ; If not, change direction
        ld      hl,(x)
        dec     hl                              ; Decrease X coordinate
        ld      (x),hl
        jr      EHorMoveDone                    ; Horizontal move done
ELeftStop:
        res     0,(ix+1)                        ; Change direction to right
        jr      EHorMoveDone
EDirRight:
        call    CheckRight                      ; Check if possible to move right
        jr      c,ERightStop                    ; If not, change direction
        ld      hl,(x)
        inc     hl                              ; Increase X coordinate
        ld      (x),hl
        jr      EHorMoveDone                    ; Horizontal move done
ERightStop:
        set     0,(ix+1)                        ; Change direction to left
EHorMoveDone:
        pop     bc
        djnz    ERepHorMove                     ; If green man, repeat once
UpdateEnemy:
        ld      hl,(x)
        ld      de,(tmp_x)
        or      a
        sbc     hl,de
        ld      de,8
        call    _cphlde
        jr      c,CheckYDif
        dec     de
        add     hl,de
        jr      nc,NoCollision
CheckYDif:
        ld      a,(y)
        ld      hl,tmp_y
        sub     (hl)
        jr      nc,AbsY
        neg
AbsY:                                           ; Now A = abs(enemy_y-sqrxz_y)
        cp      8
        call    c,KillSqrxz                     ; If distance<8, set Sqrxz dead flag
NoCollision:
        ld      de,(x)                          ; This will save (x) and (y) into the enemy table
        ld      (ix+3),d
        ld      (ix+2),e
        ld      a,(y)
        ld      (ix+5),a
NextEnemy:
        ld      de,8
        add     ix,de                           ; Nox IX -> the next enemy table entry
        pop     bc
        dec     b
        jp      nz,ControlEnemy                 ; Repeat until all enemies have been processed
        ld      hl,(tmp_x)
        ld      (x),hl                          ; Restore Sqrxz coordinates into the
        ld      a,(tmp_y)
        ld      (y),a                           ; "real variables"
        ret

KillSqrxz:
        ld      hl,dead
        ld      a,(hl)
        or      a                               ; Check if Sqrxz is already dead
        ret     nz                              ;  If so, don't kill him again
        inc     a
        ld      (hl),a
        ld      (sqrxzdir),a                    ; Face Sqrxz left (temporary)
        ld      hl,(x)
        ld      de,(XScr)
        or      a
        sbc     hl,de                           ; HL = Sqrxz pos, screen relative
        ld      a,l
        cp      64                              ; Check if X rel>=64
        ret     c                               ;  Nope, don't change Sqrxz face direction
        xor     a
        ld      (sqrxzdir),a                    ; Face Sqrxz right instead
        ret

NewAnim:                                        ; Adds a new animation A at (B,C) - addr HL.
        ld      (hl),a                          ; Store the tile no in the temp level storage
        push    af
        ld      a,(animptr)                     ; Now A=next free entry in animation table
        push    af
        push    hl
        ld      e,a
        ld      d,5
        mlt     de                              ; Multiply with 5 since each entry is 5 bytes
        ld      hl,AnimTable
        add     hl,de                           ; HL -> free location in animation table
        ld      (hl),b
        inc     hl
        ld      (hl),c
        inc     hl                              ; X and Y coordinates have been stored
        pop     de                              ; DE = address in memory where the tiles is
        ld      (hl),e
        inc     hl
        ld      (hl),d
        inc     hl
        call    _SetAToDEU
        ld      (hl),a                          ; Now that address is stored as well
        pop     af
        inc     a                               ; Increase the animation pointer
        and     $07                             ; Which is modulo 8
        ld      (animptr),a                     ; Update with the new value
        pop     af
        ret

GetBlock:                                       ; Gets the block at (HL,A) -> A. HL = addr, B,C = coord
        srl     h
        rr      l
        srl     h
        rr      l
        srl     h
        rr      l                               ; Divide X with 8
        ld      b,l
        ld      h,8
        mlt     hl                              ; HL = x with the last three bits cleared
        ld      de,LevelPtr
        add     hl,de                           ; Add HL with the pointer to the level data
        cp      100                             ; Check if Y is >0  (signed)
        jr      c,OnScreen
        ld      c,0                             ; If Y<0, act as Y was 0
        ld      a,(hl)                          ; A = top tile of current column
        or      a
        ret     z
        cp      32
        ld      a,0                             ; If a background tile, act as space (not xor a!)
        ret     nc
        inc     a                               ; Else set to steel wall (so you can't pass it)
        ret
OnScreen:
        srl     a
        srl     a
        srl     a                               ; Divide Y with 8
        ld      c,a
        ld de,0 \ ld e,a
        add hl,de                               ; HL = x*8+y
        ld      a,(hl)                          ; A = tile at (HL)
        ret

CheckTile:                                      ; Returns with Carry if A=foreground tile, else NC
        or      a
        ret     z
        cp      32
        ret

CheckLeft:                                      ; Returns with carry if not possible to move left
        ld      hl,(x)
        dec     hl
CheckHorz:
        ld      a,(y)                           ; HL,A = x,y to the left of Sqrxz
        push    af
        call    GetBlock                        ; Find out what tile is there
        ld      b,a                             ; B will be the leftupper tile of Sqrxz
        ld      c,a                             ; C will be the leftlower tile of Sqrxz
        pop     af
        bit     7,a                             ; Check if Y is negative
        jr      nz,CH_Even                      ;  If so, leftlower=leftupper
        and     $07                             ; Else check if Y is between two tiles
        jr      z,CH_Even                       ;  If not, leftlower=leftupper
        inc     hl                              ; Else leftlower = address after leftupper
        ld      c,(hl)                          ; So now C=leftlower tile
CH_Even:
        ld      a,b
        call    CheckTile                       ; Check if foreground tile
        ret     c                               ;  If so, return with carry
        ld      a,c
        call    CheckTile                       ; Check if foreground tile
        ret                                     ; Return with carry or not carry

CheckRight:
        ld      hl,(x)
        ld      de,8
        add     hl,de                           ; HL = x to the right of Sqrxz
        jr      CheckHorz                       ; Continue on the routine above

CheckUp:                                        ; Returns with Carry if not possible to move up
        ld      a,(y)
        dec     a                               ; A = possible new y coordinate
        jr      CheckVert                       ; Continue below

CheckDown:                                      ; Returns with Carry if not possible to move up
        ld      a,(y)
        add     a,8                             ; A = possible new y coordinate
        ld      bc,0                            ; If the routine ends at the return below, BC must be 0
        cp      64                              ; If the new Y coordinate >=64
        ret     nc                              ;  Then return with no carry, since you can fall forever
CheckVert:
        ld      hl,(x)                          ; HL,A = x,y below of Sqrxz
        call    GetBlock                        ; Find out what tile is there
        ld      b,a                             ; B will be the leftlower tile of Sqrxz
        ld      c,a                             ; C will be the rightlower tile of Sqrxz
        ld      a,(x)
        and     $07                             ; Check if X is between two tiles
        jr      z,CV_Even                       ;  If not, leftlower = rightlower
        ld      de,8                            ; Else add the address with 8 to get the address
        add     hl,de                           ; to the right
        ld      c,(hl)                          ; And load the tile in that address into C
CV_Even:
        ld      a,b
        call    CheckTile                       ; Check if foreground tile
        ret     c                               ;  If so, return with carry
        ld      a,c
        call    CheckTile                       ; Check if foreground tile
        ret                                     ; Return with carry or not carry

;------------------------------------------------
; drawSqrxz - draw Sqrxz to vbuf
;   input:  none
;   output: none
;------------------------------------------------
drawSqrxz:
        ld      hl,(x)
        ld      de,(XScr)
        or      a
        sbc     hl,de                           ; Now HL = x screen coordinate of Sqrxz
        ld      e,l                             ; E = x pos on screen
        ld      a,(y)
        ld      d,a                             ; D = y pos on screen
__sqrxz                 = $+1
        ld      hl,$000000
        ld      a,(sqrxzdir)
        or      a                               ; facing right?
        jp      z,FaceRight
        ld      bc,256*4
        add     hl,bc
FaceRight:
        ld      a,(sqrxzcnt)
        srl     a
        and     $03
        add     a,a
        ld      c,a
        ld      b,128
        mlt     bc
        add     hl,bc
        jp      drawTransparentSprite

;------------------------------------------------
; drawEnemies - draw enemies to vbuf
;   input:  none
;   output: none
;------------------------------------------------
drawEnemies:
        ld      ix,enemyTable                   ; IX -> enemy table
        ld      b,8                             ; Check all 8 positions in the enemy table
RepPutEnemy:
        push    bc
        ld      a,(ix)
        or      a                               ; Check if this position contains any enemy
        jp      z,PNext                         ;  If not, try next

        ld      h,(ix+3)
        ld      l,(ix+2)                        ; HL = enemy x position
        call    _SetHLUTo0
        ld      de,(XScr)
        sbc     hl,de                           ; HL = enemy x position on screen
        push    hl
        dec     a                               ; A = enemy number (0-4)
        ld      e,a
        ld      d,3
        mlt     de
        ld      hl,EnemyAddr                    ; HL -> table of enemy gfx pointers
        add     hl,de                           ; HL -> pointer to enemy gfx
        ld      hl,(hl)                         ; HL -> start of enemy graphics for current enemy
        cp      4                               ; Check if fish man
        jr      z,HH                            ;  Fishes gfx are stored the same way as hedgehog
        bit     0,a                             ; Check if Blob or Bat
        jr      z,EAnimAdd                      ;  Those are stored the same way
        dec     a                               ; Check if hedgehog
        jr      z,HH
        bit     0,(ix+1)                        ; Check direction for green man
        jr      z,EAnimAdd
        ld      de,256*4                        ; If faced left, add (256x4) to sprite address
        add     hl,de
        jr      EAnimAdd
HH:                                             ; If Fish or Hedgehog, this routine will be reached
        bit     0,(ix+1)
        jr      z,NoHHDirAdd
        ld      de,256*2                        ; If faced left, add (256x2) to sprite address
        add     hl,de
NoHHDirAdd:
        ld      a,(counter)
        bit     4,a
        jr      z,AddrFound
        ld      de,256                          ; Depending on counter, use the second sprite
        add     hl,de
        jr      AddrFound
EAnimAdd:                                       ; Reached here if Blob, Bat or Green Man
        ld      a,(counter)                     ; All those have four sprites/direction
        and     $18
        srl     a
        srl     a
        ld      e,a
        ld      d,128
        mlt     de
        add     hl,de
AddrFound:                                      ; When reached here, HL -> sprite to put
        pop     de                              ; if enemy is moving
        ld      b,e                             ; B = screen X coordinate to put enemy
        bit     7,d                             ; Check if X coordinate is negative
        jr      nz,XOk
        ld      a,d
        or      a                               ; Check if X>=256
        jr      nz,XNotOk                       ;  If so, X not OK
        bit     7,e                             ; Check if X>=128
        jr      z,XOk                           ;  If not, X is in range
XNotOk:
        ld      b,130                           ; If X>=128, set X to 130 (else wrap could occur)
XOk:
        ld      c,(ix+5)                        ; C = sprite Y coordinate
        ld      a,(ix+7)
        or      a                               ; Check if any special stuff is happening
        jr      z,PutEnemy                      ;  If not, put the sprite
Special:
        push    bc
        push    de
        inc     a
        ld      (ix+7),a                        ; Increase the spec counter
        ld      c,(ix)
        dec     c                               ; Check if blob
        jr      z,KillBlob                      ;  If so, the blob died this frame
        dec     c
        dec     c                               ; Check if bat
        jr      z,KillBat                       ;  Update bat animation
__greenmanHide          = $+1
        ld      hl,$000000                      ; Else it was the green man
        cp      4
        jr      c,SpecialPop                    ; If spec<4, show half hiding green man
        ld      de,256
        add     hl,de                           ; HL -> hiding green man
        cp      100
        jr      c,SpecialPop                    ; If spec<100, show hiding green man
        sbc     hl,de                           ; HL -> half hiding green man
        cp      120
        jr      c,SpecialPop                    ; If spec<120, show half hiding green man
        ld      (ix+7),0                        ; Else reset the spec counter - green man is in action
        ld      a,(ix+1)
        xor     1                               ; Change the green mans direction
        ld      (ix+1),a
        jr      SpecialPop
KillBat:
__batSplash             = $+1
        ld      hl,$000000                      ; HL -> bat splash graphics
        add     a,a
        add     a,a
        and     $F0
        ld      e,a
        ld      d,16
        mlt     de
        add     hl,de                           ; HL -> splash gfx this frame
        cp      48
        jr      c,SpecialPop                    ; If <48, show it
        ld      (ix),0                          ; Else the splash animation is over - remove enemy
        ld      de,256
        sbc     hl,de                           ; HL -> last splash gfx
        jr      SpecialPop
KillBlob:
__blobDead              = $+1
        ld      hl,$000000                      ; HL -> dead blob gfx
        cp      92                              ; keep dead blob around for 92 frames
        jr      c,SpecialPop
        ld      (ix),0                          ; Remove enemy from enemy table
SpecialPop:
        pop     de
        pop     bc
PutEnemy:
        ld      e,b
        ld      d,c
        call    drawTransparentSprite
PNext:
        ld      bc,8
        add     ix,bc                           ; IX -> next position in enemy table
        pop     bc
        dec     b
        jp      nz,RepPutEnemy                  ; Repeat until all enemies have been put to screen
        ret

ScrollLeft:
        ld      b,a
        ld      a,(XBlock)
        add     a,15
        ld      hl,LevelSize
        cp      (hl)                            ; Check if end of level reached
        ld      a,b
        ret     nc                              ; If so, return without scrolling
        ld      hl,(XScr)
        inc     hl
        ld      (XScr),hl                       ; Increase the scroll X coordinate
        ld      a,l
        ld      hl,XBlock
        and     $07                             ; Check if the leftmost block has changed
        ret     nz
        inc     (hl)
        ld      a,(hl)
        add     a,15
        ld      l,a
        ld      h,8
        mlt     hl
        ld      de,LevelPtr
        add     hl,de                           ; HL -> level data of blocks to scan for enemies
        ld      b,8
RepScrollScan:
        ld      a,(hl)
        bit     7,a                             ; Check if an enemy
        jr      z,SL_Next
        push    bc                              ; Prepare to add an enemy to the enemy table
        push    hl
        push    af
        ld      a,8
        sub     b
        add     a,a
        add     a,a
        add     a,a
        ld      c,a                             ; C = y coordinate of enemy
        ld      a,(XBlock)
        add     a,15
        ld      l,a
        ld      h,8
        mlt     hl                              ; HL = x coordinate of enemy
        pop     af
        and     $7F                             ; A = enemy type
        call    NewEnemy                        ; Add enemy to table
        cp      5                               ; Check if it was a fish
        ld      a,109                           ; If so, then the tile should be replaced with water
        jr      z,FillWater
        xor     a
FillWater:
        pop     hl
        ld      (hl),a
        pop     bc
SL_Next:
        inc     hl
        djnz    RepScrollScan
        ret

NewEnemy:                                       ; A - type, C - y coord, HL - x coord
        push    af
        push    bc
        push    de
        push    hl
        ld      hl,enemyTable                   ; HL -> enemy table
        ld      b,8                             ; Check 8 positions (max)
        ld      d,a
SearchFreeEntry:
        ld      a,(hl)
        or      a                               ; Check if position busy (type>0)
        jr      nz,NextEnt                      ;  Yes it was, try next entry
        ld      (hl),d                          ; Store the enemy type
        inc     hl
        ld      (hl),1                          ; Followed by the direction (always 1=Left)
        pop     de                              ; Pop the x coordinate
        inc     hl
        ld      (hl),e                          ; Store the LSB of it
        inc     hl
        ld      (hl),d                          ; Followed by the MSB
        inc     hl
        call    _SetAToDEU
        ld      (hl),a                          ; Followed by the upper
        inc     hl
        ld      (hl),c                          ; Then store the y coordinate
        inc     hl
        ld      (hl),0                          ; Clear the fall counter
        inc     hl
        ld      (hl),0                          ; And last the 'spec' byte
        jr      NE_Done
NextEnt:
        push    de
        ld      de,8
        add     hl,de                           ; Add with 7 to reach next entry
        pop     de
        djnz    SearchFreeEntry                 ; And try to find a new spot if possible
        pop     hl
NE_Done:
        pop     de
        pop     bc
        pop     af
        ret

LoadLevel:
        ld      hl,0                            ; Prepare to clear a lot of variables
        ld      (XScr),hl
        ld      (jump),hl
        ld      (dead),hl
        ld      (sqrxzcnt),hl
        ld      (portc),hl
        ld      (x),hl
        xor     a
        ld      (animptr),a
        ld      (animcnt),a
        ld      (deadcnt),a
        ld      (y),a
        ld      (XBlock),a
        ld      hl,AnimTable
        ld      bc,40-1
        ld      (hl),$FF
        call    FillChar                        ; Clear the animation table (with $FF)
        ld      hl,enemyTable
        ld      bc,64-1
        call    ClearMem                        ; Clear enemy table

        ld      hl,LevelPtr
        ld      a,(LevelSize)
        ld      c,a
        ld      a,(start_x)
        or      a                               ; Check if a lever has been pulled
        jr      z,NoLeverPulled
        push    af                              ; If so, the left edge of the screen should be
        sub     2                               ; start_x-2
        jr      nc,NoProbL
        xor     a                               ; But not less than 0
NoProbL:
        ld      b,a
        add     a,14
        cp      c                               ; Check so the right edge won't be beyond size of level
        ld      a,b
        jr      c,NoProbR
        ld      a,c
        sub     15                              ; Fix if necessary
NoProbR:
        ld      (XBlock),a                      ; Store the start screen location in XBlock
        ex      de,hl
        ld      l,a
        ld      h,8
        mlt     hl
        ld      (XScr),hl                       ; and in XScr as well
        ex      de,hl
        add     hl,de
        pop     af
        push    hl
        ld      l,a
        ld      h,8
        mlt     hl
        ld      (x),hl
        pop     hl
        ld      a,(start_y)
        add     a,a
        add     a,a
        add     a,a
        ld      (y),a                           ; Set Sqrxz Y coordinate according to start_y
NoLeverPulled:
        ld      bc,0                            ; B,C = current x,y block coordinates
RepScanLevel:
        ld      a,(hl)
        bit     7,a                             ; Check if tile to put is an enemy
        jr      z,PlainTile
        push    hl
        and     $7F
        jr      nz,EnemySt                      ; If it was an enemy, put it out
        ld      a,(start_x)
        or      a
        jr      nz,Restore                      ; If lever pulled, don't update Sqrxz x,y coordinates
        ld      a,c
        ld      (y),a
        ld hl,0 \ ld l,b
        ld      (x),hl                          ; Else store Sqrxz start coordinates
        jr      Restore
EnemySt:
        ld hl,0 \ ld l,b
        ld      de,(XScr)
        add     hl,de
        call    NewEnemy                        ; Add enemy to enemytable
        cp      5                               ; Check if it was a fish
        jr      nz,Restore
        ld      a,109                           ; If it was, water should be there as background
        jr      FRestore
Restore:
        xor     a
FRestore:
        pop     hl
        ld      (hl),a                          ; Change the tile to space (or water if fish)
PlainTile:
        inc     hl
        ld      a,c
        add     a,8                             ; Increase the Y coordinate with 8
        ld      c,a
        cp      64
        jr      nz,RepScanLevel                 ; If not 64, continue showing level
        ld      c,0                             ; Else reset Y coordinate
        ld      a,b
        add     a,8                             ; And add the X coordinate
        ld      b,a
        cp      136
        jr      nz,RepScanLevel                 ; If less than 120, continue with the level
        ld      hl,479
        ld      (waterbar),hl                   ; Set water bar max value
        ret

ChooseWorld:                                    ; This routine lets the user choose world
        ld      hl,noWorlds
        ld      bc,768+5-1
        call    ClearMem                        ; Clear list of worlds found
        ld      ix,worlds
        ld      hl,(progptr)                    ; Prepare scanning variables
SearchWorlds:
        ld      de,(ptemp)
        call    _cphlde
        jr      z,SearchDone
        push    hl
        ld      a,(hl)
        cp      AppVarObj
        jr      z,FileFound
        cp      ProtProgObj
        jr      z,FileFound
ContinueSearch:
        pop     hl
        ld      bc,-6
        add     hl,bc
        ld      b,(hl)
        inc     b
SkipName:
        dec     hl
        djnz    SkipName
        jr      SearchWorlds
FileFound:
        call    getDataPtr
        ld      a,(hl)
        cp      $02                             ; Check first header byte
        jr      nz,ContinueSearch               ; If not 2 (indicating Sqrxz level), continue search
        inc     hl
        ld      a,(hl)
        cp      $02                             ; Check second header byte
        jr      nc,ContinueSearch               ; If not 0 or 1, no Sqrxz level - continue search
        pop hl \ push hl
        ld      (ix),l                          ; Else store the VAT address to that level (LSB order)
        ld      (ix+1),h
        call    _SetAToHLU
        ld      (ix+2),a
        inc     ix
        inc     ix
        inc     ix
        ld      hl,noWorlds
        inc     (hl)                            ; Increase number of worlds found
        jr      ContinueSearch                  ; Continue search
SearchDone:
        ld      a,(noWorlds)
        or      a                               ; Check if no worlds found
        jr      nz,ShowWorlds
        ld      a,COL_RED
        ld      (__dcColour),a
        ld      de,100
        ld      c,100
        ld      hl,NoWorldsTxt
        call    drawString
        call    vramFlip
        call    waitKey
        jp      Abort
ShowWorlds:
        call    vbufClear
        ; fill top of vbuf
        ld      de,0
        ld      bc,320
        ld      hl,11*256+3
        ld      a,COL_BLUE
        call    fillRect
        ; show world choice
        ld      hl,fontArcade
        call    loadFont
        ld      de,80
        ld      c,5
        ld      hl,WorldChoice
        call    drawString
        ld      a,22
        ld      (__swY),a
        ld      hl,worlds
        ld      de,(top)                        ; Top = top of list
        ld      d,3
        mlt     de
        add     hl,de                           ; HL -> pointers to worlds
        ld      b,16                            ; Max 16 worlds to be shown at the same time
ShowWorld:
        call    _LoadDEInd                      ; DE = pointer to VAT entry
        ld      a,e
        or      a
        jr      z,EndOfList                     ; If A=0, end of list reached
        push    bc
        push    hl
        ex      de,hl
        call    getDataPtr
        inc     hl                              ; HL => compressed flag
        ld      de,OP1
        ld      bc,33
        push    de
        ldir
        pop     ix
        inc     ix                              ; Skip world type
        inc     ix                              ; Skip first byte of no levels (always 0)
        ld      a,(ix)                          ; A = number of levels in this world
        inc     ix
        push    af
        ld      de,10
__swY                   = $+1
        ld      c,$00
        push    bc
        push ix \ pop hl
        call    drawString                      ; Show world name
        pop     bc                              ; C = y pos
        ld      de,295
        pop     af
        ld hl,0 \ ld l,a
        ld      b,2
        call    drawHL                          ; And display number of levels
        ld      hl,__swY
        ld      a,(hl)
        add     a,11                            ; Increase the Y coordinate with 11
        ld      (hl),a
        pop     hl
        pop     bc
        djnz    ShowWorld        ; Repeat showing the list
EndOfList:
        ld      a,16
        sub     b                               ; A = number of worlds in current list
        ld      (noItems),a
        call    vramFlip
        ld      a,(curItem)
UpdateChoice:
        ld      (curItem),a
        ld      c,COL_GREEN
        call    MarkItem                        ; Mark the current selection
Choose:
        call    waitKey
        cp      GK_CLEAR
        jr      z,Abort
        cp      GK_MODE
        jp      z,NextPage
        cp      GK_2ND
        jp      z,Select
        cp      GK_ENTER
        jp      z,Select
        cp      GK_DOWN
        jr      z,CursorDown
        cp      GK_UP
        jr      nz,Choose
        ld      a,(curItem)
        ld      c,COL_BLACK
        call    MarkItem                        ; Unmark the current selected world
        or      a                               ; Check if at top of list
        jr      nz,NoWarp                       ; If not, decrease
        ld      a,(noItems)                     ; Else A=bottom of list+1
NoWarp:
        dec     a
        jr      UpdateChoice
CursorDown:
        ld      a,(curItem)
        ld      c,COL_BLACK
        call    MarkItem                        ; Unmark the current selected world
        inc     a
        ld      hl,noItems
        cp      (hl)                            ; Check if bottom of list passed
        jr      nz,UpdateChoice
        xor     a                               ; If so, current world is 0
        jr      UpdateChoice
Abort:
        pop     hl
QuickQuit:
        ld      hl,vram
        ld      (mpLcdBase),hl
        call    vram16bpp
        call    _clrlcdfull
        call    _homeup
        call    _drawstatusbar
        ei
        ret

TeacherKey:
        ld      hl,fileSave
        call    _Mov9toOp1
        call    _ChkFindSym
        jr      c,CreateSave
        call    _DelVarArc
CreateSave:
        ld      hl,SAVE_FILE_REQD
        call    _EnoughMem
        jp      c,PauseResume
        ld      hl,SAVE_FILE_DATA
        push    hl
        call    _CreateAppVar
        inc de \ inc de
        ld      hl,vars
        pop     bc
        ldir                                    ; write variables and level data
        ld      hl,fileSave
        call    _Mov9toOp1
        call    _Arc_Unarc
        jp      QuickQuit

TeacherKeyResume:
        push    hl
        push    de
        call    _ChkInRam
        ex      de,hl
        jr      z,LoadSave
        push    hl
        pop     ix
        ld      a,10
        add     a,(ix+9)
        ld de,0 \ ld e,a
        add     hl,de
LoadSave:
        inc hl \ inc hl                         ; skip size bytes
        ld      de,vars
        ld      bc,SAVE_FILE_DATA
        ldir                                    ; read variables and level data
        pop     de
        pop     hl
        call    _DelVarArc                      ; delete save state file
        ld      hl,fileWorld
        call    _Mov9toOp1
        call    _ChkFindSym
        jp      c,Start                         ; if world file is no longer on the calculator, can't restore save state
        call    getDataPtr
        inc     hl
        ld      (pWorld),hl                     ; save pointer to compressed flag of world data
        call    loadGamePalette
        call    vramClear
        call    GetReady
        ld      bc,300000
        call    waitBC
        call    vramClear
        call    drawStats                       ; write stats text
        call    vramFlip
        call    drawStats                       ; on both vbufs
        ld      hl,fontLarge
        call    loadFont                        ; load large font (will be used during MainLoop)
        ld      a,2
        ld      (statusCnt),a                   ; update status bar text for first 2 frames
        jp      PauseResume

NextPage:
        ld      a,(top)
        add     a,16                            ; Add top of list with 16
        ld      hl,noWorlds
        cp      (hl)                            ; Check if not that many worlds
        jr      c,ShowW
        xor     a                               ; If so, start from page 0
ShowW:
        ld      (top),a
        xor     a
        ld      (curItem),a
        jp      ShowWorlds

Select:
        ld      a,(top)
        ld      hl,curItem
        add     a,(hl)
        ld      e,a
        ld      d,3
        mlt     de
        ld      hl,worlds
        add     hl,de                           ; HL -> pointer to current world
        call    _LoadDEInd                      ; DE = VAT entry of world to play
        ex      de,hl
        push    hl
        ld      de,fileWorld                    ; save world file information in case teacher key is pressed
        ld      a,(hl)
        ld      (de),a                          ; save data type
        inc     de
        ld      bc,-6
        add     hl,bc                           ; HL => VAT NL
        ld bc,0 \ ld b,(hl)                     ; B = name length
        dec     hl                              ; HL => VAT name
SelectCopy:
        ld      a,(hl)
        ld      (de),a                          ; copy letters
        dec hl \ inc de
        djnz    SelectCopy
        xor     a
        ld      (de),a                          ; write NULL terminator
        pop     hl
        call    getDataPtr
        inc     hl
        ld      (pWorld),hl                     ; save pointer to compressed flag
        ret

MarkItem:
        push    af
        ld      h,11
        ld      l,a
        mlt     hl
        ld      h,160
        mlt     hl
        add     hl,hl
        ld      de,(mpLcdBase)
        add     hl,de
        ld      de,20*320
        add     hl,de
        push    hl
        ld      de,10*320
        add     hl,de
        pop de \ push de
        ld      b,160
        ld      a,c
MarkItemH:
        ld (de),a \ inc de
        ld (de),a \ inc de
        ld (hl),a \ inc hl
        ld (hl),a \ inc hl
        djnz    MarkItemH
        pop     de
        pop     af
        ret

missingSpritesFile:
        ld      hl,fileSprites
        jr      missingFile

missingTitleFile:
        ld      hl,fileTitle
missingFile:
        push    hl
        ld      hl,fontArcade
        call    loadFont
        call    vramClear
        ld      a,COL_RED
        ld      (__dcColour),a
        ld      hl,missingTxt
        ld      de,84
        ld      c,110
        call    drawString
        pop     hl
        ld      de,188
        ld      c,110
        call    drawString
        call    vramFlip
        call    waitKey
        jp      QuickQuit

ClearMem:
        ld      (hl),0                          ; Clears BC+1 chars from HL
FillChar:                                       ; Fills BC+1 chars from HL with (HL)
        push hl \ pop de
        inc     de
        ldir
        ret

WaitEnter:
        call    waitKey
        cp      GK_ENTER
        jr      nz,WaitEnter
        ret

GetReady:
        call    vramClear
        ld      hl,fontLarge
        call    loadFont
        ld      hl,GetReadyTxt
        ld      de,82
        ld      c,100
        call    drawString
        jp      vramFlip

;------------------------------------------------
; loadGamePalette - load the game palette
;   input:  none
;   output: none
;------------------------------------------------
loadGamePalette:
__palSprites            = $+1
        ld      hl,$000000
        ld      bc,PAL_SPRITES_SIZE
        ; fall through to loadPalette

;------------------------------------------------
; loadPalette - load a palette
;   input:  HL -> start of palette data
;           BC = size
;   output: none
;------------------------------------------------
loadPalette:
        ld      de,mpLcdPalette
        ldir
        ret

;------------------------------------------------
; vram8bpp - set up LCD for 8bpp
;   input:  none
;   output: none
;------------------------------------------------
vram8bpp:
        ld      a,$27
        ld      (mpLcdCtrl),a                   ; set 8bpp
        ld      hl,%0000000000000011
        ld      (mpLcdPalette+512-2),hl         ; set last colour of palette for game background
        ret
        
;------------------------------------------------
; vram16bpp - return LCD to default 16bpp
;   input:  none
;   output: none
;------------------------------------------------
vram16bpp:
        ld      a,$2D
        ld      (mpLcdCtrl),a                   ; set 16bpp  5:6:5
        ld      l,mpLcdIcr&$FF
        ld      (hl),4
        ret

;------------------------------------------------
; vbufClear - clear (pVbuf) (in 8bpp mode)
;   input:  none
;   output: A = 0
;           HL => last byte that was cleared
;------------------------------------------------
vbufClear:
        xor     a

;------------------------------------------------
; vbufFill - fill (pVbuf) (in 8bpp mode)
;   input:  A = byte to fill with
;   output: A = byte that was used
;           HL => last byte that was written
;------------------------------------------------
vbufFill:
        ld      hl,(pVbuf)
        push hl \ pop de \ inc de
        ld      (hl),a
        ld      bc,320*240-1
        ldir
        ret

;------------------------------------------------
; vramClear - clear entire vram
;   input:  none
;   output: none
;------------------------------------------------
vramClear:
        ld      hl,vram
        ld      de,vram+1
        ld      bc,320*240*2-1
        ld      (hl),0
        ldir
        ret

;------------------------------------------------
; vramFlip - flip vram between vbuf1/vbuf2
;   input:  none
;   output: none
;------------------------------------------------
vramFlip:
        ld      de,vbuf1
        ld      hl,(mpLcdBase)
        or      a
        sbc     hl,de
        add     hl,de
        jr      nz,vfSetBuffer
        ld      de,vbuf2
vfSetBuffer:
        ld      (pVbuf),hl
        ld      bc,20520
        add     hl,bc
        ld      (pVbufWin),hl
        ld      (mpLcdBase),de
        ld	    hl,mpLcdIcr
        set	    2,(hl)
        ld	    l,mpLcdRis&$FF
vfWait:
        bit     2,(hl)
        jr      z,vfWait
        ret

;------------------------------------------------
; drawSprite - draw a 16x16 8bpp sprite to vbuf (no clipping)
;   input:  IX => sprite
;           DE = x position
;           L = y position
;   output: none
;------------------------------------------------
drawSprite:
        ld      h,160
        mlt     hl
        add     hl,hl
        add     hl,de
        ld      de,(pVbuf)
        add     hl,de
        ld      c,16
dsRow:
        ld      b,16
dsCol:
        ld      a,(ix)
        cp      $FF
        jr      z,dsSkip
        ld      (hl),a
dsSkip:
        inc     ix
        inc     hl
        djnz    dsCol
        ld      de,320-16
        add     hl,de
        dec     c
        jr      nz,dsRow
        ret

;------------------------------------------------
; drawClippedSprite - draw a 16x16 8bpp sprite to a 240x128 window of vbuf (with clipping)
;   input:  HL => sprite
;           E = x pos / 2
;           D = y pos / 2
;   output: none
;------------------------------------------------
drawClippedSprite:
        ; y-clipping
        ld      a,d
        bit     7,a
        jr      z,dcsCheckBottom
        cp      -7
        ret     c
        neg
        ld      c,a
        ld      b,32                            ; 32 bytes of sprite data to skip per row clipped
        mlt     bc
        add     hl,bc
        ld      a,d
        add     a,8
        ld      d,0
        jr      dcsSetRows
dcsCheckBottom:
        cp      64-7
        ld      a,8
        jr      c,dcsSetRows
        ld      a,d
        sub     64
        ret     nc
        neg
dcsSetRows:
        add     a,a
        ld      (__dcsRows),a
        ; x-clipping
        ld      a,e
        bit     7,a
        jr      z,dcsCheckRight
        cp      -7
        ret     c
        neg
        add     a,a
        ld bc,0 \ ld c,a                        ; skip 2 bytes per for each column clipped
        add     hl,bc
        ld      a,e
        add     a,8
        ld      e,0
        jr      dcsSetCols
dcsCheckRight:
        cp      120-7
        ld      a,8
        jr      c,dcsSetCols
        ld      a,e
        sub     120
        ret     nc
        neg
dcsSetCols:
        add     a,a
        ld      (__dcsCols),a
        sub     16
        neg
        ld      (__dcsNext),a
        ; prepare to draw
        ex      de,hl                           ; DE => sprite, H = y pos / 2, L = x pos / 2
        sla     h                               ; H = y pos
        sla     l                               ; L = x pos
        ld bc,0 \ ld c,l
        ld      l,160
        mlt     hl
        add     hl,hl
        add     hl,bc
        ld      bc,(pVbufWin)
        add     hl,bc                           ; HL => location on vbuf
        push    iy
        push    hl
        ex      de,hl                           ; HL => sprite
        pop     iy                              ; IY => vbuf
        ld      bc,0                            ; ensure that BCU is 0
__dcsRows               = $+1
        ld      a,16
dcsRow:
__dcsCols               = $+1
        ld      c,16
        lea     de,iy
        ldir
__dcsNext               = $+1
        ld      c,16
        add     hl,bc
        ld      de,320
        add     iy,de
        dec     a
        jr      nz,dcsRow
        pop     iy
        ret

;------------------------------------------------
; drawTransparentSprite - draw a 16x16 8bpp sprite to a 240x128 window of vbuf (with clipping & transparency)
;                         almost identical to drawClippedSprite but slower to allow for transparency
;   input:  HL => sprite
;           E = x pos / 2
;           D = y pos / 2
;   output: none
;------------------------------------------------
drawTransparentSprite:
        ; y-clipping
        ld      a,d
        bit     7,a
        jr      z,dtsCheckBottom
        cp      -7
        ret     c
        neg
        ld      c,a
        ld      b,32                            ; 32 bytes of sprite data to skip per row clipped
        mlt     bc
        add     hl,bc
        ld      a,d
        add     a,8
        ld      d,0
        jr      dtsSetRows
dtsCheckBottom:
        cp      64-7
        ld      a,8
        jr      c,dtsSetRows
        ld      a,d
        sub     64
        ret     nc
        neg
dtsSetRows:
        add     a,a
        ld      (__dtsRows),a
        ; x-clipping
        ld      a,e
        bit     7,a
        jr      z,dtsCheckRight
        cp      -7
        ret     c
        neg
        add     a,a
        ld bc,0 \ ld c,a                        ; skip 2 bytes per for each column clipped
        add     hl,bc
        ld      a,e
        add     a,8
        ld      e,0
        jr      dtsSetCols
dtsCheckRight:
        cp      120-7
        ld      a,8
        jr      c,dtsSetCols
        ld      a,e
        sub     120
        ret     nc
        neg
dtsSetCols:
        ld      (__dtsCols),a
        sub     8
        neg
        add     a,a
        ld      (__dtsNext),a
        ; prepare to draw
        ex      de,hl                           ; DE => sprite, H = y pos / 2, L = x pos / 2
        sla     h                               ; H = y pos
        sla     l                               ; L = x pos
        ld bc,0 \ ld c,l
        ld      l,160
        mlt     hl
        add     hl,hl
        add     hl,bc
        ld      bc,(pVbufWin)
        add     hl,bc                           ; HL => location on vbuf
        push    iy
        push    hl
        ex      de,hl                           ; HL => sprite
        pop     iy                              ; IY => vbuf
        ld      bc,0                            ; ensure that BCU is 0
        push    ix
__dtsRows               = $+2
        ld      ixh,16
dtsRow:
__dtsCols               = $+1
        ld      b,8
        lea     de,iy
dtsCol:
        ld      a,(hl)
        cp      255
        jr      z,dtsSkip1
        ld      (de),a
dtsSkip1:
        inc     de
        inc     hl
        ld      a,(hl)
        cp      255
        jr      z,dtsSkip2
        ld      (de),a
dtsSkip2:
        inc     de
        inc     hl
        djnz    dtsCol
__dtsNext               = $+1
        ld      c,16
        add     hl,bc
        ld      de,320
        add     iy,de
        dec     ixh
        jr      nz,dtsRow
        pop     ix
        pop     iy
        ret

;------------------------------------------------
; drawBG - draw the game background 15x8 tiles (or 16x8 with clipping depending on x-position)
;   input:  none
;   output: none
;------------------------------------------------
drawBG:
        ld      hl,(XBlock)
        ld      h,8
        mlt     hl
        ld      de,LevelPtr
        add     hl,de                           ; HL => column of level data
        push    hl
        pop     ix
        ld      a,(XScr)
        and     %00000111
        or      a                               ; is the screen aligned with a tile?
        ld      c,15                            ; if so, 15 columns to draw
        jr      z,dbgXOffset
        ld      c,16
dbgXOffset:
        neg
        ld      e,a                             ; E = x position to start from {-7=>0}
dbgCol:
        ld      b,8                             ; 8 rows per column
        ld      d,0                             ; D = y position
dbgRow:
        push    bc
        push    de
        ld      l,(ix)
        ld      h,128
        mlt     hl
        add     hl,hl
        ld      bc,sprites
        add     hl,bc                           ; HL => sprite
        call    drawClippedSprite
        pop     de
        ld      a,8
        add     a,d
        ld      d,a                             ; D = y position of next tile
        inc     ix
        pop     bc
        djnz    dbgRow
        ld      a,8
        add     a,e
        ld      e,a                             ; E = x position of next column
        dec     c
        jr      nz,dbgCol
        ret

;------------------------------------------------
; drawStats - draw stats background (will be called once for each vbuf)
;   input:  none
;   output: none
;------------------------------------------------
drawStats:
        ld      hl,fontArcade
        call    loadFont                        ; load arcade font
        ld      a,COL_PALEBLUE
        ld      (__dcColour),a
        ld      hl,(worldN)
        ld      de,10
        ld      c,4
        call    drawString                      ; draw world name
        inc     hl
        push    hl
        ld      hl,WorldAuthor
        ld      de,10
        ld      c,14
        call    drawString                      ; "MADE BY"
        pop     hl
        ld      de,74
        ld      c,14
        call    drawString                      ; draw author
        ld      a,COL_WHITE
        ld      (__dcColour),a
        ld      hl,fontSmall
        call    loadFont                        ; load small font
        ld      hl,StatsTxt
        ld      de,20
        ld      c,196
        call    drawString                      ; "TIME:"
        ld      de,28
        ld      c,206
        call    drawString                      ; "AIR:"
        ld      ix,(__sqrxz)
        ld      de,16
        ld      l,220
        call    drawSprite                      ; sqrxz sprite
__sausage               = $+2
        ld      ix,$000000
        ld      de,130
        ld      l,220
        call    drawSprite                      ; sausage sprite
__lever                 = $+2
        ld      ix,$000000
        ld      de,254
        ld      l,220
        jp      drawSprite                      ; lever sprite

;------------------------------------------------
; updateBars - update bars at bottom of lcd
;   input:  none
;   output: none
;------------------------------------------------
updateBars:
        ; draw time bar
        ld      bc,(timeleft)
        srl b \ rr c
        srl b \ rr c
        srl b \ rr c
        srl b \ rr c
        srl b \ rr c
        srl b \ rr c                            ; / 64
        ld      hl,6*256+197
        push    hl
        push    bc
        ld      de,64
        push    de
        ld      a,COL_TIME_LIGHT
        call    fillRect
        pop     de
        pop     hl
        ld      a,240
        sub     l
        ld bc,0 \ ld c,a
        add     hl,de
        ex      de,hl
        pop     hl
        ld      a,COL_TIME_DARK
        call    fillRect
        ; draw air bar
        ld      bc,(waterbar)
        srl     b
        rr      c                               ; BC = half waterbar
        ld      hl,6*256+207
        push    hl
        push    bc
        ld      de,64
        push    de
        ld      a,COL_AIR_LIGHT
        call    fillRect
        pop     de
        pop     hl
        ld      a,240
        sub     l
        ld bc,0 \ ld c,a
        add     hl,de
        ex      de,hl
        pop     hl
        ld      a,COL_AIR_DARK
        jp      fillRect

;------------------------------------------------
; updateStats - update status bar text if required
;   input:  none
;   output: none
;------------------------------------------------
updateStats:
        ld      hl,statusCnt
        ld      a,(hl)
        or      a
        ret     z
        dec     (hl)
        bit     7,(hl)
        jr      z,usContinue
        ld      (hl),0                          ; reset if negative
usContinue:
        ld      a,'X'
        ld      de,40
        ld      c,220
        call    drawChar                        ; "X" next to Sqrxz
        ld      a,'X'
        ld      de,154
        ld      c,220
        call    drawChar                        ; and again next to Sausage
        ld      a,OP_SCF
        ld      (__dhlFlag),a                   ; keep leading zeros
        ld      (__dcFlag),a                    ; remove font transparency
        ld      a,(lives)
        ld      b,2
        ld      de,64
        ld      c,220
        call    drawA                           ; show lives
        ld      a,(sausages)
        ld      b,2
        ld      de,178
        ld      c,220
        call    drawA                           ; show sausages
        ld      a,(lvl)
        inc     a
        ld      b,2
        ld      de,276
        ld      c,220
        call    drawA                           ; show level
        ld      a,OP_OR_A
        ld      (__dhlFlag),a
        ld      (__dcFlag),a
        ret

;------------------------------------------------
; drawImage - draw an 8bpp image to vbuf
;   input:  IX => image data (width, height, pixels)
;           DE = x position
;           L = y position
;   output: none
;------------------------------------------------
drawImage:
        ld      h,160
        mlt     hl
        add     hl,hl
        add     hl,de
        ld      de,(pVbuf)
        add     hl,de
        ld      b,(ix)
        ld      c,(ix+1)
        inc ix \ inc ix
diRow:
        push    bc
        push    hl
diCol:
        ld a,(ix) \ inc ix
        ld (hl),a \ inc hl
        djnz    diCol
        pop     hl
        ld      de,320
        add     hl,de
        pop     bc
        dec     c
        jr      nz,diRow
        ret

;------------------------------------------------
; fillRect - fill a rectangle on vbuf
;   input:  DE = top left x pos
;           BC = width
;           L = top left y pos
;           H = height
;           A = colour id
;   output: none
;------------------------------------------------
fillRect:
        ld      (__frColour),a
        ld      a,b
        or      c
        ret     z                               ; leave if width is zero
        ld      a,h
        or      a
        ret     z                               ; leave if height is zero
        push    hl
        push    de
        ld      h,160
        mlt     hl
        add     hl,hl
        ld      de,(pVbuf)
        add     hl,de
        pop     de
        add     hl,de                           ; HL = vbuf ptr (top-left of rect)
        pop     de                              ; D = height, BC = width
__frColour              = $+1
        ld      e,$00                           ; E = colour id
frRow:
        push    bc
        push    hl
frCol:
        ld (hl),e \ inc hl
        dec     bc
        ld      a,c
        or      b
        jr      nz,frCol
        pop     hl
        ld      bc,320
        add     hl,bc
        pop     bc
        dec     d
        jr      nz,frRow
        ret

;------------------------------------------------
; loadFont - load font
;   input:  HL => font data
;               texture flag (either "or a" or "scf")
;               sprite width
;               sprite height
;               character width
;               packed font sprite data
;   output: none
;------------------------------------------------
loadFont:
        ld      a,(hl)                                      ; texture flag instruction
        ld      (__dcTexture),a
        inc     hl
        ld      a,(hl)                                      ; sprite width
        ld      e,a
        ld      (__dcWidth),a
        inc     hl
        ld      a,(hl)                                      ; sprite height
        ld      d,a
        ld      (__dcHeight),a
        inc     hl
        mlt     de
        ld      a,e
        ld      (__dcSize),a                                ; sprite data size
        ld      a,(hl)                                      ; character width
        ld      (__dstWidth),a
        inc     hl
        call    _loadDEInd
        ld      (__dcTable),de                              ; font table
        ld      de,sprChars
        ld      ix,huffLib
        jp      HuffExtr                                    ; extract font sprite data

;------------------------------------------------
; drawString - draw a string to vBuf
;   input:  HL => string
;           DE = x pos
;           C = y pos
;   output: HL => data after string
;------------------------------------------------
drawString:
        ld      a,(hl)
        inc     hl
        or      a                                           ; null terminator?
        ret     z
        push    hl
        push    de
        push    bc
        call    drawChar
        pop     bc
        pop     de
__dstWidth              = $+1
        ld      hl,0
        add     hl,de
        ex      de,hl
        pop     hl
        jr      drawString
        
;------------------------------------------------
; drawChar - draw a font character to vBuf
;   input:  A = ascii character
;           DE = x pos
;           C = y pos
;   output: none
;------------------------------------------------
drawChar:
        push    de
        push    bc
        sub     $20
        ld bc,0 \ ld c,a
__dcTable               = $+1
        ld      hl,$0000
        add     hl,bc
        ld      c,(hl)
__dcSize                = $+1
        ld      b,0
        mlt     bc
        ld      ix,sprChars
        add     ix,bc
        pop     hl
        ld      h,160
        mlt     hl
        add     hl,hl
        ld      de,(pVbuf)
        add     hl,de
        pop     de
        add     hl,de                           ; HL => vbuf location
__dcHeight              = $+1
        ld      c,0
dcRow:
        push    hl
__dcWidth               = $+1
        ld      b,0
dcCol:
__dcFlag                = $
        or      a
        ld      a,(ix)
        jr      c,dcNoSkip
        or      a
        jr      z,dcSkip
dcNoSkip:
__dcTexture             = $
        nop
        jr      c,dcWrite
__dcColour              = $+1
        ld      a,COL_WHITE
dcWrite:
        ld      (hl),a
dcSkip:
        inc ix \ inc hl
        djnz    dcCol
        pop     hl
        ld      de,320
        add     hl,de
        dec     c
        jr      nz,dcRow
        ret

;------------------------------------------------
; drawA - same as drawHL but with 8-bit value in A
;   input:  A = value to show
;           B = # chars to show
;           DE = x pos
;           C = y pos
;   output: none
;------------------------------------------------
drawA:
        ld hl,0 \ ld l,a
        ; fall through to drawHL

;------------------------------------------------
; drawHL - draw value of HL right-aligned using specified number of characters (left will be padded with either spaces or zeros)
;   input:  HL = value to show
;           B = # chars to show
;           DE = x pos
;           C = y pos
;   output: none
;------------------------------------------------
drawHL:
        push    de
        push    bc
        ld      de,strBuffer
drawHLIni:
        inc     de
        djnz    drawHLIni
        pop bc \ push bc
        xor     a
        ld      (de),a                          ; write null terminator
drawHLMake:
        dec     de
        call    _divHLby10_s
        add     a,'0'                           ; A = character to display
        ld      (de),a
        djnz    drawHLMake
        push de \ pop ix                        ; IX => start of string
__dhlFlag               = $
        or      a
        jr      c,drawHLReady
        ; check string and elimate leading zeros with spaces
        pop bc \ push bc \ dec b                ; get original B counter a decrement to calc max number of zeros to elimate
drawHLCheck:
        ld      a,(ix)
        cp      '0'
        jr      nz,drawHLReady
        ld      (ix),' '
        inc     ix
        djnz    drawHLCheck
drawHLReady:
        ld      hl,strBuffer
        pop     bc
        pop     de
        jp      drawString

;------------------------------------------------
; waitBC - wait for a period of time
;   input:  BC = delay
;   output: none
;------------------------------------------------
waitBC:
        push bc \ pop bc
        dec     bc
        call    _setAToBCU
        or      b
        or      c
        jr      nz,waitBC
        ret

;------------------------------------------------
; waitKey - wait until a key is pressed
;   input:  none
;   output: A = keycode
;------------------------------------------------
waitKey:
        call    _getcsc                         ; A = key pressed
        or      a                               ; check if 0
        jr      z,waitKey                       ; if so, no key was pressed
        ret

;------------------------------------------------
; waitModeUp - wait until Mode is released
;   input:  none
;   output: none
;------------------------------------------------
waitModeUp:
        call    keyScan
        ld      a,(kbdG1)
        bit     kbitMode,a
        jr      nz,waitModeUp
        ret

;------------------------------------------------
; keyScan - perform a keyscan
;   input:  none
;   output: none
;------------------------------------------------
keyScan:
        di
        ld      hl,DI_Mode
        ld      (hl),$02
        xor     a
ksWait:
        cp      (hl)
        jr      nz,ksWait
        ret

;------------------------------------------------
; findFile - find a data appvar file
;   input:  IX => search string
;   output: HL => data location
;           CA = file not found
;------------------------------------------------
findFile:
        ld      hl,(progptr)
ffNext:
        ld      de,(pTemp)
        call    _cphlde
        jr      nz,ffType
        scf
        ret
ffType:
        ld      a,(hl)
        push    hl
        cp      AppVarObj
        jr      z,ffData
ffSkip:
        pop     hl                              ; HL => name length
        ld      bc,-6
        add     hl,bc                           ; HL => name length
        ld      b,(hl)
        inc     b
ffsLoop:
        dec     hl
        djnz    ffsLoop
        jr      ffNext
ffData:
        dec hl \ dec hl \ dec hl
        ld      e,(hl)
        dec     hl
        ld      d,(hl)
        dec     hl
        ld      a,(hl)
        call    _setDEUToA                      ; save address in DE
        ex      de,hl
        cp      $D0                             ; is file in RAM?
        jr      nc,ffCheck
        push    ix
        push    hl
        pop     ix
        ld      a,10
        add     a,(ix+9)
        ld de,0 \ ld e,a
        add     hl,de
        pop     ix
ffCheck:
        inc hl \ inc hl                         ; skip size bytes
        lea     de,ix                           ; DE => detection string
ffcLoop:
        ld      a,(de)
        or      a                               ; NULL terminator?
        jr      z,ffFound
        cp      (hl)
        inc     hl
        inc     de
        jr      z,ffcLoop
        jr      ffSkip                          ; didn't match
ffFound:
        pop     de                              ; clear stack
        ret                                     ; carry is already clear from "or a" above and HL is pointing to start of file data

;------------------------------------------------
; getDataPtr - get a pointer to the variable data from the VAT entry
;   input:  HL => VAT entry
;   output: HL => data
;------------------------------------------------
getDataPtr:
        dec     hl
        dec     hl
        dec     hl
        ld      e,(hl)
        dec     hl
        ld      d,(hl)
        dec     hl
        ld      a,(hl)
        call    _SetDEUToA
        ex      de,hl                           ; HL => data
        cp      $D0                             ; is file in RAM?
        jr      nc,dataInRam
        push    ix
        push    hl
        pop     ix
        ld      a,10
        add     a,(ix+9)
        ld de,0 \ ld e,a
        add     hl,de
        pop     ix
dataInRam:
        inc hl \ inc hl                         ; skip length
        ret

#include "huffextr.asm"

;LastW:                  .dl $000000

ArcData:
 .db 0,0,1,0,0,1,0,1,1,1,2,1,1,2,1,2,2,2,3,3,3,2,3,4,4,4,4

EArc:
 .db 0,1,0,1,2,3,2,3,4,5,6,5,8,8

WorldChoice:
 .db "SQRXZ - choose world ",0

NoWorldsTxt:
 .db "No worlds found",0

WorldAuthor:
 .db "made by ",0

EnemyAddr:
__blob                  = $
__hedgehog              = $+3
__bat                   = $+6
__greenman              = $+9
__fish                  = $+12
 .dl $000000,$000000,$000000,$000000,$000000

OutOfTimeTxt:
 .db "OUT OF TIME ",0

DrownedTxt:
 .db "YOU DROWNED ",0

GetReadyTxt:
 .db "GET READY!",0

LevelFinTxt:
 .db "LEVEL COMPLETE!",0

GameFinTxt:
 .db "CONGRATULATIONS",0
 .db "You have beaten all",0
 .db "levels in this world!",0

PausedTxt:
 .db "PAUSED",0

StatsTxt:               .db "TIME:",0
                        .db "AIR:",0

Coder:                  .db "by Jimmy Mardell",0
                        .db "CE port by James Vernon",0
                        .db "v",VERSION,0

fileSprites:            .db "SQRXZS",0
fileTitle:              .db "SQRXZT",0
missingTxt:             .db "MISSING FILE ",0

fileSave:               .db appVarObj,"SQRXZV",0

fontArcade:
.db OP_OR_A,7,7,8
.dl fontArcadeTable
packedFontArcade:       #import "font_arcade.huf"
fontArcadeTable:
.db 0,1,0,0,0,0,2,0,0,0,0,0,0,3,4,0                         ; $20-$2F
.db 5,6,7,8,9,10,11,12,13,14,15,0,0,0,0,0                   ; $30-$3F
.db 0,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30          ; $40-$4F
.db 31,32,33,34,35,36,37,38,39,40,41,0,0,0,0,0              ; $50-$5F
.db 0,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30          ; $60-$6F
.db 31,32,33,34,35,36,37,38,39,40,41,0,0,0,0,0              ; $70-$7F

fontLarge:
.db OP_SCF,15,15,16
.dl fontLargeTable
packedFontLarge:        #import "font_large.huf"
fontLargeTable:
.db 0,1,0,0,0,0,2,0,0,0,0,0,0,3,4,0                         ; $20-$2F
.db 5,6,7,8,9,10,11,12,13,14,15,0,0,0,0,0                   ; $30-$3F
.db 0,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30          ; $40-$4F
.db 31,32,33,34,35,36,37,38,39,40,41,0,0,0,0,0              ; $50-$5F
.db 0,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30          ; $60-$6F
.db 31,32,33,34,35,36,37,38,39,40,41,0,0,0,0,0              ; $70-$7F

fontSmall:
.db OP_SCF,6,6,8
.dl fontSmallTable
packedFontSmall:        #import "font_small.huf"
fontSmallTable:
.db 0,1,0,0,0,0,2,0,0,0,0,0,0,3,4,0                         ; $20-$2F
.db 5,6,7,8,9,10,11,12,13,14,15,0,0,0,0,0                   ; $30-$3F
.db 0,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30          ; $40-$4F
.db 31,32,33,34,35,36,37,38,39,40,41,0,0,0,0,0              ; $50-$5F
.db 0,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30          ; $60-$6F
.db 31,32,33,34,35,36,37,38,39,40,41,0,0,0,0,0              ; $70-$7F

#include "title_pal.asm"

.end