; ------------------------------> TESTS2.z80 <------------------------------ ; ; Sqrxz 82 main program ; ; 2nd beta version of Sqrxz 82 ; ; I had try to allocate before the vat, but the ti82 doesn't deals with mem ; same way the ti85 does ( the program are stored just before the vat ) ; So, this beta WILL CRASH YOUR CALC EVERYTIME YOU RUN IT... ( on the one ; I've borrowed, it doesn't crash it but screw all the programs up, so you ; have to do a reset... ) ; ; (c) 16/05/98 Florent Dhordain ; ; This code is based on SQRXZ 86 v0.9b, BY ; // Jimmy M†rdell ; ; ; If you make any changes to the source, please tell us 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. ; ; Please notify changes to : ; Florent Dhordain ; Jimmy M†rdell ; Bugs found : ; ; None... #define FIX_PORTC_BUG2 #define FIX_ENEMY_PORTC ; If not #defined, ascr82 will use 64 #define ASCR_BOTCLIP 62 ; Note for the variables : ; ; I need 256 bytes of free mem, they'll be used later for huffman compression, in order ; to save what's after one of the graph mem, to have a 1024 long block ; variables are stored in SAVESSCREEN : ; from $8228 to $8327 : 256 bytes of tmp buffer ; and $8328 to $8528 : 512 bytes ;Variable Location Size Comment sysptrcpy = $8228 ; 256 See above XScr = $8328 ; 2 X scroller XBlock = $832a ; 1 XScr SHR 3 (1 block = 8x8 pixels) LevelSize = $832b ; 2 Length of level (x blocks) x = $832d ; 2 X location of Sqrxz y = $832f ; 1 Y location of Sqrxz flags = $8330 ; 1 1 - HScroll, 2 - Sqrxz has moved ; 3 - Died this frame, 4 - Was under portcullis ; 5 - Update enemies jump = $8331 ; 1 Where in the jump arc (backwards) fall = $8332 ; 1 Same as above, except when falling (and not bw) sqrxzcnt = $8333 ; 1 Sqrxz counter sqrxzdir = $8334 ; 1 Sqrxz direction (0 = Right, 1 = Left) jump_button = $8335 ; 1 The jump button status dead = $8336 ; 1 0 = alive, >0 = dead counter = $8337 ; 1 Main counter animcnt = $8338 ; 1 Animation counter, counts 3-1 hmovelast = $8339 ; 1 1 = Update Sqrxz deadcnt = $833a ; 1 Death pause counter - starts after death tmp_x = $833b ; 2 Temporary storage for Sqrxz X tmp_y = $833d ; 1 Temporary storage for Sqrxz Y start_y = $833e ; 1 Sqrxz start Y start_x = $833f ; 2 Sqrxz start X (0=start of level) enemytable = $8341 ;56 Enemy table [0..7] type,dir,x(w),y,fall,spec (8*7) world = $8379 ; 2 Address to selected world lvlptrs = $837b ; 2 Pointer to level pointers, alt level info worldT = $837d ; 1 0=uncompressed levels, 1=compressed levels noLevels = $837e ; 1 Number of levels on the selected world lvl = $837f ; 1 Current level lives = $8380 ; 1 Lives left timeleft = $8381 ; 2 Time left sausages = $8383 ; 1 Sausages taken gaming = $8384 ; 1 0=not playing, 1=playing waterbar = $8385 ; 2 The water bar (0-479) worldptr = $8387 ; 2 pointer to the world selected clipmask = $8389 ; 1 Temporary byte used in ASCR rows2put = $838a ; 1 Temporary byte used in ASCR bitmask = $838b ; 1 Temporary byte used in ASCR backcnt = $838c ; backward scroll counter (1..24) curOpt = $838d ; 1 Current option SqrxzBG = $838e ;$A Old Sqrxz X,Y and background (10 byte) LastSprite = $8398 ; 2 Pointer to the last enemy sprite put BGETable = $839a ;$50 Table of old enemy data: X,Y and background animptr = $83ea ; 1 Next change in the table (0-7) AnimTable = $83eb ;$20 Anim table. 8 entries, 4 byte each (x,y,addr) portc = $840b ; 4 A portcullis that will go down (x,y,addr) ; Ends at $840E (included), 256+230 bytes were used ; 281 bytes left noWorlds = $838e ; 1 Number of worlds found on calc top = $838f ; 1 Top of the world list noItems = $8390 ; 1 Number of worlds in the current list curItem = $8391 ; 1 Current world choice worlds = $8392 ; Pointers to different worlds ; enough room to store 400 bytes, ; allowing 200 worlds (a lot more than enough) ; System equates : LD_HL_MHL = $0033 CP_HL_DE = $0095 _divHLby10 = $00A1 UNPACK_HL = _divHLby10 _getcsc = $01D4 ; This one is meaningless GET_KEY = _getcsc ; This is better ; All these must be called using ROM_CALL _GRBUFCLR =$38E4-$1A _vputs =$37EE-$1A _clrLCD =$38B4-$1A _clrScrn =$38E4-$1A _puts =$3914-$1A _putc =$3752-$1A BUSY_OFF =$3932-$1A #DEFINE ROM_CALL(addr) CALL $8D74 \ .DW addr #DEFINE ROM_JUMP(addr) CALL $8D74 \ .DW addr \ ret OP1 = $8028 timer = $8009 ; LSB of APD counter : used as counter : Thank you for that ; great idea movax =) K_MODE = $37 K_CLEAR = $0F K_ENTER = $09 ; K_Add = $0A ; If they are defined, then ; K_Sub = $0B ; they are surely used =P .org $9104 Title: .db "Sqrxz ",188,"2 by" .db "Florent Dh.",0 progstart: ROM_CALL(_GRBUFCLR) ROM_CALL(BUSY_OFF) ; Turn busy indicator off ; First, allocate some memory ; make sure there's enough memory : ld de, ($8CFF) ; de = -free mem (hehe, please update your 82-ram.txt, Dines =) ld hl, 0 or a sbc hl, de ; now, hl = free mem ld a, h cp 8 ; $800 = 2048 : size of mem to allocate jr nc, enoughmem ld hl, 0 ld ($800C), hl ; goto (0,0) ld hl, txtmem ROM_CALL(_puts) jp WaitKey ; call+ret enoughmem: res 1,(iy+$0D) ; _clrLCD, _puts etc doesn't affect the textshadow res 2,(iy+$0D) ; DONT SCROLL ! set 4,(iy+$14) ; _vputs writes directly to graph res 7,(iy+$14) ; NOT TO THE BUFFER ! ld hl, TitlePic ; HL -> titlepicture raw data ld de, $88B8+(7*12) ; Copy it at line 7 ld bc, 21*12 ; Titlepic is rows high ldir call ScreenCopy ; Show the pic ld hl, $2012 ld ($8215),hl ; goto (18,32) ld hl, coder ROM_CALL(_vputs) ; show coder ld de, $2903 ld ($8215), de ; goto (3,41) ROM_CALL(_vputs) ; show TI82 port by ld de, $3216 ld ($8215), de ; goto (22, 50) ROM_CALL(_vputs) ; show titlepic designer ld de, $3919 ld ($8215), de ; goto (25, 57) ROM_CALL(_vputs) ; show titlepic designer call WaitKey ; Wait until a key is pressed ld (iy+8),$8c ; set bits 2&3 : Enable APD ( used as counter ) xor a ; Clear some main variables 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 ChangeOptions ; Call the option menu call ChooseWorld ; And a call to select world to play on PlayWorld: ld a, $40 out ($10), a ; Disable screen Y offset ld a,1 ld (gaming),a ; Now the game has started ld hl,(Worldptr) ; 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 call ShowTopMsg ; Display the name of the world (with puts it didnt fit ld a,96 ; A = size of the screen sub (hl) ; Subtract with the size of the author inc hl ; HL -> author srl a ; Divide A with 2. This will center the text. ld d,$09 ld e,a ld ($8215),de ; Set cursor (PEN cursor) to row 9, col A ROM_CALL(_vputs) ; And show the name of the author directly after ld (lvlptrs),hl ; Save HL for later, which points to level data ld hl,$0907 ld ($800C),hl ; Set cursor position to middle of screen ld a,(lvl) inc a ; Increase the level number since Level 1 really is 0 call DispA ; And show the level number ld a,$04 ld ($800D),a ld hl,LevelTxt ROM_CALL(_puts) ; Display "Level" ld hl,$0804 ld ($800C),hl ld a,(lives) call DispA ; Show lives left ld a,$07 ld ($800D),a ; Update the X coordinate ld a,'x' ROM_CALL(_putc) ; Put out the 'x' (must be done in this order) ld hl,Sqrxz ; HL -> Sqrxz sprite ;; di ; di cause directly to LCD ld a, 5 ; LCD cursor goes down call LCDOut ld a, $80+$1F ; Goto Y = 31 call LCDOut ld a, $20+$3 ; Goto X = 3 call LCDOut ld bc,$811 ; 8 rows to port $11 PutSq: call LCDBusy outi ; shorter than out (c), (hl) \ inc hl \ dec b jr nz,PutSq ; And show another row call WKey_Cl cp K_CLEAR ; If exit was pressed jp z,QuickQuit ; then abort the game ld hl,LevelPtr ; HL -> Future storage of temporary level ld bc,2047 ; Prepare clear 2k memory. Max lev size is 255*8=2040 ld (hl),0 call FillChar ; Clear it. Last 8 bytes are necessary to be cleared ld hl,(lvlptrs) ; HL -> level info ld d,0 ; This saves a few bytes (ld de,xx won't be necessary) 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) UncompWorld: add a,a ; Multiply noLevels with 2 ld e,a 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 e,a add hl,de ; thus getting the offset to the offset pointer 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 h,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 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: call LoadLevel ; Initialize the current level call movebars ; put bars where they should be ; 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. Check if the enemies have to be removed from the screen to avoid trash. ; This is the case if 1) the enemies have moved 2) the screen scrolls ; or 3) Sqrxz is overlapping the enemy which happens when a) Sqrxz is ; dying (death jump), b) when the Bat dies or c) when the green man is ; hiding in the helemt ; 13. scroll screen if necessary, put Sqrxz on the screen and also put all ; enemies if they were removed (see above). ; 14. Update the timebar (decrease) and the waterbar (decrease if in water, ; else decrease). ; 15. Show the screen and Remove Sqrxz from the buffer ; 16. Then make a delay, so the game goes at a constant speed (if possible) ; 17. If Sqrxz is alive and the player hasn't aborted the game, jump to step 1. MainLoop: ei xor a ; A lot of vars should be cleared each frame ld (timer),a ld (flags),a ld hl,counter inc (hl) ; Increase the overall counter ld hl,animcnt dec (hl) ; Decrease the anim counter, which is modulo 3 jr nz,CheckDead ; Only animate 1/3 ld (hl),3 ; Reset 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 ld e,(hl) inc hl ld d,(hl) ; 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,PortClosed ; 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 PortClosed: #ifdef FIX_ENEMY_PORTC push af push bc push hl call RemoveEnemies pop hl pop bc pop af #endif 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: call CoordConv ; Get where on screen that tile is call PutTileNo ; And put tile A on that position NextAnim: pop hl ; Restore HL inc hl inc hl inc hl ; Increase HL with 3 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 d,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,flags 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 d,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 d,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+6) 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 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+4) ld hl,y sub (hl) ; Subtract enemies Y coord with Sqrxz sub 8 ; Subtract with 8 (the sprite size) : head to Sqrx feet 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+6),1 ; Set a flag indicating a hit xor a ld (fall),a ; Clear fall counter ld a,(ix+4) 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,$FE out (1),a in a,(1) bit 3,a ; Check if the up key is being pressed jr z,JumpOnEnemy ; If so, start a jump ld a,$BF out (1),a in a,(1) bit 5,a ; Check if the 2nd key is being pressed jr nz,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,7 add ix,de ; Add with 7 to get the next entry djnz 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 7 ; 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,$BF ; Set the mask (scanning for 2nd) out (1),a ; Send the mask to the port in a,(1) ; And receive the "anser" and $F0 ; Mask unnecessary bits ld c,a ld a,$FE ; Set the mask out (1),a ; Send the mask to the port in a,(1) ; And receive the "anser" bit 0,a ; Check if the down key is held down jr z,SlowMovement ; If yeah, move slowly ld b, a ld a, $DF out (1), a ; Û Flo Û : I've added the Alpha key which does in a, (1) ; the same as [Down] and $80 ld a, b jr nz, CheckMovement 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 and %1000 ; Is the up key held down? jr z,TryJump ; Yup - see if it's possible to jump bit 5,c ; Is the 2nd key held down? jr z,TryJump ; Yup - see 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 and %10 ; Left key held down? call z,MoveLeft ; Yup - move left if possible pop af and %100 ; Right key held down? call z,MoveRight ; Yup - move right if possible ld hl,flags bit 2,(hl) ; Check if Sqrxz has moved this frame ld a, 1 ; if Yes, indicate it 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 a bit in the Sqrxz anim counter res 1,(hl) ; This will prevent Sqrxz from having his legs xor a ; in the air if he's not moving (would look stupid) MovedHor: ld (hmovelast),a ; Clear the flag - Sqrxz didn't move this frame SetUpdateSqrxz: ; Sqrxz is to be updated (the Sqrxz animation changed) ld a, (flags) or %100 ; And set the Sqrxz Moved This Frame flag ld (flags), a 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,flags 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,flags set 3,(hl) ; Set a flag indicating Sqrxz died this frame CheckIfDied: ld a,(flags) and %1000 ; 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 (L=x, H=y) ld a,h or l ; Check if HL=0 jr z,CheckEnemies ; It was - no such port. Check for enemies ld a,(flags) and %10000 ; Check if Sqrxz was below a portcullis this frame jr nz,CheckEnemies ; he was - can't close the portcullis now. ; xor a isn't needed as a gets cleared by the and ld b, l ; B = x coor of portcullis to go down ld c, h ; C = y coor of portcullis to go down ld hl,portc ld (hl),a ; Clear x coord byte inc hl ld (hl),a ; Clear y coord byte as well inc hl call LD_HL_MHL ; 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) <- This wasn't so good : destroys hl & de... ;; ld de,(XScr) <- ;; or a <- ;; sbc hl,de ; HL = Sqrxz X position on screen ld a, (xscr) ; Rather only use LSB, as it will anyway be modulo 256 ld d, a ld a, (x) sub d ; A = Sqrxz X position cp 59 ; Check if >=59 (85-120+96) jr nc,Scroll ; If so, scroll cp 44 ; Check if <44 jr c,TScrollR ; If so, test if scroll right ld a, (counter) and 1 ; If between 44-59, scroll every second frame jr z,NoScroll ; If an "even" frame, skip scrolling Scroll: call RemoveEnemies ; Must remove enemies before scrolling call ScrollLeft ; Scroll screen! DoneScroll: ld a, (counter) and 1 ; Check if it was an even frame jr z,ScanEnemies ; Yes, no enemy moved this frame jr CheckSqUpdate ; Skip ScanEnemies since they have already been removed NoScroll: ld a, (counter) and 1 ; Check if it was an even frame jr z,ScanEnemies ; Yes, no enemy moved this frame call RemoveEnemies ; Else remove the enemies jr CheckSqUpdate ; Skip ScanEnemies since they have already been removed TScrollR: cp 17 ; check if <=16 jr c, ScrollR ; if yeah, scroll right cp 25 ; check if <=24 jr nc, NoScroll ; if no, don't scroll ld a,(counter) and 1 ; If between 20-37, scroll every second frame jr z,NoScroll ; If an "even" frame, skip scrolling ScrollR: call RemoveEnemies call ScrollRight jr DoneScroll ScanEnemies: ; Check if Sqrxz is close to an enemy ld ix,enemytable ; IX -> enemy table ld b,8 ; 8 entries to check ScanEnemy: ld a,(ix) or a ; Is there an enemy at this entry? jr z,ScanNext ; Nope, check next entry ld a,(y) ld c,(ix+4) sub c ; Now A = difference between the y coordinates bit 7,a jr z,SE_AbsY neg ; If A<0, then A=-A SE_AbsY: cp 10 jr nc,ScanNext ; If A>=10 then enemy is not close ld d,(ix+3) ld e,(ix+2) ld hl,(x) or a sbc hl,de ; Now HL = difference between the x coordinates ld a,l ; A = dif jr nc,SE_AbsX ; If negative, neg ; then make it positive SE_AbsX: cp 10 jr nc,ScanNext ; If A>=10 then enemy is not close call RemoveEnemies ; Remove all enemies jr CheckSqUpdate ScanNext: ld de,7 add ix,de ; Point to next enemy entry djnz ScanEnemy ; Scan next enemy CheckSqUpdate: ; Update Sqrxz, NOW EVERY TIME ld a,(flags) and %100000 ; Check if RemoveEnemies has been called call nz,PutEnemies ; Yes, put the enemies on the screen again call PutSqrxz ; Put Sqrxz on new location : must be done after ; putenemies, as it is drawn before 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 :( ld a, l and 7 jr nz, UpdateTime ; Don't change waterbar if not a 0 here call GetWBBit cpl and (hl) ld (hl), a ; Clear the bit jr UpdateTime ; Continue with timebar IncreaseWaterBar: ld de,479 call CP_HL_DE ; Check if top of bar is reached jr z,UpdateTime ; If so, don't increase the bar push hl ; The old value is the one to be deleted later inc hl ; Increase bar call CP_HL_DE ; 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 pop hl ; HL = old water bar value ld a, l and 6 jr nz, UpdateTime ; Don't change if not necessary call GetWBBit or (hl) ld (hl), a 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 jr z,TimeOut ; If so, time is out - show message ld a, l and $FE jr nz, TimeOk ; Don't draw timebar if not necessary ld a, h ld b, a ; save length and 7 ld l, a ld h, 0 ld de, ExpTable+1 add hl, de ld c, (hl) ld hl, $88B8+(62*12) ld a, b srl a srl a srl a ; a = length/8 ld e, a ld d, 0 add hl, de ; hl = where to change ld a, c cpl and (hl) ld (hl), a ; Clear bit TimeOk: call ScreenCopy ; Copy screen, needed on the TI83-82 call RemoveSqrxz ; Remove Sqrxz each time ld hl,timer ; HL -> the interrupt timer jr nohalt ; we don't know if halt is needed here Wait: HALT ; Save a little of battery power nohalt: ld a,(hl) neg ; caus the APD counter counts backwards speedC: cp 3 ; Has it gone 3/200 sec since frame started? jr c,Wait ; Nope, wait until it has ld a,($8001) ; KEY_1 : Undocumented ;) cp K_CLEAR ; Check if Exit has been pressed jr z,LoseLife ; If so, quit the game cp K_MODE ; Check if P has been pressed jp z,Pause ; If so, pause the game PauseResume: ld a,(y) ; Prepare to find out if Sqrxz y coordinate is >=64 cp 150 ; If it's bigger then 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 GET_KEY 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: ld hl,lives dec (hl) ; Lose a life jp z,QuickQuit ; If no lifes left, quit game xor a ld (sausages),a ; Reset sausages counter jp PlayWorld ; And restart the level TimeOut: ld hl,OutOfTimeTxt ; HL -> "OUT OF TIME" ShowDeathMess: ld de,$0203 ld ($800C),de ; Set cursor position to middle of screen set 3,(iy+$05) ; Set white on black ROM_CALL(_puts) ; Show text res 3,(iy+$05) ; Set black on white call WaitEnter ; Wait until enter key is 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: ld hl, lvl inc (hl) Changelevel: xor a ld (start_x), a ld a, (noLevels) cp (hl) jp nz, playWorld call ShowTitle ; Show title ld hl,$0002 ; Prepare to display a congratulation message ld ($800C),hl ld hl,GameFinTxt ROM_CALL(_puts) ld de,$2010 ; (x=16,y=32) ld ($8215),de ROM_CALL(_vputs) ld de,$2A0F ; (x=15,y=42) ld ($8215),de ROM_CALL(_vputs) 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 ld hl,flags 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 push bc call RemoveEnemies ; Removing enemies to avoid trash pop bc call CoordConv ; Converts the coordinate to screen coordinates xor a call PutTileNo ; Put an empty tile at those coordinates ld hl,sausages ld a,(hl) inc a ; Increase the number of sausages and 31 ; The counter is modulo 32 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 push bc call RemoveEnemies ; Removing enemies to avoid trash pop bc call CoordConv ; Converts the coordinate to screen coordinates xor a call PutTileNo ; Put an empty tile at those coordinates 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 Pause: ld a, $40 out ($10), a ; Disable screen Y offset call ChangeOptions call movebars ; Move screen if needed jp PauseResume ; Resume game MoveLeft: call CheckLeft ; Check if possible to move left ret c ; Nope, it wasn't ld hl,(x) ld de,(XScr) call CP_HL_DE ; 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,flags 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,88 ; (12-1)*8=88 add hl,de ld de,(x) call CP_HL_DE ; 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,flags 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,flags 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,flags set 2,(hl) ; Set the flag indicating that Sqrxz has moved ld hl,y dec (hl) ; Decrease the y coordinate ret MoveEnemies: ld a, (counter) and 1 ; 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+6) or a jp nz,NextEnemy ; If spec>0, the enemy is not moving at the moment ld h,(ix+3) ld l,(ix+2) ; HL = enemy x location ld (x),hl ld a,(ix+4) ; A = enemy y location ld (y),a ld de,(XScr) ld bc,88 ; Old value was 64. but, with backscrolling : 64+24 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 a,(LevelSize) ld h,0 ld l,a add hl,hl add hl,hl add hl,hl ex de,hl ld hl,(x) call CP_HL_DE ; 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+5) 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+5),a ; Update the fall counter ld hl,EArc ld d,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 jr NextEnemy EResetFall: ld (ix+5),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 a,l ; Now A = enemy_x-sqrxz_x jr nc,UEAbsX ld a, h cpl ; Negate also H, elsewhere when sqrxz is at right of ld h, a ; the enemy, the collision isn't detected ld a, l neg ; If A<0, negate it UEAbsX: cp 8 jr nc,NoCollision ; If difference>=8, no collision with Sqrxz ld a,h or a ; This is necessary to check, else Sqrxz would jr nz,NoCollision ; die if enemy_x=sqrxz_x+256 :-/ 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+4),a NextEnemy: ld de,7 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) call GET_KEY ; Clear last keypress ;; ld hl,(x) <- This wasn't so good : destroys hl & de... ;; ld de,(XScr) <- ;; or a <- ;; sbc hl,de ; HL = Sqrxz X position on screen ld a, (xscr) ; Rather only use LSB, as it will anyway be modulo 256 ld d, a ld a, (x) sub d ; A = Sqrxz X position cp 48 ; Check if X rel>=(96/2=48) 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 add a,a add a,a ; Multiply with 4 since each entry is 4 bytes ld d,0 ld e,a 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 ; Now that address is stored as well pop af inc a ; Increase the animation pointer and 7 ; Which is modulo 8 ld (animptr),a ; Update with the new value call CoordConv ; Convert the coordinates of the tile pop af call PutTileNo ; change the tile ret PutTileNo: ; Puts tile A at B,C (B,C = screen coordinates) ld h,0 ld l,a add hl,hl add hl,hl add hl,hl ; Multiply tile with 8 since each tile is 8 bytes ld de,Sprites add hl,de ; Add with pointer to first tile call PutSprite ; And put the sprite ret CoordConv: ; Converts tile coordinates B,C to screen coord B,C ;; push de push hl sla c sla c sla c ; Multiply the Y coordinate with 8 ld h,0 ld l,b add hl,hl add hl,hl add hl,hl ; Multiply the X coordinate with 8 ;; ld de,(XScr) ;; sbc hl,de ; And decrease it with XScr to get the screen coord ;; ld b,l ld h, a ld a, (Xscr) sub l ; And decrease it with LSB(XScr) to get screen x coord neg ld b, a ld a, h pop hl ;; pop de 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 add hl,hl add hl,hl add hl,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 DecodeTopTile: or a ret z #ifdef FIX_PORTC_BUG2 cp 94 ret z #endif cp 32 ld a,0 ; If a background tile, act as space (not xor a!) ret nc inc a ; Else set to crushable wall (so you can't pass it) ret OnScreen: srl a srl a srl a ; Divide Y with 8 ld c,a ld d,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 7 ; 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: push af 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 7 ; 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 pop af cp 100 ; Check if on screen jr c, CV_checks ; Yeah : all is OK jr CV_checks CV_Even: pop af CV_checks: 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 PutSqrxz: ;; ld hl,(x) ;; ld de,(XScr) ;; or a ;; sbc hl,de ; Now HL = x screen coordinate of Sqrxz :: ld b,l ; B = x pos on screen ld a, (Xscr) ld b, a ld a, (x) sub b ld b, a ; B = x pos on screen ld a,(y) ld c,a ; C = y pos on screen ld (SqrxzBG),bc ; Store those coordinates in the Background buffer ld hl,Sqrxz ; HL -> Sqrxz sprites ld a,(sqrxzcnt) ; A = Sqrxz animation counter ;; srl a ; Divide with two so he doesn't animate too fast ;; and 3 ; Now A = 0-3 ;; add a,a ; Flo : and 6 ; Now A = 0, 2, 4 or 6. add a,a ; so one mul by 2 has already been done ! add a,a add a,a ; Multiply with 16 since each sprite is 16 byte (mask) ld d,0 ld e,a add hl,de ; Add the sprite offset with the pointer ld a,(sqrxzdir) or a ; Check if Sqrxz is faced to the right jr z,FaceRight ; If so, no more adding ld de,64 add hl,de ; Add with 64 (16*4) to get those sprites FaceRight: ld de,SqrxzBG+2 ; DE -> Sqrxz background storage jp PutSprite_MSB ; Put the sprite (1 byte less than call PS \ ret) RemoveSqrxz: push af push bc ; These register needs to be saved ld bc,(SqrxzBG) ; B,C = old Sqrxz screen relative coordinates ld hl,SqrxzBG+2 ; HL -> Sqrxz background call PutSprite ; Put the background pop bc pop af ret PutEnemies: ld hl,0 ld (LastSprite),hl ; Clear the LastSprite pointer (will be updated) ld ix,enemyTable ; IX -> enemy table ld de,BGETable ; DE -> table storing enemy backgrounds 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 (LastSprite),de ; LastSprite -> Last enemy background information push de ld h,(ix+3) ld l,(ix+2) ; HL = enemy x position ld de,(XScr) sbc hl,de ; HL = enemy x position on screen push hl dec a ; A = enemy number (0-4) add a,a ld hl,EnemyAddr ; HL -> table of enemy gfx pointers ld d,0 ld e,a add hl,de ; HL -> pointer to enemy gfx ld b,(hl) inc hl ld h,(hl) ld l,b ; HL -> start of enemy graphics for current enemy cp 8 ; Check if fish man jr z,HH ; Fishes gfx are stored the same way as hedgehog bit 1,a ; Check if Blob or Bat jr z,EAnimAdd ; Those are stored the same way cp 2 ; Check if hedgehog jr z,HH bit 0,(ix+1) ; Check direction for green man jr z,EAnimAdd ld de,64 ; If faced left, add 64 (16x4) 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,32 ; If faced left, add 32 (16x2) to sprite address add hl,de NoHHDirAdd: ld a,(counter) bit 4,a jr z,AddrFound ld de,16 ; 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 add a,a ; A will now be 0, 16, 32 or 64 ld d,0 ld e,a 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+4) ; C = sprite Y coordinate pop de ex de,hl ; Now HL -> enemy background storage, DE -> sprite ld (hl),b ; Store the enemy coordinates inc hl ld (hl),c inc hl ex de,hl ld a,(ix+6) 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+6),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 ld hl,GreenManHide ; Else it was the green man cp 4 jr c,SpecialPop ; If spec<4, show half hiding green man ld de,16 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+6),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: ld hl,BatSplash ; HL -> bat splash graphics add a,a add a,a and $F0 ld d,0 ld e,a 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,16 sbc hl,de ; HL -> last splash gfx jr SpecialPop KillBlob: ld hl,BlobDead ; HL -> dead blob gfx ld (ix),0 ; Remove enemy from enemy table pop de pop bc call PutSprite_MSB ; Show dead blob jr PutEnemy ; And show it again (else the dead blob would disappear) SpecialPop: pop de pop bc PutEnemy: call PutSprite_MSB ; Show sprite ex de,hl ld de,8 add hl,de ex de,hl ; HL -> next position in sprite background table PNext: ld bc,7 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 RemoveEnemies: push af ld hl,flags bit 5,(hl) ; Check if this routine has already been called jr nz,RemDone set 5,(hl) ; Set that flag now else ld hl,(LastSprite) ; HL -> last enemy that has been put to screen ld a,h or l ; If 0, no enemies was on screen jr z,RemDone RemNextSprite: ld b,(hl) inc hl ld c,(hl) inc hl ; Now B,C = previous enemy coordinates, HL->background call PutSprite ; Show the background ld de,BGETable+2 call CP_HL_DE ; Check if all backgrounds have been put to screen jr z,RemDone ld de,-12 add hl,de ; HL -> next background to be put to screen jr RemNextSprite RemDone: pop af ret ScrollLeft: ld b,a ld a,(XBlock) add a,12 ld hl,LevelSize cp (hl) ; Check if end of level reached ; ld a, b ret nc ; If so, return without scrolling ld hl, backcnt ld a, (hl) dec a ; Allow to scroll back one more pixel jr z, SL_back0 ; don't dec backcnt if it's already 1 ld (hl), a SL_back0: ld a, b di ; Disable interrupts so IY can be used ld hl,SqrxzBG+1 dec (hl) ; Update Sqrxz screen X coordinate push af push iy ld hl,$88B8+$2E7 ; HL -> end of video_mem-2 rows ld c,62 ; 62 rows to scroll ( leave 2 for the status bars ) RepScrollR1: ld b,3 ; 3x4 bytes to scroll each row or a ; Clear CF : a '0' will be put, so no res 0,(ix) will be used ! RepScrollR2: ; The actual scroll loop rl (hl) dec hl rl (hl) ; It's faster to use 3x4 rotates instead of dec hl ; 12x1 since the djnz is called much less frequently rl (hl) dec hl ; Every bit of speed is needed! rl (hl) dec hl djnz RepScrollR2 ; Repeat the first scroll loop dec c jr nz,RepScrollR1 ; And scroll the next row 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 jr z,Even scf ; If not, there are 13 tiles left<->right jr FindRightBlock Even: inc (hl) ; If so, increase XBlock FindRightBlock: ld a,(hl) ; A = x block tile to left adc a,11 ; Add with 11 (or 12) to get the x block tile to right ld h,0 ld l,a add hl,hl add hl,hl add hl,hl ld de,LevelPtr add hl,de ; HL -> level data of the blocks to be put to the right push hl pop iy ld a,(XScr) and $07 ld hl,ExpTable ld d,0 ld e,a add hl,de ld d,(hl) ; D = bit value to mask out of tile sprite data ld c,8 ; 8 tiles to put ld ix,$88B8+$0B RepFillRight: push de ld a,(iy) ; A = tile to put inc iy bit 7,a ; Check if an enemy jr z,SL_Tile push bc ; Prepare to add an enemy to the enemy table push hl push af ld a,8 sub c add a,a add a,a add a,a ld c,a ; C = y coordinate of enemy ld a,(XBlock) add a,12 ld h,0 ld l,a add hl,hl add hl,hl add hl,hl ; HL = x coordinate of enemy pop af and $7F call NewEnemy ; Add enemy to table cp 5 ; Check if it was a fish ld a,109 ; If so, then the tile should be replace with water jr z,FillWater xor a ; Else an empty tile FillWater: ld (iy-1),a ; Store the new tile pop hl pop bc SL_Tile: ld h,0 ld l,a add hl,hl add hl,hl add hl,hl ld de,Sprites add hl,de ; HL -> tile to put pop de ld b,8 ; 8 rows in the sprite : puts 64 pixels, but not a pb SR_Fill: ld a,(hl) inc hl and d ; This masks out the only bit that will be put jr z,SR_NextY ; If bit cleared, remove the last pixel set 0,(ix) ; Else set it SR_NextY: push de ld de,12 add ix,de ; IX -> next row in video mem pop de djnz SR_Fill ; Proceed with next row in tile dec c jr nz,RepFillRight ; And proceed with next tile SR_Done: pop iy pop af ei ret ScrollRight: ld b,a ld hl, backcnt ld a, (hl) cp 25 ret nc ; Don't scroll if maximum is reached inc (hl) ld a,b di ; Disable interrupts so IY can be used ld hl,SqrxzBG+1 inc (hl) ; Update Sqrxz screen X coordinate push af push iy ld hl,$88B8 ; HL -> start of video mem ld c,62 ; 62 rows to scroll ( leave 2 for the status bars ) RepScrollL1: ld b,3 ; 3x4 bytes to scroll each row or a ; Clear CF : a '0' will be put, so no res 0,(ix) will be used ! RepScrollL2: ; The actual scroll loop rr (hl) inc hl rr (hl) ; It's faster to use 3x4 rotates instead of inc hl ; 12x1 since the djnz is called much less frequently rr (hl) inc hl ; Every bit of speed is needed! rr (hl) inc hl djnz RepScrollL2 ; Repeat the first scroll loop dec c jr nz,RepScrollL1 ; And scroll the next row ld hl,(XScr) dec hl ld (XScr),hl ; decrease the scroll X coordinate ld a,l ld hl,XBlock and $07 ; Check if the leftmost block has changed xor 7 jr nz, FindLeftBlock dec (hl) ; If so, decrease XBlock FindLeftBlock: ld l,(hl) ; A = x block tile to left ld h,0 add hl,hl add hl,hl add hl,hl ld de,LevelPtr add hl,de ; HL -> level data of the blocks to be put to the right push hl pop iy ld a,(XScr) and $07 ld hl,ExpTable+1 ld d,0 ld e,a add hl,de ld d,(hl) ; D = bit value to mask out of tile sprite data ld c,8 ; 8 tiles to put ld ix,$88B8 RepFillLeft: push de ld l,(iy) ; l = tile to put inc iy ld h,0 ; No ennemy check as this part as already be shown add hl,hl add hl,hl add hl,hl ld de,Sprites add hl,de ; HL -> tile to put pop de ld b,8 ; 8 rows in the sprite : puts 64 pixels, but not a pb SL_Fill: ld a,(hl) inc hl and d ; This masks out the only bit that will be put jr z,SL_NextY ; If bit cleared, remove the last pixel set 7,(ix) ; Else set it SL_NextY: push de ld de,12 add ix,de ; IX -> next row in video mem pop de djnz SL_Fill ; Proceed with next row in tile dec c jr nz,RepFillLeft ; And proceed with next tile SL_Done: pop iy pop af ei 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 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,7 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 (lastsprite),hl ; ld (x),a (??? : useless : a wasn't initialized) xor a ld (animptr),a ld (deadcnt),a ld (XBlock),a ld a, 3 ; as animcnt counts backwards for more speed ld (animcnt),a ld a, 25 ld (backcnt), a ; This saves begin_of_level_check in ScrollRight ld hl,AnimTable ld bc,39 ld (hl),$FF call FillChar ; Clear the animation table (with $FF) ld hl,enemyTable ld bc,55 ld (hl),0 call FillChar ; Clear enemy table ROM_CALL(_GRBUFCLR) ; Clear the PLOTSCREEN 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,11 cp c ; Check so the right edge won't be beyond size of level ld a,b jr c,NoProbR ld a,c sub 12 ; Fix if necessary NoProbR: ld (XBlock),a ; Store the start screen location in XBlock ex de,hl ld h,0 ld l,a add hl,hl add hl,hl add hl,hl ld (XScr),hl ; and in XScr as well ex de,hl add hl,de pop af push hl ld h,0 ld l,a add hl,hl add hl,hl add hl,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 coordinates RepShowLevel: 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 h,0 ld l,b ld (x),hl ; Else store Sqrxz start coordinates jr Restore EnemySt: ld h,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 push hl ld h,0 ld l,a add hl,hl add hl,hl add hl,hl ld de,Sprites add hl,de ; HL -> tile to put call PutSprite ; And put the sprite pop hl ld a,c add a,8 ; Increase the Y coordinate with 8 ld c,a cp 64 jr nz,RepShowLevel ; 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 96 jr nz,RepShowLevel ; If less than 96, continue with the level ld a, (timeleft+1) ; A= MSB of time left ld hl, $88B8+(62*12) call makebar ld hl, 479 ld (waterbar), hl ; Init waterbar ld a, 60 ld hl, $88B8+(63*12) makebar: ld c, a srl a srl a srl a ld b, a ; number of bytes (8 pixels) ld a, c and 7 ; number of bits remaining MB_byteloop: ld (hl), $FF inc hl djnz MB_byteloop or a ret z ; Exit if no bits remaining ld b, a ld a, $80 MB_bitloop: ld c, a or (hl) ld (hl), a ld a, c rra djnz MB_bitloop ret ; Input : hl = waterbar ; output : (hl) & a is the bit to set/clear GetWBBit: srl h rr l srl h rr l srl h rr l ; l = length ld a, l ld b, a ; save length and 7 ld l, a ; h is already 0 ld de, ExpTable+1 add hl, de ld c, (hl) ld hl, $88B8+(63*12) ld a, b srl a srl a srl a ; a = length/8 ld e, a ld d, 0 add hl, de ; hl = where to change ld a, c ret ChooseWorld: ; This routine lets the user choose world ld hl, WorldChoice call ShowTopMsg ; Show it set 3, (iy+$05) call ShowCInfo res 3, (iy+$05) ; Why isn't this in ShowCInfo ?? ld hl, noWorlds ld bc, 167 ld (hl), 0 call FillChar ; Clear list of worlds found ld hl, $FE6E ; Prepare scanning programs ld ix, worlds SearchWorlds: ld de, ($8D12) ; end of progtable - 1 inc de or a sbc hl, de jr c, SearchDone add hl, de push hl ld a, (hl) and $1F ; mask out type bits cp 5 jr z, ProgFound cp 6 jr z, ProgFound ; no, it isn't a program, but a variable (entry is 6 bytes) : so skip it pop hl ld b, 6 jr SkipName ContinueSearch: pop hl dec hl dec hl dec hl ; skip type & ptr ld b, (hl) ; retrieve name length inc b ; inc so also skip namelen byte SkipName: dec hl djnz SkipName jr SearchWorlds ; And continue searching ProgFound: dec hl ld e, (hl) dec hl ld d, (hl) ; de now points on the program data dec hl ld a, (hl) ; a = namelen ex de, hl inc hl inc hl ; skip the size ld a, (hl) cp 2 jr nz, ContinueSearch ; If not 2 (indicating Sqrxz level), continue search inc hl ld a, (hl) or a jr nz, ContinueSearch ; Must be 0 (uncompressed world) ld (ix), l ld (ix+1), h ; Store the address to that level ( in LSB order ) inc ix inc ix ld hl, noWorlds inc (hl) ; increase number of worlds found jr ContinueSearch ; Continue search SearchDone: ld a, (noWorlds) or a jr nz, ShowWorlds ld hl, $1d01 ld ($8215), hl ; go to middle ld hl, NoWorldsTxt ROM_CALL(_vputs) call WaitEnter jp Abort ShowWorlds: ld hl, $0703 ld ($8215), hl ; go to line after the title ld hl, worlds ld a, (top) add a, a ld d, 0 ld e, a ; de = 2*top add hl, de ; hl -> pointers to worlds ld b, 8 ; max 8 worlds to be shown at the same time ShowWorld: ld e, (hl) inc hl ld d, (hl) ; DE = pointer to world data inc hl xor a or d jr z, EndOfList ; if NULL pointer , end of list reached push bc push hl ex de, hl inc hl ; Skip level type inc hl ; and first byte of no levels (always 0) ld a, (hl) ; a = number of levels in this world inc hl push af ROM_CALL(_vputs) ; show world name ld a, $48 ld ($8215), a ; update X coordinate pop af call MDispA ; and display number of levels ld hl, $8215 ld (hl), $03 ; set X coord to 3 inc hl ld a, (hl) add a, 6 ld (hl), a ; and increase the Y coord with 6 pop hl pop bc djnz ShowWorld EndOfList: ld a,8 sub b ; A = number of worlds in current list ld (noItems), a xor a UpdateChoice: ld (curItem), a call MarkItem ; Mark the current selection Choose: call WKey_A cp K_CLEAR jr z, Abort cp K_MODE jr z, NextPage cp K_ENTER jp z, Select dec a ; if =1 : down arrow has been pressed jr z, CursorDown cp 3 ; Else check up arrow (3+1) jr nz, Choose ; no : continue the loop ld a, (curItem) 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) ; unmark the current selected world call MarkItem 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 a, $40 out ($10), a ; Disable Y start offset res 3,(iy+$05) ; Normal mode res 0,(iy+$03) ; Don't regraph set 1,(iy+$0D) ; write to textshadow set 2,(iy+$0D) ; enable scrolling ROM_CALL(_clrScrn) ; Clear the LCD and the textshadow call GET_KEY ret NextPage: ld a, (top) add a, 8 ; Add top of list with 8 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 ld bc, $0C31 ; Fill 6*8+1 lines of 12*8 pixels ld hl, $0700 ; from line 7 (to line 56) ld a, $00 ; Clear all pixels call fillLCD jp ShowWorlds Select: ld a, (top) ld hl, curItem add a, (hl) ld hl, worlds add a, a ld d, 0 ld e, a add hl, de ; HL -> pointer to current world ld e, (hl) inc hl ld d, (hl) ; DE = world to play ex de, hl ld (Worldptr), hl ; And save the pointer ret ; this procedure invert the selection ; WITHOUT WRITING TO THE GRAPH BUFFER MarkItem: ld h, a ; save the row # add a, a add a, a ; 4*a add a, h add a, h ; 6*a MI_NoRowcalc: ld e,a ld b, 7 ; 7 lines to invert ;; di ld a, 7 ; go right after each write call LCDOut MI_NextRow: ld a, e add a,b add a, $80+7-1 ; go to line a+b-1 call LCDOut ld c, $20 ; invert one entire row InvertRow: ld a, c ; go to colomn ... call LCDOut call LCDBusy in a, ($11) ; dummy read call LCDBusy in a, ($11) cpl ld d, a ld a, c call LCDOut ld a, d call LCDBusy out ($11), a ; and show the inverted pixel octet inc c ld a, c cp $2C ; row finished ? jr nz, InvertRow djnz MI_NextRow ;; ei ld a, h ret ChangeOptions: ; Let the user change the game options call Showtitle set 3, (iy+$05) ; Why isn't this directly in ShowCInfo ?? call ShowCInfo res 3, (iy+$05) ld hl, $1D09 ld ($8215), hl ld hl, OptionsTxt ROM_CALL(_vputs) ld de, $2509 ld ($8215), de ROM_CALL(_vputs) ld de, $1509 ld ($8215), de ld a, (gaming) or a jr z, NoPause ld hl, ResumeTxt NoPause: ROM_CALL(_vputs) call UpdateSpeedOpt call UpdateBarsOpt set 3, (iy+$05) ResetOpt: xor a ld (curOpt), a UpdOpt: call UpdateCurOpt OptLoop: call WKey_A cp K_CLEAR jp z,Abort cp K_ENTER jr z, EntPressed ld hl, curOpt dec a ; Down arrow ? jr z, OptDown cp 3 ; Up arrow ? jr nz, OptLoop call UpdateCurOpt ld a, (hl) dec (hl) or a jr nz, UpdOpt ld (hl), 2 jr UpdOpt OptDown: call UpdateCurOpt ld a, (hl) inc (hl) cp 2 jr nz, UpdOpt jr ResetOpt EntPressed: ld a, (curopt) or a jr z, OptDone ; Start/Continue game dec a jr z, ChangeBars ; Status Bars ld hl, $2540 ; Else speed call ClearOpt ld hl, speed ld a, (hl) dec (hl) or a jr nz, UpdSpe ld (hl), 2 UpdSpe: call UpdateSpeedOpt jr OptLoop ChangeBars: ld hl, $1D40 call ClearOpt ld hl, bars ld a, (hl) xor 1 ld (hl), a call UpdateBarsOpt jr OptLoop OptDone: res 3,(iy+$05) ld a,(speed) add a,2 ld (speedC+1), a ; SelfModifying code - updates a cp xx in the source ret ClearOpt: ld ($8215), hl ld hl, OP1 ld (hl), 32 ld bc, 10 call FillChar ld (hl), 0 ld b,3 RepClear: ld hl, OP1 ROM_CALL(_vputs) djnz RepClear ret UpdateSpeedOpt: ; Show current screen option ld hl, $2540 ld ($8215), hl ld hl, SpeedOpt ld a, (speed) add a, a add a, a add a, a ; de = a*8 ld d, 0 ld e, a add hl, de ROM_CALL(_vputs) ret UpdateBarsOpt: ld hl, $1D40 ld ($8215), hl ld hl, BarsOpt ld a, (bars) add a, a add a, a add a, a ; de = a*8 ld d, 0 ld e, a add hl, de ROM_CALL(_vputs) ret UpdateCurOpt: push hl ld a, (curOpt) add a, a add a, a add a, a add a, 14 call MI_NoRowCalc pop hl ret ShowTitle: ld hl, Title ShowTopMsg: push hl ROM_CALL(_clrLCD) ld bc, $0C07 ; Fill 7 lines of 12*8 pixels ld hl, 0 ; from the beginning ld a, $FF ; with '1' call fillLCD ld hl,$0001 ld ($8215),hl set 3,(iy+$05) pop hl ROM_CALL(_vputs) res 3,(iy+$05) ret ShowCInfo: ld bc, $0C07 ; Fill 7 lines of 12*8 pixels ld hl, $3900 ; from line 57 (to the end) ld a, $FF ; with '1' call fillLCD ld hl,$3901 ld ($8215),hl ld hl,CInfo ROM_CALL(_vputs) ret FillChar: ld d, h ld e, l inc de ldir ret ScreenCopy: ; Really optimized ; But this could work badly on a turboed TI83-82 LD HL, $88B8 LD A, 7 DI ; the runindic and so on mustn't influence the LCD CALL LCDOut ; send 00000111 to LCD : cursor move right after write LD A,$80 ; Goto the top SC_LineLoop: LD D,A ; Save currect coloum CALL LCDOut LD A,$20 ; Goto left CALL LCDOut LD BC,$0C11 ; 12 bytes to port 011h SC_WriteLoop: ; Write 'em NEG NEG NEG OUTI JR NZ,SC_WriteLoop LD A,D INC A CP $C0 JR NZ,SC_LineLoop EI RET LCDBusy: ; This one also is really optimized PUSH AF POP AF RET LCDOut: PUSH AF POP AF OUT ($10), A RET fillLCD: ld e, a ; Save a ld a, 7 ;; di ; No int ! call LCDOut FillLoop: ld a, $80 add a,h ; Goto line h call LCDOut ld a, $20 add a,l ; goto col 8*l call LCDOut ld a, e ; Get a ld d, b ; we need to keep c unchanged lineloop: call LCDBusy out ($11), a djnz lineloop ld b, d inc h dec c ret z ; finished ? then exit jr FillLoop ; Move bar to top of screen (move all the screen 2 rows down) if needed movebars: ld a, (bars) or a ret z ld a, $7E out ($10), a ret ; Clear keypress and wait for another WKey_Cl: call GET_KEY ; Wait until a key other than arrows is pressed WaitKey: ei ; Enable interrupts WK_loop: call WKA_loop ; (skip the ei) cp 5 jr c, WK_loop ret ; Wait for a key INCLUDING arrows WKey_A: ei WKA_loop: HALT call GET_KEY or a jr z, WKA_loop ret WaitEnter: push hl WEnter: call WKey_A cp K_ENTER jr nz,WEnter pop hl ret ExtractA: ld h,0 ld l,a ld de, OP1+3 xor a ld (de), a ld b, 3 UnpackNo: call UNPACK_HL ; Divide HL by 10 -> hl, remainder -> a add a, 48 dec de ld (de), a djnz UnpackNo ret MDispA: call ExtractA ld b, 2 SkipZeros: ld a, (de) cp 48 jr nz,SZ_Done ld hl, $8215 ld a, (hl) add a, 4 ld (hl),a inc de djnz SkipZeros SZ_Done: ex de, hl ;; jp _vputs ROM_JUMP(_vputs) DispA: call ExtractA push de ld b,2 RemoveZeros: ld a, (de) cp 48 jr nz, RZ_Done ld a, 32 ld (de), a inc de djnz RemoveZeros RZ_Done: pop hl ; jp _puts ROM_JUMP(_puts) #include "ascr82.h" ; ----> End of code part <---- ; ----> Here starts the data part <---- ; First, the options speed: .db 1 ; 0=fast, 1=normal, 2=slow bars: .db 0 ;0=at bottom, 1=at top ExpTable: .db $01,$80,$40,$20,$10,$08,$04,$02,$01 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 txtmem: ;-----0123456789ABCDEF .db "Not enough mem",0 CInfo: .db $C1,$1E,"] ", $C1,$1F,"] " .db $C1,"Enter] ", $C1,"Clear]", 0 EnemyAddr: .dw Blob,Hedgehog,Bat,Greenman,Fish LevelTxt: .db "Level ",0 OutOfTimeTxt: .db "OUT OF TIME!",0 DrownedTxt: .db "YOU DROWNED!",0 GameFinTxt: ; 0123456789ABCDEF .db "CONGRATULATIONS!",0 .db "You have beaten all",0 .db "levels in this world!",0 OptionsTxt: .db "Status bars",0 .db "Speed",0 .db "Start game", 0 ResumeTxt: .db "Resume game",0 BarsOpt: .db "BOTTOM ",0 .db "TOP",0 SpeedOpt: .db "FAST ",0 .db "NORMAL ",0 .db "SLOW",0 Coder: .db "by Jimmy Mardell",0 .db "82 port by " .db "Florent Dhordain",0 .db "Title picture by",0 .db "Nicholas Reed",0 ; The cool titlepicture TitlePic: #include "sqtitle.h" ; The sprites #include "sqrxzgfx.h" ; With CrAsh, this part of the memory is free LevelPtr: .END