; Jet Set Willy JSW48 Game Engine Source ; ====================================== ; JSW Copyright (C) 1984 Matthew Smith & Software Projects ; Commentary Copyright (C) 1985, 2004 J.G.Harston ; See http://mdfs.net/Software/JSW/ ; The source is assembleable with ZMac ; See http://mdfs.net/Software/Z80/ZMac ; This was originally a commented disassembly, but it became easier to type ; up as a source file and create the disassembly from it. ; I have typed this up from my disassembly commentary from an 19-year-old ; exercise book ;) The guardian movement and Willy movement code is not ; commented as I treated it as "magic" code that did it's job! I'd welcome ; anyone else's commentary to be inserted, with appropriate credits. ; These flags control assembly of the JGH extensions. Set to 1 to turn ; them on. FIXPAUSE1 EQU 0 ; Pause bugfix version 1 FIXPAUSE2 EQU 0 ; Pause bugfix version 2 FIXPAUSE3 EQU 1 ; Pause bugfix version 3 FIXBLOCKS EQU 1 ; Block graphics bugfix GAMEEXIT EQU 1 ; Allow exit from game MOREROOMS EQU 1 ; 7-bit rooms UDTELEPORT EQU 0 ; Up/Down teleport routine WALLLEFT EQU 0 ; Check for wall when moving left WALLRIGHT EQU 0 ; Ignore wall when moving right ROOMSPRITE EQU 1 ; Room data specifies Willy's sprite? ; Geoff/Broad/Elliot extension ORG &8000 ; Program starts at &8000 ; Some terminology ; ================ ; Room ; 256 bytes of data that define a room and what guardians appear in it. ; Guardian Instance ; 2 bytes of data in a room specifying a guardian class and initial ; position. Each room can have up to eight guardian instances. ; Guardian Class ; 8 bytes of data specifying how a guardian moves, its colours, minimum ; and maximum positions and sprite. A JSW game can have up to 127 guardian ; classes, numbered 0-126. ; Sprite ; 32 byte 16x16pixel 1bpp image. Can be numbered in various ways: ; Sprite page/subpage: ; sprite address=Page*256+subpage*32 ; Sprite bank/sprite: ; sprite address=&8000+Bank*8192+sprite*32 ; Sprite Number: ; sprite address=&9B00+sprite*32 ; Guardian ; The guardian classes for a specific room specified by that room's guardian ; instances. ; With acknowledgements to Andrew Broad, JSWMM posting, 03-Apr-2004. ; Guardian Classes ; ================ ; A guardian class is defined by eight bytes in the guardian table at &A000 ; 0 - b7=Direction, b3-b0=Type ; Types are: 0=null, 1=horizontal, 2=vertical, 3=rope, ; 4=arrow, 5=horizontal, 6=vertical, 7=rope ; 1 - b7-b4=Animation, b3=BRIGHT, b2-b0=INK ; 2 - Initial X (from room data) ; 3 - Initial Y ; 4 - Speed ; 5 - Sprite page ; 6 - Movement Minimum ; 7 - Movement Maximum ; Screen buffers ; ============== ; To prevent flicker and give fast screen update, JSW writes to two screen ; buffers, copying from buffer 1 to buffer 2 before copying buffer 2 to the ; screen to be displayed. ; The current room is drawn to buffer 1. On each game tick buffer 1 is ; copied to buffer 2. The sprites are draw to buffer 2, then buffer 2 is ; copied to the screen. SCREEN EQU &4000 ; Screen SCREEN2 EQU &6000 ; Screen buffer 2 SCREEN1 EQU &7000 ; Screen buffer 1 ATTR EQU &5800 ; Attributes ATTR2 EQU &5C00 ; Attribute buffer 2 ATTR1 EQU &5E00 ; Attribute buffer 1 ; Program data areas ; ================== ATTRS EQU &9800 ; Startup and status attributes GUARDIANS EQU &A000 ; Guardian table OBJECTS EQU &A3FF ; Object table SPRITES EQU &AB00 ; Main sprites ROOMS EQU &C000 ; Base of room data ; Current room buffer ; =================== ; On entry to a room its definition is copied here. ROOM DEFS 256 NAME EQU ROOM+&80 BACKGROUND EQU ROOM+&A0 FLOOR EQU ROOM+&A9 WALL EQU ROOM+&B2 NASTY EQU ROOM+&BB SLOPE EQU ROOM+&C4 CONVEYOR EQU ROOM+&CD CONV_DIR EQU ROOM+&D6 CONV_PSN EQU ROOM+&D7 CONV_NUM EQU ROOM+&D9 SLOPE_DIR EQU ROOM+&DA SLOPE_PSN EQU ROOM+&DB SLOPE_NUM EQU ROOM+&DD BORDER EQU ROOM+&DE OBJECT EQU ROOM+&E1 LEFT EQU ROOM+&E9 RIGHT EQU ROOM+&EA UP EQU ROOM+&EB DOWN EQU ROOM+&EC WILLYSP EQU ROOM+&ED INSTANCES EQU ROOM+&F0 ; Current room's guardian instance buffer ; ======================================= ; The eight-byte data for each guardian specified in the current room at ; &80F0-&80FF is copied here. If you only save the program code from &8200 ; onwards, this terminator will be omitted. Any room with exactly eight ; guardians then continues using nonexistant guardian data past &8140. ; This can happen if a room's eighth guardian is a rope, as a rope uses ; sixteen bytes of guardian data and so will overwrite the terminator. GUARDIAN DEFS 64 ; Space for 8 guardian instances DEFB &FF ; This terminates the guardians DEFS &BF ; Spare ; Pixel-line lookup table ; ======================= ; The word at (PIXEL+line*2) is the address of the first character cell on ; pixel line 'line' in the second screen buffer. This makes converting pixel ; cell coordinates to screen address a lot faster and easier. PIXEL: DEFW &6000,&6100,&6200,&6300,&6400,&6500,&6600,&6700 ; line 0 DEFW &6020,&6120,&6220,&6320,&6420,&6520,&6620,&6720 ; line 1 DEFW &6040,&6140,&6240,&6340,&6440,&6540,&6640,&6740 ; line 2 DEFW &6060,&6160,&6260,&6360,&6460,&6560,&6660,&6760 ; line 3 DEFW &6080,&6180,&6280,&6380,&6480,&6580,&6680,&6780 ; line 4 DEFW &60A0,&61A0,&62A0,&63A0,&64A0,&65A0,&66A0,&67A0 ; line 5 DEFW &60C0,&61C0,&62C0,&63C0,&64C0,&65C0,&66C0,&67C0 ; line 6 DEFW &60E0,&61E0,&62E0,&63E0,&64E0,&65E0,&66E0,&67E0 ; line 7 DEFW &6800,&6900,&6A00,&6B00,&6C00,&6D00,&6E00,&6F00 ; line 8 DEFW &6820,&6920,&6A20,&6B20,&6C20,&6D20,&6E20,&6F20 ; line 9 DEFW &6840,&6940,&6A40,&6B40,&6C40,&6D40,&6E40,&6F40 ; line 10 DEFW &6860,&6960,&6A60,&6B60,&6C60,&6D60,&6E60,&6F60 ; line 11 DEFW &6880,&6980,&6A80,&6B80,&6C80,&6D80,&6E80,&6F80 ; line 12 PIXTOILET: DEFW &68A0,&69A0,&6AA0,&6BA0,&6CA0,&6DA0,&6EA0,&6FA0 ; line 13 DEFW &68C0,&69C0,&6AC0,&6BC0,&6CC0,&6DC0,&6EC0,&6FC0 ; line 14 DEFW &68E0,&69E0,&6AE0,&6BE0,&6CE0,&6DE0,&6EE0,&6FE0 ; line 15 ; Rope structure table ; ==================== ROPE: DEFB 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 ; Rope X offsets DEFB 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 DEFB 1,1,1,1,1,1,1,1,1,1,1,1,2,2,2,2 DEFB 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 DEFB 2,2,1,2,2,1,1,2,1,1,2,2,3,2,3,2 DEFB 3,3,3,3,3,3,0,0,0,0,0,0,0,0,0,0 DEFB 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 DEFB 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 DEFB 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6 ; Rope Y offsets DEFB 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6 DEFB 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6 DEFB 4,6,6,4,6,4,6,4,6,4,4,4,6,4,4,4 DEFB 4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4 DEFB 4,4,4,4,4,4,0,0,0,0,0,0,0,0,0,0 DEFB 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 DEFB 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 ; Program starts here ; =================== START: DI ; Disable interupts IF GAMEEXIT LD SP,&5C00 ; Stack is at &5B00-&5BFF JP GAMESTART ; Jump to GAMESTART to start ; Check a key to start the game, or exit to Basic ; =============================================== ; This is called from the intro screen. If SS-Space is pressed, Basic is ; re-entered with RAMTOP at &7FFF. Otherwise, Enter/Fire/0 are checked. L840D: LD A,&7F ; Keyrow 'b'-'spc' IN A,(&FE) ; Read from keyboard AND 3 ; SS-SPC pressed? JP NZ,L96C9 ; No, check Enter LD DE,&7FFF ; Top of memory JP &0005 ; Jump to NEW_ETC ; DEFB 0,0,0,0,0 ; Spare DEFB 0,0,0,0,0 ; Spare ELSE LD HL,&5BFF ; Stack is at &5B00-&5BFF LD (HL),GAMESTART / 256 ; Push GAMESTART onto stack DEC HL ; Original pushes PASSCHECK LD (HL),GAMESTART % 256 LD SP,&5BFE ; Set up stack pointer ; The following would scan through memory, but does nothing to it. It could ; be the remnents of some code decryption. Putting a RET at L840C releases ; this space for more code. The GameExit patch uses this space. L840C: SUB A ; Zero A register LD L,A ; Set L to zero XOR &0A ; A now equals &0A LD B,A INC B ; B now equals &0B LD H,B ; H now equals &0B RRC H ; Divide H by 2. HL now equals &0500 L8415: LD C,(HL) ; Get byte from memory LD A,L XOR C XOR H ; A=byte EOR (addr low) EOR (addr high) LD (HL),C ; If this was LD (HL),A then it INC HL ; would store back to memory BIT 7,H JR NZ,L8415 ; Loop until loops past &FF RET ; Jump to stacked GAMESTART to start ENDIF HERE: DEFB &00 ; Current room number ; Conveyor stuff ; -------------- L8421: DEFB 0,1,0,1,1,3,1,3,2,0,2,0,0,1,2,3 ; Bitmaps for triangle characters ; ------------------------------- L8431: DEFB &C0,&F0,&FC,&FF,&FF,&FF,&FF,&FF DEFB &00,&00,&00,&00,&C0,&F0,&FC,&FF DEFB &FF,&FF,&FF,&FF,&FC,&F0,&C0,&00 DEFB &FC,&F0,&C0,&00,&00,&00,&00,&00 ; Program Strings ; =============== L8451: DEFB "AIR" MESSAGE: DEFB "+++++ Press ENTER to Start +++++" DEFB " JET SET WILLY by Matthew Smith " DEFB &7F," 1984 SOFTWARE PROJECTS Ltd " DEFB ". . . . .Guide Willy to collect " DEFB "all the items around the house " DEFB "before Midnight so Maria will let " DEFB "you get to your bed. . . . . . ." DEFB "+++++ Press ENTER to Start +++++" ITEMS: DEFB "Items collected 000 Time 00:00 m" GAME: DEFB "Game" OVER: DEFB "Over" COLLECTED: DEFB "000" NOWTIME: DEFB " 7:00a" ; Current time STARTTIME: DEFB " 7:00a" ; Start time ; Password code entry prompts ; =========================== ; This space is spare if the password code is bypassed PROMPT1: DEFB "Enter Code at grid location " PROMPT2: DEFB "Sorry, try code at location " ; Game state variables ; ==================== TICKER: DEFB 0 ; Game ticker, 1/256th of a JSW minute LIVES: DEFB 0 ; Number of lives left FLASH: DEFB 0 ; Screen flash counter KEMPSTON: DEFB 0 ; Kempston joystick present ; Current Willy state ; =================== YPOSN: DEFB 0 ; Willy's Y position L85D0: DEFB 0 FALLING: DEFB 0 ; died/movement/falling state - -1/0/1/2 FRAME: DEFB 0 ; Willy's animation frame - 0/1/2/3 POSITION: DEFW 0 ; Willy's position on screen JUMPING: DEFB 0 ; jumping ONROPE: DEFB 0 ; rope flag - 0=not on a rope ; Preserved Willy state ; ===================== ; On entry to a room, Willy's state is saved here and restored if he dies. L85D7: DEFB 0,0,0,0,0,0,0 ; Seven bytes of Willy state ; Other variables ; =============== REMAIN: DEFB 0 ; Number of objects to collect. STATUS: DEFB 0 ; Game/finished/flee/vomit flag COUNTDOWN: DEFB 0 ; Pause countdown timer MTICK: DEFB 0 ; Music ticker MFLAGS: DEFB 0 ; Sound on/off flags ; b0: 0='h'-'ent' pressed ; b1: 0=play music, 1=no music TELEPORT: DEFB 0 ; Number of teleport keys matched ; 10=Teleport ON TEMP: DEFB 0 ; Temporary location ; Password entry codes ; ==================== ; Each keypress is encoded in two bytes, only b0-b4 relevant. ; First byte is the keystate to expect reading 'Q'-'T'. ; The second byte is the keystate to expect reading 'Y'-'P'. L85E5: DEFB &1F,&1F ; ---------- L85E7: DEFB &1D,&1F ; -W-------- DEFB &17,&1F ; ---R------ DEFB &1F,&1B ; -------I-- DEFB &0F,&1F ; ----T----- DEFB &1B,&1F ; --E------- DEFB &0F,&1F ; ----T----- DEFB &1F,&0F ; -----Y---- DEFB &1F,&1E ; ---------P DEFB &1B,&1F ; --E------- DEFB &17,&1F ; ---R------ ; Game Music Data ; =============== ; Moonlight Sonata is played at the intro screen MOONLIGHT: DEFB &51,&3C,&33,&51,&3C,&33,&51,&3C,&33,&51,&3C,&33 DEFB &51,&3C,&33,&51,&3C,&33,&51,&3C,&33,&51,&3C,&33 DEFB &4C,&3C,&33,&4C,&3C,&33,&4C,&39,&2D,&4C,&39,&2D DEFB &51,&40,&2D,&51,&3C,&33,&51,&3C,&36,&5B,&40,&36 DEFB &66,&51,&3C,&51,&3C,&33,&51,&3C,&33,&28,&3C,&28 DEFB &28,&36,&2D,&51,&36,&2D,&51,&36,&2D,&28,&36,&28 DEFB &28,&3C,&33,&51,&3C,&33,&26,&3C,&2D,&4C,&3C,&2D DEFB &28,&40,&33,&51,&40,&33,&2D,&40,&36,&20,&40,&36 DEFB &3D,&79,&3D,&FF ; Terminated with &FF ; If I Were A Rich Man is played through the game RICHMAN: DEFB &56,&60,&56,&60,&66,&66,&80,&80,&80,&80,&66,&60 DEFB &56,&60,&56,&60,&66,&60,&56,&4C,&48,&4C,&48,&4C DEFB &56,&56,&56,&56,&56,&56,&56,&56,&40,&40,&40,&40 DEFB &44,&44,&4C,&4C,&56,&60,&66,&60,&56,&56,&66,&66 DEFB &51,&56,&60,&56,&51,&51,&60,&60,&40,&40,&40,&40 DEFB &40,&40,&40,&40 ; 64 bytes long, just loops ; Copy Protection Password Entry Code ; =================================== ; This code space becomes free for other use if the password system is ; bypassed. PASSCHECK: LD HL,&4000 ; Clear screen LD DE,&4001 LD BC,&1AFF LD (HL),&00 LDIR LD IX,PROMPT1 ; Point to message 1 CALL PASSASK ; Ask for a password JP Z,GAMESTART ; Password ok, enter game LD IX,PROMPT2 ; Point to message 2 CALL PASSASK ; Ask for a password JP Z,GAMESTART ; Password ok, enter game JP &0000 ; Reset ; Ask for a password, displaying the message pointed to by IX ; ----------------------------------------------------------- PASSASK: LD DE,&4800 ; Point to line 8 LD C,&20 ; 32 characters CALL PRMESSAGE ; Print the message LD HL,&4842 ; Column 2, line 10 LD DE,&9B00 ; Point to '1' block LD C,&00 ; Ignore collisions CALL DRAWSPRITE ; Draw the sprite to screen LD HL,&4845 ; Column 5, line 10 CALL DRAWSPRITE ; Draw '2' block LD HL,&4848 ; Column 8, line 10 CALL DRAWSPRITE ; Draw '3' block LD HL,&484B ; Column 11, line 10 CALL DRAWSPRITE ; Draw '4' block LD HL,&9B80 ; Point to password screen attributes LD DE,&5900 ; Point to screen middle third LD BC,&0080 ; Four lines of attributes LDIR ; Copy attributes to screen LD A,(&5C78) ; Get FRAMES to seed randomiser ADD A,&25 LD (&5C78),A ; Store updated FRAMES for next call CP &B3 ; There are 180 codes. If FRAMES>&B3 JR C,L8701 ; reduce by 180. SUB &B4 L8701: LD L,A ; Index into passcodes in &9Exx LD H,&9E LD A,(HL) ; Get passcode ADD A,L ; Add offset to passcode LD (TEMP),A ; Store in temp location LD C,L ; Calculate code location LD E,&2F ; Start at digit '0'-1 L870C: INC E ; Increment digit LD A,C CP &12 JR C,L8717 ; Jump to ask for passcode SUB &12 LD C,A JR L870C ; Loop to manipulate passcode L8717: LD A,E ; Get digit LD DE,&481E ; Column 30, line 10 CALL PRCHAR ; Print digit LD A,C ; Get letter offset ADD A,&41 ; Add to 'A' LD DE,&481D ; Coulmn 29, line 10 CALL PRCHAR ; Print letter L8727: LD IX,&5950 ; Point to attributes for 1st block L872B: CALL PASSKEY ; Get keypress and colour in a block INC IX INC IX INC IX ; Bump IX up to point to next block DEFB &DD LD A,L ; LD A,IXL CP &5C ; Got to nonexistant 5th block? JR NZ,L872B ; No, loop to get another keypress JR L8727 ; Loop back to 1st block ; Get a passcode keypress ; ----------------------- PASSKEY: LD BC,&F7FE ; Keyboard row '1'-'5' IN A,(C) ; Read from keyboard AND &0F ; Keep '1'-'4' only CP &0F ; No keys pressed? JR NZ,PASSKEY ; Loop until *no* key pressed L8747: LD B,&BF ; Keyboard row 'H'-'ent' IN A,(C) ; Read from keyboard BIT 0,A ; Is 'Enter' pressed? JR NZ,L8785 ; No, jump to look for digit keys LD A,(&5959) ; Get attribute of 4th block AND &7F ; Lose flash bit CP &07 ; Is it still white ink? JR Z,L8785 ; Jump to keep looking for digit keys SUB &08 AND &18 ; Keep two bits RRCA RRCA RRCA LD C,A LD A,(&5953) ; Get attribute of 2nd block SUB &08 AND &18 ; Keep two bits RLCA OR C LD C,A LD A,(&5956) ; Get attribute of 3rd block SUB &08 AND &18 ; Keep two bits RRCA OR C LD C,A LD A,(&5950) ; Get attribute of 1st block SUB &08 AND &18 ; Keep two bits RLCA RLCA RLCA POP HL ; Lose return address OR C ; Clear carry flag LD HL,TEMP ; Point to temp store CP (HL) ; Compare with entered passcode RET ; Return Z=Code Matches L8785: SET 7,(IX+&00) ; Make current block flashing SET 7,(IX+&01) SET 7,(IX+&20) SET 7,(IX+&21) LD BC,&F7FE ; Keyboard row '1' to '5' IN A,(C) ; Read keyboard AND &0F ; Keep '1'-'4' LD E,&08 ; Prepare for blue paper CP &0E ; '1' pressed? JR Z,L87B5 ; Yes, jump to set LD E,&10 ; Prepare for red paper CP &0D ; '2' pressed? JR Z,L87B5 ; Yes, jump to set LD E,&18 ; Prepare for magenta paper CP &0B ; '3' pressed? JR Z,L87B5 ; Yes, jump to set LD E,&20 ; Prepare for green paper CP &07 ; '4' pressed? JP NZ,L8747 ; No, loop back to wait again L87B5: LD (IX+&00),E ; Set attribute of the current block LD (IX+&01),E LD (IX+&20),E LD (IX+&21),E LD BC,&0018 ; Loop &1800 times L87C4: DJNZ L87C4 ; Slight pause DEC C JR NZ,L87C4 RET ; JSW48 Game Engine ; ================= ; The actual game engine starts at GAMESTART. The code is entered here ; from the password protection code, or direct from startup if the ; password scheme is bypassed. ; Introduction screen ; ------------------- GAMESTART: XOR A ; Clear a load of variables LD (KEMPSTON),A ; Kempston joystick not present LD (MTICK),A ; Music ticker LD (FLASH),A ; No screen flash LD (FALLING),A ; Willy's not falling LD (TICKER),A ; Game ticker counter LD (COUNTDOWN),A ; Autopause countdown timer LD (STATUS),A ; Normal gameplay LD A,&07 LD (LIVES),A ; Set lives to 8 minus 1 LD A,&D0 LD (YPOSN),A ; Willy's pixel-line coordinate LD A,&21 LD (HERE),A ; Set HERE to 33 - The Bathroom LD HL,&5DB4 ; Put Willy at column 20, line 13 LD (POSITION),HL ; Set Willy's location LD HL,COLLECTED ; Point to item count LD (HL),&30 ; Initialise to "000" INC HL LD (HL),&30 INC HL LD (HL),&30 IF MOREROOMS LD H,&A6 ; Point to relocated collection flags ELSE LD H,&A4 ; Point to object collection flags ENDIF LD A,(OBJECTS) ; Get object count LD L,A LD (REMAIN),A ; Set 'objects remaining' L8809: SET 6,(HL) ; Clear 'object collected flag' INC L JR NZ,L8809 ; Loop for all objects LD HL,MFLAGS SET 0,(HL) ; 'mute' keys not being pressed L8813: LD HL,&4000 ; Point to screen LD DE,&4001 LD BC,&17FF LD (HL),&00 LDIR ; Clear screen LD HL,ATTRS ; Point to intro screen attributes LD BC,&0300 LDIR ; Copy attributes to screen LD HL,&5A60 ; Point to attributes for line 16 LD DE,&5A61 LD BC,&001F LD (HL),&46 ; Bright yellow on black LDIR ; Set attributes for line 16 LD IX,MESSAGE ; Point to "Press SPACE..." LD DE,&5060 ; Point to line 16 LD C,&20 ; 32 characters to print CALL PRMESSAGE ; Print the string LD DE,&5800 ; Draw the triangle based on the L8844: LD A,(DE) ; attributes on the screen OR A JR Z,L888E ; Black/Black - block CP &D3 JR Z,L888E ; Red/Magenta - block CP &09 JR Z,L888E ; Blue/Blue - block CP &2D JR Z,L888E ; Cyan/Cyan - block CP &24 JR Z,L888E ; Green/Green - block LD C,&00 CP &08 JR Z,L8871 CP &29 JR Z,L8871 CP &2C JR Z,L886E CP &05 JR Z,L8871 LD C,&10 JR L8871 L886E: LD A,&25 LD (DE),A L8871: LD A,E AND &01 RLCA RLCA RLCA OR C LD C,A LD B,&00 LD HL,L8431 ; Index into triangle bitmaps ADD HL,BC PUSH DE BIT 0,D LD D,&40 JR Z,L8888 LD D,&48 L8888: LD B,&08 CALL PRMATRIX POP DE L888E: INC DE ; Move to next attribute LD A,D ; Check address high byte CP &5A ; Got to &5A00 yet? JP NZ,L8844 ; Loop back for whole triangle LD BC,&001F ; Check for kempston joystick DI ; But INTs are already disabled XOR A L889A: IN E,(C) ; Read IN 31 OR E DJNZ L889A ; Loop to read 256 times AND &20 ; Check out of range value JR NZ,L88A8 ; If set, no joystick present LD A,&01 LD (KEMPSTON),A ; Set joystick flag L88A8: LD HL,MOONLIGHT ; Point to intro screen music CALL L96A2 ; Play until keypress JP NZ,L88FC ; If ENTER/FIRE/0 pressed, start game XOR A ; Set scroll offset to zero LD (TEMP),A ; Save in temp store L88B5: CALL L8AEB ; Change screen attributes LD HL,&5A60 ; Ensure line 19 is white ink on LD DE,&5A61 ; blue paper LD BC,&001F LD (HL),&4F LDIR LD A,(TEMP) ; Get scroll offset LD IX,MESSAGE ; Point to scrolling message LD E,A ; Pass scroll offset into DE LD D,&00 ADD IX,DE ; Add scroll offset into message LD DE,&5060 ; Point to line 19 LD C,&20 ; 32 characters CALL PRMESSAGE ; Print the string LD A,(TEMP) ; Get scroll offset AND &1F ; Reduce to 0-31 ADD A,&32 ; Add 50 -> 50-81 CALL L96DE ; Play a note IF GAMEEXIT CALL L840D ; Check ENTER/FIRE/SS-SPACE DEFB 0,0,0,0,0,0 ELSE LD BC,&AFFE ; Keyboard row 'h'-'ent' IN A,(C) ; Read keyboard AND &01 ; Keep 'enter' CP &01 ; Is it pressed? ENDIF JR NZ,L88FC ; ENTER pressed, enter current room LD A,(TEMP) ; Get scroll offset INC A ; Increment offset CP &E0 ; End of message yet? LD (TEMP),A ; Store offset back to temp location JR NZ,L88B5 ; Loop to keep scrolling JP L8813 ; Jump back to play intro music ; Play the game ; ============= ; Before starting the game, the screen is cleared and the clock is set to ; 7:00am. L88FC: LD HL,STARTTIME ; Point to start time LD DE,NOWTIME ; Point to current time LD BC,&0006 ; Six characters LDIR ; Set clock to 7:00am LD HL,ATTRS+512 ; Point to bottom third attributes LD DE,&5A00 LD BC,&0100 LDIR ; Set attrs of bottom of screen ; Play a room ; =========== ; Entry here plays the room that is in HERE with Willy's position in ; POSITION. Any movement between rooms jumps straight back to here. PLAYROOM: LD A,(HERE) ; Get current room IF MOREROOMS XOR &C0 ; Convert to room data address ELSE OR &C0 ; Convert to room data address ENDIF LD H,A ; Rooms start at &C000 LD L,&00 LD DE,ROOM ; Point to room buffer LD BC,&0100 ; A room definition is 256 bytes LDIR ; Copy current room to room buffer L8922: LD IX,INSTANCES ; Point to this room's guardian data LD DE,GUARDIAN ; Point to expanded guardian data LD A,&08 ; There are a maximum of 8 guardians L892B: LD L,(IX+&00) ; Get guardian number RES 7,L ; Ensure 0-127 LD H,&14 ; Guardian table address DIV &800 ADD HL,HL ADD HL,HL ADD HL,HL ; HL=&A000+guardian*8 LD BC,&0002 ; Copy first two bytes of data LDIR LD C,(IX+&01) ; Get guardian info byte LD (HL),C ; Put into guardian data LD BC,&0006 ; Copy into guardian buffer LDIR INC IX ; Move to next guardian entry INC IX DEC A JR NZ,L892B ; Loop for eight entries LD HL,YPOSN ; Current Willy state LD DE,L85D7 ; Preserved Willy state LD BC,&0007 LDIR ; Preserve current Willy state CALL DRAWROOM ; Draw room to buffer 1 LD HL,&5000 LD DE,&5001 LD BC,&07FF LD (HL),&00 LDIR ; Clear bottom third of screen LD IX,NAME ; Point to room name LD C,&20 ; 32 characters LD DE,&5000 ; Point to line 16 CALL PRMESSAGE ; Display room name LD IX,ITEMS ; Point to "Items collected..." LD DE,&5060 ; Point to line 19 LD C,&20 ; 32 characters CALL PRMESSAGE ; Print string LD A,(BORDER) ; Get border colour LD C,&FE OUT (C),A ; Set border colour XOR A LD (ONROPE),A ; Willy is not on a rope JP ACTION ; Jump to action loop ; Print moving Willies along the bottom of the screen ; =================================================== ; One Willy is displayed for each remaining life. The Willies are animated ; according to the music ticker. If music is turned off the Willies will stand ; still. DRAWLIVES: LD A,(LIVES) ; Get number of remaining lives LD HL,&50A0 ; Point to line 21 OR A RET Z ; Exit with zero remaining lives LD B,A ; Loop for remaining lives L8994: LD C,&00 ; Ignore collisions PUSH HL ; Save registers PUSH BC LD A,(MTICK) ; Get music ticker RLCA RLCA RLCA ; Multiply by eight AND &60 ; Offset to sprite 0, 1, 2 or 3 LD E,A ; Low byte of sprite address LD D,&9D ; Willies are in page &9D CALL DRAWSPRITE ; Draw sprite POP BC ; Restore registers POP HL INC HL ; Move to next positon on screen INC HL DJNZ L8994 ; Loop for remaining lives RET ; Action loop ; =========== ACTION: CALL DRAWLIVES ; Display remaining lives Willies LD HL,&5E00 ; Point to attribute buffer 1 LD DE,&5C00 ; Point to attribute buffer 2 LD BC,&0200 LDIR ; Copy from attrs buffer 1 to 2 LD HL,&7000 ; Point to screen buffer 1 LD DE,&6000 ; Point to screen buffer 2 LD BC,&1000 LDIR ; Copy from screen buffer 1 to 2 CALL UPDATEGUARD ; Update guardians LD A,(STATUS) ; CP &03 ; Is Willy throwing up? CALL NZ,MOVEMENT ; No, move Willy LD A,(YPOSN) ; Get Willy's position CP &E1 ; Above the top of the screen? CALL NC,GO_UP ; Go up LD A,(STATUS) ; Is Willy throwing up? CP &03 ; If not, check what Willy is standing CALL NZ,UPDATEWILLY ; on and draw him LD A,(STATUS) CP &02 ; Is Willy fleeing? CALL Z,CHKTOILET ; Yes, check if toilet reached yet CALL SPECIAL ; Draw Maira and Toilet if needed CALL DRAWGUARD ; Draw guardians to buffer 2 CALL UPDATECONV ; Update conveyors in buffer 2 CALL CHKOBJECTS ; Draw objects to buffer 2 L89F5: LD HL,&6000 ; Point to screen 2 LD DE,&4000 ; Point to displayed screen LD BC,&1000 LDIR ; Copy screen buffer 2 to screen LD A,(STATUS) AND &02 ; Keep flee/vomit bit RRCA ; Move to bit 0 LD HL,FRAME ; Willy's animation frame OR (HL) ; Merge vomit bit into frame LD (HL),A ; Only frames 2 and 3 if vomiting ; The following code flashes the PAPER over the whole of the top two thirds ; of the screen from white down to black, if FLASH is nonzero. This appears ; to be a left-over from Manic Miner. [???] LD A,(FLASH) ; Get screen flash counter OR A ; Is it zero? JR Z,L8A26 ; No screen flash, jump past DEC A ; Decrease flash counter LD (FLASH),A RLCA ; Move up into PAPER RLCA RLCA AND &38 ; Set PAPER with black INK LD HL,&5C00 LD DE,&5C01 LD BC,&01FF LD (HL),A ; Set first attribute in buffer 2 LDIR ; Set all attributes L8A26: LD HL,&5C00 ; Point to attribute buffer 2 LD DE,&5800 ; Point to displayed attributes LD BC,&0200 LDIR ; Copy attrs from buffer 2 to screen LD IX,NOWTIME ; Point to current time LD DE,&5079 ; Point to line 19, column 25 LD C,&06 ; "xx:xx?m"=six characters CALL PRMESSAGE ; Display clock LD IX,COLLECTED ; Point to items collected string LD DE,&5070 ; Point to line 19, column 16 LD C,&03 ; "xxx"=three characters CALL PRMESSAGE ; Display collected items count ; Update JSW clock ; ---------------- ; JSW runs 256 ticks of the TICKER variable per JSW minute. One JSW minute ; equals about 15 real seconds, so the JSW clock runs about four times faster ; than reality. LD A,(TICKER) ; Get TICKER INC A ; Increment TICKER LD (TICKER),A ; Store updated TICKER JR NZ,L8AAB ; TICKER<>0, so jump forward LD IX,NOWTIME ; Point to displayed clock to update INC (IX+&04) ; Increment minutes units LD A,(IX+&04) ; Get minutes units CP &3A ; Overflowed past '9'? JR NZ,L8AAB ; No, jump to continue LD (IX+&04),&30 ; Reset minutes units to '0' INC (IX+&03) ; Increment minutes tens digit LD A,(IX+&03) ; Get minutes tens digit CP &36 ; Overflowed past '5'? JR NZ,L8AAB ; No, jump to continue LD (IX+&03),&30 ; Reset minutes tens digit to '0' LD A,(IX+&00) ; Get Hours tens digit CP &31 ; Is it '1'? JR NZ,L8A99 ; No, jump to update hours<10 INC (IX+&01) ; Increment hours units digit LD A,(IX+&01) ; Get hours unit digit CP &33 ; Overflowed past '2' (ie '12')? JR NZ,L8AAB ; No, jump to continue LD A,(IX+&05) ; Get 'am' or 'pm' character CP &70 ; Is is 'p' for 'pm'? JP Z,GAMESTART ; Yes -> midnight -> quit game LD (IX+&00),&20 ; ' ' - set to "1:00pm" LD (IX+&01),&31 ; '1' LD (IX+&05),&70 ; 'p' JR L8AAB ; Jump to continue L8A99: INC (IX+&01) ; Update hours unit digit LD A,(IX+&01) ; Get hours unit digit CP &3A ; Overflowed past '9'? JR NZ,L8AAB ; No, jump to continue LD (IX+&01),&30 ; Set hours to '10' LD (IX+&00),&31 L8AAB: LD BC,&FEFE ; Keyboard row 'b'-'spc' IN A,(C) ; Read from keyboard LD E,A ; Save result in E LD B,&7F ; Keyboard row 'sht'-'v' IN A,(C) ; Read from keyboard OR E ; Merge both rows AND &01 ; Keep Shift+Space JP Z,GAMESTART ; If pressed, go back to intro L8ABB: LD A,(COUNTDOWN) INC A LD (COUNTDOWN),A ; Update autopause counter JR Z,L8AD1 LD B,&FD IN A,(C) AND &1F CP &1F JR Z,L8B17 LD DE,&0000 L8AD1: LD B,&02 ; Pause loop IN A,(C) AND &1F CP &1F JR NZ,L8B07 INC E JR NZ,L8AD1 INC D JR NZ,L8AD1 LD A,(TELEPORT) ; Get teleport flag CP &0A ; Is teleport on? CALL NZ,L8AEB ; No, change background colours JR L8AD1 ; Loop to keep pausing ; Change background colours while paused ; -------------------------------------- ; While paused all the INKs on the screen change by have 3 added, and all the ; PAPERs have 1 added, and the border is set to the INK of the first character ; on screen. L8AEB: LD HL,&5800 ; Point to displayed attributes LD A,(HL) ; AND &07 ; Keep INK OUT (&FE),A ; Set border colour L8AF3: LD A,(HL) ; Get attribute ADD A,&03 ; Cycle INK by 3 AND &07 LD D,A LD A,(HL) ; Get attribute again ADD A,&18 ; Cycle PAPER by 1 AND &B8 OR D ; Mask in updated INK LD (HL),A ; Store back to attributes INC HL ; Move to next character cell LD A,H ; Loop for all 24 lines. Fix PAUSE ; bug by changing this to CP &5A IF FIXPAUSE1 CP &5A ; Loop for top two thirds attributes ELSE CP &5B ENDIF JR NZ,L8AF3 ; Loop for all attributes RET L8B07: LD HL,ATTRS+512 ; Point to attrs for lower screen LD DE,&5A00 ; Point to lower screen LD BC,&0100 ; Copy 256 bytes to screen ; This is where the PAUSE bug appears. C now holds &00 and will still hold ; &00 after the following LDIR. The IN (C) instruction at &8B24 assumes ; that C holds &FE. This results in an IN 0 which crashes the Interface 1. ; If the attribute-changing routine at &8AEB doesn't change the bottom of ; the screen, then this LDIR is not needed and can be changed to LD C,&FE ; instead. IF FIXPAUSE1 LD C,&FE ; Restore keyboard i/o port ELSE LDIR ; Change to LD C,&FE to fix PAUSE bug ENDIF LD A,(BORDER) ; Get border colour OUT (&FE),A ; Set border L8B17: LD A,(FALLING) ; Get flag ; An alternate pause fix is to crunch the following code down to release ; a spare byte to replace the LD B,&BF with LD BC,&BFFE. IF FIXPAUSE2 INC A ; &FF->&00? JP Z,LOSTLIFE ; Lost a life LD BC,&BFFE ; 'h'-'enter' LD HL,MFLAGS IN A,(C) ; Read keyboard row ENDIF ; Another alternative is to read from port &FE directly instead of using ; the zeroed BC register. IF FIXPAUSE3 CP &FF ; &FF? JP Z,LOSTLIFE ; Lost a life LD A,&BF ; 'h'-'enter' LD HL,MFLAGS IN A,(&FE) ; Read keyboard row ENDIF IF FIXPAUSE1+FIXPAUSE2+FIXPAUSE3=0 CP &FF ; &FF? JP Z,LOSTLIFE ; Lost a life LD B,&BF ; 'h'-'enter' LD HL,MFLAGS IN A,(BC) ; Read keyboard row ENDIF AND &1F ; Keep keys CP &1F ; Are any keys pressed? JR Z,L8B36 ; No key pressed, jump ... BIT 0,(HL) JR NZ,L8B38 LD A,(HL) XOR &03 LD (HL),A JR L8B38 L8B36: RES 0,(HL) L8B38: BIT 1,(HL) ; Is music enabled? JR NZ,L8B70 ; No, jump to check teleport XOR A LD (COUNTDOWN),A LD A,(MTICK) ; Increment music ticker INC A LD (MTICK),A ; Play in-game music ; ----------------- AND &7E ; Lose bit 7 & bit 0 RRCA ; Divide by 2 -> A=0..63 LD E,A ; Pass to DE. A new note is played L8B4B: LD D,&00 ; every two game ticks. LD HL,RICHMAN ; Point to in-game music ADD HL,DE ; Point to current note LD A,(LIVES) ; Get number of remaining lives RLCA ; Note gets lower with fewer lives RLCA ; Lives*4 SUB &1C ; Lives*4-&1C NEG ; &1C-lives*4 -> 0..28 ADD A,(HL) ; Add to current note LD D,A ; Save current note in D LD A,(BORDER) ; Get border colour LD E,D ; Initialise E with current note LD BC,&0003 L8B63: OUT (&FE),A ; Set buzzer state DEC E JR NZ,L8B6B ; Loop for note delay LD E,D ; Reinitialise E with current note XOR &18 ; Toggle buzzer state L8B6B: DJNZ L8B63 ; Loop 256 times with this note DEC C JR NZ,L8B63 ; Loop 768 times with this note ; Check for teleport jump ; ----------------------- L8B70: LD BC,&EFFE ; Keyrow '6'-'0' IN A,(C) ; Read keyboard BIT 1,A ; Is '9' pressed? JP NZ,L8B97 ; No, jump to check for password AND &10 ; Keep '6' keypress XOR &10 ; Flip bit RLCA ; Move '6' up to bit 5 LD D,A ; Save in D for later LD A,(TELEPORT) ; Get teleport state CP &0A ; Is teleport on? JP NZ,L8B97 ; No, jump to check for password LD BC,&F7FE ; Keyrow '1'-'5' IN A,(C) ; Read keyboard CPL ; Flip bits AND &1F ; Keep '1'-'5' OR D ; Merge bit 5 in from earlier LD (HERE),A ; Store as current room JP PLAYROOM ; And jump to enter it ; Check for teleport password entry ; --------------------------------- L8B97: LD A,(TELEPORT) ; Get teleport state CP &0A ; Is teleport already on? JP Z,ACTION ; Yes, go back to action loop LD A,(HERE) ; Are we in room 28? CP &1C ; (The Main Landing) JP NZ,ACTION ; No, go back to action loop LD A,(YPOSN) ; Get Willy's pixel-line position CP &D0 ; Are we on the floor? JP NZ,ACTION ; No, go back to action loop LD A,(TELEPORT) ; Get teleport state RLCA ; Double it to index into keycodes LD E,A LD D,&00 LD IX,L85E7 ; Add to keycodes base ADD IX,DE ; IX=>next keycode to expect LD BC,&FBFE ; Keyrow 'Q'-'T' IN A,(C) ; Read from keyboard AND &1F ; Keep keys CP (IX+&00) ; Does it match expected keystate? JR Z,L8BDA ; Yes, jump to check other half-row CP &1F ; Are no keys pressed? JP Z,ACTION ; Yes, return to action loop CP (IX-&02) ; Does it match previous keystate? JP Z,ACTION ; Yes, return to action loop XOR A ; Otherwise, reset teleport state LD (TELEPORT),A JP ACTION ; Return to action loop L8BDA: LD B,&DF ; Keyrow 'Y'-'P' IN A,(C) ; Read keyboard AND &1F ; Keep keys CP (IX+&01) ; Does it match expected keystate? JR Z,L8BF7 ; Yes, jump to update teleport state CP &1F ; Are no keys pressed? JP Z,ACTION ; Yes, return to action loop CP (IX-&01) ; Does it match previous keystate? JP Z,ACTION ; Yes, return to action loop XOR A ; Otherwise, resetl teleport state LD (TELEPORT),A JP ACTION ; Return to action loop L8BF7: LD A,(TELEPORT) ; Get teleport state INC A ; One more keypress has been matched LD (TELEPORT),A ; Update teleport state JP ACTION ; Return to action loop ; Lost a life ; =========== ; When Willy dies the screen colours cycle from white on black to black ; on black, with a squeeking sound. If there are no lives remaining, the ; game ends. Otherwise, Willy's state is restored to when he entered the ; room and the room is re-entered. LOSTLIFE: LD A,&47 ; White INK on black PAPER L8C03: LD HL,&5800 ; Point to start of attributes LD DE,&5801 LD BC,&01FF ; Top two thirds of screen LD (HL),A ; Set first attribute LDIR ; Set all attributes LD E,A ; Save current attributes in E CPL ; Flip all bits AND &07 ; Keep INK RLCA ; Move up three bits RLCA RLCA ; Buzzer pause is 8*(7-old INK) OR &07 ; Set INK to white LD D,A ; Save in D LD C,E ; Copy old attributes to C RRC C ; Move down three bits RRC C RRC C ; Buzzer delay in C is old PAPER OR &10 ; Superfluous instruction? XOR A ; Clear A L8C23: OUT (&FE),A ; Set border and buzzer XOR &18 ; Flip buzzer state LD B,D ; L8C28: DJNZ L8C28 ; Pause according to INK value DEC C ; Loop according to PAPER value JR NZ,L8C23 ; Loop to make sound LD A,E ; Get saved attributes DEC A ; Decrease attribute CP &3F ; Wrapped past black on black? JR NZ,L8C03 ; Loop to cycles through colours LD HL,LIVES LD A,(HL) ; Get remaining lives OR A ; No remaining lives? JP Z,GAMEOVER ; None left - Game Over DEC (HL) ; Decrease remaining lives LD HL,L85D7 ; Point to saved Willy state LD DE,YPOSN ; Point to current Willy state LD BC,&0007 ; 7 bytes in total LDIR ; Restore Willy state JP PLAYROOM ; Jump to reenter current room ; Game Over ; ========= ; Willy has died and has no lives remaining. Clear the top of the screen and ; drop the foot onto Willy standing on the barrel. GAMEOVER: LD HL,&4000 ; Point to screen LD DE,&4001 LD BC,&0FFF LD (HL),&00 ; Clear first byte LDIR ; Clear top two thirds of screen XOR A ; TEMP holds pixel-line*2 LD (TEMP),A ; Start at pixel-line 0 LD DE,&9D40 ; Point to a Willy sprite LD HL,&488F ; Point to line 12, column 15 LD C,&00 ; Ignore collisions CALL DRAWSPRITE ; Draw Willy to screen LD DE,&9C60 ; Point to barrel sprite LD HL,&48CF ; Point to line 14, column 15 LD C,&00 ; Ignore collisions CALL DRAWSPRITE ; Draw barrel to screen ; Now loop to draw the foot descending above Willy. As the foot descends it ; leaves two lines of pixels that generates the leg. L8C71: LD A,(TEMP) ; Get pixel-line*2 LD C,A ; Look up screen address LD B,PIXEL / 256 ; BC=> LD A,(BC) ; Get low byte of pixel-line address OR &0F ; Set to column 15 LD L,A ; Store in L INC BC ; Point to high byte of address LD A,(BC) ; Get high byte of pixel-line address SUB &20 ; Convert address from buffer 2 to LD H,A ; screen address and store in H ; HL now points to pixel-line in TEMP, ; column 15 on the displayed screen LD DE,&9C40 ; Point to foot sprite LD C,&00 ; Ignore collisions CALL DRAWSPRITE ; Draw foot to screen LD A,(TEMP) ; Get pixel-line to generate sound CPL ; Delay gets shorter as foot decends ; So, pitch gets higher LD E,A ; Set buzzer delay XOR A ; Clear initial buzzer state LD BC,&0040 ; Toggle buzzer 64 times L8C91: OUT (&FE),A ; Output to buzzer XOR &18 ; Toggle buzzer state LD B,E ; Get delay value L8C96: DJNZ L8C96 ; Delay according to foot height DEC C JR NZ,L8C91 ; Loop to toggle buzzer LD HL,&5800 ; Point to attributes LD DE,&5801 LD BC,&01FF ; Top two thirds of screen LD A,(TEMP) ; Get pixel-line to generate colour AND &0C ; Keep b2-b3 RLCA ; Move up to PAPER OR &47 ; Ensure bright white INK LD (HL),A ; Set first attribute LDIR ; Copy to top two thirds of screen AND &FA ; Lose INK OR &02 ; Set INK to red LD (&59CF),A ; Set attributes for barrel LD (&59D0),A LD (&59EF),A LD (&59F0),A LD A,(TEMP) ; Get pixel-line ADD A,&04 ; Move down two lines LD (TEMP),A ; Store pixel-line CP &C4 ; Has foot got past pixel-line 192? JR NZ,L8C71 ; Loop until foot reaches barrel LD IX,GAME ; Point to "Game" LD C,&04 ; 4 characters LD DE,&40CA ; Line 6, column 10 CALL PRMESSAGE ; Print the message LD IX,OVER ; Point to "Over" LD C,&04 ; 4 characters LD DE,&40D2 ; Line 6, column 18 CALL PRMESSAGE ; Print the message ; Print "Game Over" and wobble the colours LD BC,&0000 ; C=initial colour, B=initial loop counter LD D,&06 ; Loop 6*256 times L8CE8: DJNZ L8CE8 ; Pause for 256 loops LD A,C ; Get current colour AND &07 ; Keep INK OR &40 ; Ensure BRIGHT LD (&58CA),A ; Set attribute of "G" INC A ; Increment INK AND &07 ; Ensure black PAPER OR &40 ; Ensure BRIGHT LD (&58CB),A ; Set attribute of "a" INC A ; Increment INK AND &07 ; Ensure black PAPER OR &40 ; Ensure BRIGHT LD (&58CC),A ; Set attribute of "m" INC A ; Increment INK AND &07 ; Ensure black PAPER OR &40 ; Ensure BRIGHT LD (&58CD),A ; Set attribute of "e" INC A ; Increment INK AND &07 ; Ensure black PAPER OR &40 ; Ensure BRIGHT LD (&58D2),A ; Set attribute of "O" INC A ; Increment INK AND &07 ; Ensure black PAPER OR &40 ; Ensure BRIGHT LD (&58D3),A ; Set attribute of "v" INC A ; Increment INK AND &07 ; Ensure black PAPER OR &40 ; Ensure BRIGHT LD (&58D4),A ; Set attribute of "e" INC A ; Increment INK AND &07 ; Ensure black PAPER OR &40 ; Ensure BRIGHT LD (&58D5),A ; Set attribute of "r" DEC C ; Decrement colour JR NZ,L8CE8 ; Loop 256 times DEC D ; Decrement main loop counter JR NZ,L8CE8 ; Loop 6*256 times JP GAMESTART ; Go back to intro screen ; Draw the current room to Buffer 1 ; ================================= DRAWROOM: CALL BUILDROOM ; Build up the attributes for the room LD IX,&5E00 ; Point to top third attributes LD A,&70 ; Point to top third of buffer 1 LD (L8D5C+1),A ; Poke into later code CALL L8D4B ; Draw the characters for the attributes LD IX,&5F00 ; Point to middle third attributes LD A,&78 ; Point to middle third of buffer 1 LD (L8D5C+1),A ; Poke into later code IF FIXBLOCKS L8D4B: LD E,&00 ; Start at screen address 0 L8D4D: LD A,(IX+0) ; Get current attribute LD HL,BACKGROUND-9 ; Start at the background attribute LD BC,9 ; Nine bytes per block L8D56: ADD HL,BC ; Step to next block CP (HL) ; Does attribute byte match? JR NZ,L8D56 ; No match, check next block LD B,&08 ; Eight pixel-lines L8D5C: LD D,&00 ; This is poked to &70 or &78 earlier L8D5E: INC HL ; Point to bitmap pixel line LD A,(HL) ; Get a character byte LD (DE),A ; Store into screen buffer INC D ; Move to next pixel-line DJNZ L8D5E ; Loop for eight pixel-lines INC IX ; Move to next attribute INC E ; Move to next screen address JP NZ,L8D4D ; Loop for 256 attributes RET ELSE L8D4B: LD C,&00 ; Start at screen address 0 L8D4D: LD E,C ; Set screen address low byte LD A,(IX+&00) ; Get current attribute ; As pointed out by various people [ref], this is where the block graphics ; bug occurs. Instead of checking the attribute bytes by stepping by 9 from ; BACKGROUND, all bytes are checked, including the character matrix bitmaps. ; Consequently, if an attribute matches a bitmap byte before the actual ; attribute then the eight bytes from that point onwards will be used as the ; character matrix, regardless of where they occur. LD HL,BACKGROUND ; Start at the background attribute LD BC,&0036 ; Check a maximum of six 9-byte blocks CPIR ; Look for a byte that matches LD C,E ; Get address low byte back LD B,&08 ; Eight pixel-lines L8D5C: LD D,&00 ; This is poked to &70 or &78 earlier L8D5E: LD A,(HL) ; Get a character byte LD (DE),A ; Store into screen buffer INC HL ; Move to next byte INC D ; Move to next pixel-line DJNZ L8D5E ; Loop for eight pixel-lines INC IX ; Move to next attribute INC C ; Move to next screen address JP NZ,L8D4D ; Loop for 256 attributes RET ENDIF ; Build room ; ---------- BUILDROOM: LD HL,ROOM ; Point to room buffer LD IX,&5E00 ; Point to attribute buffer 1 L8D72: LD A,(HL) ; Get a room byte RLCA RLCA ; Rotate b6-b7 to b0-b1 CALL ROOMBLOCK ; Insert attribute for this block LD A,(HL) ; Get the room byte again RRCA RRCA RRCA RRCA ; Rotate b4-b5 to b0-b1 CALL ROOMBLOCK ; Insert attribute for this block LD A,(HL) ; Get the room byte again RRCA RRCA ; Rotate b2-b3 to b0-b1 CALL ROOMBLOCK ; Insert attribute for this block LD A,(HL) ; Get the room byte a fourth time CALL ROOMBLOCK ; Insert attribute for this block INC HL ; Point to next room byte LD A,L ; Get address low byte AND &80 ; Wrapped past &7F? JR Z,L8D72 ; Loop for 128 bytes ; Insert conveyor blocks into attribute buffer ; -------------------------------------------- LD A,(CONV_NUM) ; Get conveyor length OR A ; Is it zero? JR Z,L8DA1 ; Jump forward with no conveyors LD HL,(CONV_PSN) ; Get conveyor start LD B,A ; Pass conveyor length to B LD A,(CONVEYOR) ; Get conveyor attribute L8D9D: LD (HL),A ; Store a conveyor attribute block INC HL ; Move to next block DJNZ L8D9D ; Loop for length of conveyor L8DA1: LD A,(SLOPE_NUM) ; Get slope length OR A ; Is it zero? RET Z ; Exit with no slopes ; Insert slope blocks into attribute buffer ; ----------------------------------------- LD HL,(SLOPE_PSN) ; Get slope start LD A,(SLOPE_DIR) ; Get slope direction AND &01 ; Keep left/right bit RLCA ; Move to b2 ADD A,&DF ; A=&DF or &E1 for left or right LD E,A ; Pass to E LD D,&FF ; DE is step between slope addresses ; A slope leftwards steps by -33 (&FFDF) ; A slope rightwards steps by -31 (&FFE1) LD A,(SLOPE_NUM) ; Get slope length again LD B,A ; Pass length to B for looping LD A,(SLOPE) ; Get slope attribute L8DBB: LD (HL),A ; Store a slope attribute block ADD HL,DE ; Move up and one block left or right DJNZ L8DBB ; Loop for length of slope RET ; All done ; Insert a room block attribute ; ----------------------------- ROOMBLOCK: AND &03 ; Keep b0-b1 LD C,A ; Hold in C for additional add RLCA RLCA RLCA ; Multiply by 8 ADD A,C ; Add again to multiply by 9 ADD A,BACKGROUND-ROOM ; Add to base of room blocks LD E,A ; Pass to E to point to attribute LD D,ROOM / 256 ; within room buffer LD A,(DE) ; Get attribute for this block LD (IX+&00),A ; Store into attribute buffer INC IX ; Point to next buffer location RET ; Move Willy ; ========== ; Move Willy taking account of keypresses, joystick state, whether Willy is on ; a conveyor or rope, whether he is jumping or falling, etc. ; Will probably use somebody else's commentary for here... MOVEMENT: LD A,(ONROPE) ; Is Willy on a rope? DEC A BIT 7,A JP Z,L8ED4 ; Jump to deal with Willy on a rope LD A,(FALLING) ; moving/falling CP &01 JR NZ,L8E36 LD A,(JUMPING) ; jumping AND &FE SUB &08 LD HL,YPOSN ; Point to Willy's position ADD A,(HL) LD (HL),A CP &F0 ; Above top of screen? JP NC,GO_UP ; If so, go up CALL L8E9C LD A,(WALL) ; Wall attribute CP (HL) JP Z,L8EBC INC HL CP (HL) JP Z,L8EBC LD A,(JUMPING) ; jumping INC A LD (JUMPING),A ; jumping SUB &08 JP P,L8E11 NEG L8E11: INC A RLCA RLCA RLCA LD D,A LD C,&20 LD A,(BORDER) L8E1B: OUT (&FE),A XOR &18 LD B,D L8E20: DJNZ L8E20 DEC C JR NZ,L8E1B LD A,(JUMPING) ; Get jump counter CP &12 ; Jump has finished when counter is &12 JP Z,L8EB0 ; Jump finished, set JUMPING to 6 CP &10 JR Z,L8E36 ; Check what Willy is standing on CP &0D JP NZ,&8FBC ; Not 13 or 16, skip ahead to do movement L8E36: LD A,(YPOSN) AND &0E JR NZ,L8E62 LD HL,(POSITION) ; Get Willy's position LD DE,&0040 ADD HL,DE ; Point to block under Willy's feet BIT 1,H ; Wrapped off the bottom of the screen? JP NZ,GO_DOWN ; Yes, so go downwards LD A,(NASTY) ; Get Nasty attribute CP (HL) ; Is a nasty under Willy? JR Z,L8E62 ; Yes, kill Willy INC HL ; Point to under Willy's right foot LD A,(NASTY) ; Get Nasty attribute superfluously CP (HL) ; Is a nasty under Willy? JR Z,L8E62 ; Yes, kill Willy LD A,(BACKGROUND) ; Get the background attribute CP (HL) ; Is there 'nothing' under Willy? DEC HL ; Point to under Willy's left foot JP NZ,L8ED4 ; Something under Willy, stop falling CP (HL) ; Is there 'nothing' under Willy? JP NZ,L8ED4 ; No, so jump to stop falling L8E62: LD A,(FALLING) ; moving/falling? CP &01 JP Z,&8FBC LD HL,L85D0 RES 1,(HL) LD A,(FALLING) OR A JP Z,L8EB6 INC A CP &10 JR NZ,L8E7D LD A,&0C L8E7D: LD (FALLING),A RLCA RLCA RLCA RLCA LD D,A LD C,&20 LD A,(BORDER) OUT (&FE),A XOR &18 LD B,D L8E8F: DJNZ L8E8F DEC C JR NZ,&8E8A LD A,(YPOSN) ADD A,&08 LD (YPOSN),A L8E9C: AND &F0 LD L,A XOR A RL L ADC A,&5C LD H,A LD A,(POSITION) AND &1F OR L LD L,A LD (POSITION),HL RET L8EB0: LD A,&06 LD (FALLING),A RET L8EB6: LD A,&02 LD (FALLING),A RET L8EBC: LD A,(YPOSN) ADD A,&10 AND &F0 LD (YPOSN),A CALL L8E9C LD A,&02 LD (FALLING),A LD HL,L85D0 RES 1,(HL) RET ; Move Willy ; ========== L8ED4: LD E,&FF ; Prepare E with 'not moving' LD A,(ONROPE) DEC A BIT 7,A JR Z,L8EFA LD A,(FALLING) CP &0C JP NC,DIED1 XOR A LD (FALLING),A LD A,(CONVEYOR) ; Get conveyor attribute CP (HL) ; Check attribute under Willy JR Z,L8EF4 ; Standing on a conveyor INC HL ; Look under Willy's right foot CP (HL) ; Check attribute under Willy JR NZ,&8EFA ; Not standing on a conveyor L8EF4: LD A,(CONV_DIR) ; Get conveyor direction SUB &03 ; 0->bit1 clear, 1->bit0 clear LD E,A ; This also gives 2->sticky and 3->off ; [ref] L8EFA: LD BC,&DFFE IN A,(C) AND &1F OR &20 AND E LD E,A LD A,(STATUS) AND &02 RRCA XOR E LD E,A LD BC,&FBFE IN A,(C) AND &1F RLC A OR &01 AND E LD E,A LD B,&E7 IN A,(C) RRCA OR &F7 AND E LD E,A LD B,&EF IN A,(C) OR &FB AND E LD E,A IN A,(C) RRCA OR &FB AND E LD E,A LD A,(KEMPSTON) OR A JR Z,L8F42 LD BC,&001F IN A,(C) AND &03 CPL AND E LD E,A L8F42: LD C,&00 LD A,E AND &2A CP &2A JR Z,L8F51 LD C,&04 XOR A LD (COUNTDOWN),A L8F51: LD A,E AND &15 CP &15 JR Z,L8F5E SET 3,C XOR A LD (COUNTDOWN),A L8F5E: LD A,(L85D0) ADD A,C LD C,A LD B,&00 LD HL,L8421 ADD HL,BC LD A,(HL) LD (L85D0),A LD BC,&7EFE IN A,(C) AND &1F CP &1F JR NZ,L8F8F LD B,&EF IN A,(C) BIT 0,A JR Z,L8F8F LD A,(KEMPSTON) OR A JR Z,L8FBC LD BC,&001F IN A,(C) BIT 4,A JR Z,L8FBC L8F8F: LD A,(STATUS) BIT 1,A JR NZ,L8FBC XOR A LD (JUMPING),A LD (COUNTDOWN),A INC A LD (FALLING),A LD A,(ONROPE) DEC A BIT 7,A JR NZ,L8FBC LD A,&F0 LD (ONROPE),A LD A,(YPOSN) AND &F0 LD (YPOSN),A LD HL,L85D0 SET 1,(HL) RET L8FBC: LD A,(L85D0) AND &02 RET Z LD A,(ONROPE) DEC A BIT 7,A RET Z LD A,(L85D0) AND &01 JP Z,L9042 LD A,(FRAME) OR A JR Z,L8FDC DEC A LD (FRAME),A RET ; Move Willy leftwards ; -------------------- L8FDC: LD A,(FALLING) LD BC,&0000 ; 0 for no movement CP &00 ; JR NZ,L900A LD HL,(POSITION) LD BC,&0000 ; again? LD A,(SLOPE_DIR) DEC A OR &A1 XOR &E0 LD E,A LD D,&00 ADD HL,DE ; Point to under Willy's feet LD A,(SLOPE) ; Get slope attribute CP (HL) ; Willy standing on a slope? JR NZ,L900A ; No, jump ahead LD BC,&0020 ; +32 for downwards LD A,(SLOPE_DIR) OR A JR NZ,L900A LD BC,&FFE0 ; -32 for upwards L900A: LD HL,(POSITION) ; Get Willy's current position LD A,L AND &1F ; If Willy is in column zero, JP Z,GO_LEFT ; move left ADD HL,BC ; Willy's potential new position DEC HL ; Just left of Willy's head LD DE,&0020 ; DE holds offset between lines ADD HL,DE ; Just left of Willy's feet LD A,(WALL) ; Get wall attribute CP (HL) ; Is there a wall here? RET Z ; If so, return without moving Willy LD A,(YPOSN) ; Get Willy's pixel-line position SRA C ; Is Willy occupying three character ADD A,C ; lines? LD B,A AND &0F JR Z,L9032 ; Only occupying two character lines LD A,(WALL) ; Get wall attribute ADD HL,DE ; Point to left of Willy's feet on 3rd CP (HL) ; character line. Is there wall here? RET Z ; If so, return without moving Willy OR A SBC HL,DE ; Move back to second character line L9032: OR A ; Back to new head-height position SBC HL,DE ; Note: no check for wall block IF WALLLEFT LD A,(WALL) ; Get wall attribute CP (HL) ; Check left of Willy's head RET Z ; Return if wall LD (POSITION),HL ; Update Willy's position LD A,&03 ; Set Willy's sprite to frame 3 JP L90AE ; Jump to end of right movement ELSE LD (POSITION),HL ; Update Willy's position LD A,B LD (YPOSN),A ; Update pixel-line position LD A,&03 ; Set Willy's sprite to frame 3 LD (FRAME),A RET ENDIF L9042: LD A,(FRAME) CP &03 JR Z,L904E INC A LD (FRAME),A RET ; Move Willy rightwards ; --------------------- L904E: LD A,(FALLING) LD BC,&0000 ; 0 for no movement OR A JR NZ,L9078 LD HL,(POSITION) LD A,(SLOPE_DIR) DEC A OR &9D XOR &BF LD E,A LD D,&00 ADD HL,DE ; Point to under Willy's feet LD A,(SLOPE) ; Get slope attribute CP (HL) ; Willy standing on a slope? JR NZ,L9078 ; No, jump ahead LD BC,&0020 ; +32 for downwards LD A,(SLOPE_DIR) OR A JR Z,L9078 LD BC,&FFE0 ; -32 for upwards L9078: LD HL,(POSITION) ; Get's Willy's current position ADD HL,BC ; Willy's potential new position INC HL INC HL ; Just right of Willy's head LD A,L AND &1F ; If Willy's position about to wrap JP Z,GO_RIGHT ; round into column zero, move right LD DE,&0020 ; DE holds offset between lines LD A,(WALL) ; Get wall attribute ADD HL,DE ; Just right of Willy's feet CP (HL) ; Is there a wall here? RET Z ; If so, return without moving Willy LD A,(YPOSN) ; Get Willy's pixel-line position SRA C ; Is Willy occupying three character ADD A,C ; lines? LD B,A AND &0F JR Z,L90A1 ; Only occupying two character lines LD A,(WALL) ; Get wall attribute ADD HL,DE ; Point right of Willy's feet on third CP (HL) ; character line. Is there wall here? RET Z ; If so, return without moving Willy OR A SBC HL,DE ; Move back to second character line L90A1: LD A,(WALL) ; Get wall attribute OR A ; SBC HL,DE ; Now points to right of Willy's head CP (HL) ; Check if that square is a wall IF WALLRIGHT NOP ; Ignore wall at head-height ELSE RET Z ; Return without moving Willy ENDIF DEC HL ; Back to new head-height position LD (POSITION),HL ; Update Willy's position XOR A ; Set Willy's sprite to frame 0 L90AE: LD (FRAME),A LD A,B LD (YPOSN),A ; Update pixel-line position RET ; Compare this with the left-movement code at L8FDC onwards. The left movement ; code checks Willy's future position at foot height only, while the right ; movement checks both at head height and at foot height. This is why wall ; blocks at head height can be moved through going left, but not right. ; [Andrew Broad, 11-12-2004] DIED2: POP HL DIED1: POP HL LD A,&FF LD (FALLING),A JP L89F5 ; Update active guardians in current room ; ======================================= UPDATEGUARD: LD IX,GUARDIAN ; Point to active guardian buffer L90C4: LD A,(IX+&00) CP &FF ; End marker? RET Z ; Exit when all done AND &03 ; Check guardian type JP Z,L91B6 ; Type 0/4 -> jump to check next guardian CP &01 ; Type 1/5? JP Z,L9133 ; Horizontal guardian CP &02 ; Type 2/6? JP Z,L917F ; Vertical guardian ; We're now left with type 3 and type 7 guardians - ropes. ; -------------------------------------------------------- BIT 7,(IX+&00) ; Check rope direction JR Z,L90FF LD A,(IX+&01) BIT 7,A JR Z,L90F5 SUB &02 CP &94 JR NC,L911D SUB &02 CP &80 JR NZ,L911D XOR A JR L911D L90F5: ADD A,&02 CP &12 JR NC,L911D ADD A,&02 JR L911D L90FF: LD A,(IX+&01) BIT 7,A JR NZ,L9115 SUB &02 CP &14 JR NC,L911D SUB &02 OR A JR NZ,L911D LD A,&80 JR L911D L9115: ADD A,&02 CP &92 JR NC,L911D ADD A,&02 L911D: LD (IX+&01),A AND &7F CP (IX+&07) JP NZ,L91B6 LD A,(IX+&00) XOR &80 LD (IX+&00),A JP L91B6 ; Horizontal guardian ; ------------------- L9133: BIT 7,(IX+&00) ; Get direction JR NZ,L915C ; Jump to move right ; Move horizontal guardian left ; ----------------------------- LD A,(IX+&00) SUB &20 AND &7F LD (IX+&00),A CP &60 JR C,L91B6 LD A,(IX+&02) AND &1F CP (IX+&06) JR Z,L9156 DEC (IX+&02) JR L91B6 L9156: LD (IX+&00),&81 JR L91B6 ; Move horizontal guardian right ; ------------------------------ L915C: LD A,(IX+&00) ADD A,&20 OR &80 LD (IX+&00),A CP &A0 JR NC,L91B6 LD A,(IX+&02) AND &1F CP (IX+&07) JR Z,L9179 INC (IX+&02) JR L91B6 L9179: LD (IX+&00),&61 JR L91B6 ; Vertical guardian ; ----------------- L917F: LD A,(IX+&00) XOR &08 LD (IX+&00),A AND &18 JR Z,L9193 LD A,(IX+&00) ADD A,&20 LD (IX+&00),A L9193: LD A,(IX+&03) ADD A,(IX+&04) LD (IX+&03),A CP (IX+&07) JR NC,L91AE CP (IX+&06) JR Z,L91A8 JR NC,L91B6 L91A8: LD A,(IX+&06) LD (IX+&03),A L91AE: LD A,(IX+&04) NEG LD (IX+&04),A L91B6: LD DE,&0008 ; Eight bytes per guardian ADD IX,DE ; Point to next guardian JP L90C4 ; Loop back to check all guardians ; Draw guardians to screen buffer 2 ; ================================= DRAWGUARD: LD IX,GUARDIAN ; Point to guardian buffer L91C2: LD A,(IX+&00) ; Get guardian type CP &FF ; End marker? RET Z ; Exit when all done AND &07 ; Check guardian type JP Z,L93B3 ; Type 0 - null guardian CP &03 ; Type 3? - a rope? JP Z,L92A4 ; Jump to deal with ropes CP &04 ; Type 4 - an arrow? JR Z,L9237 ; Jump to deal with arrows ; Horizontal and vertical guardians are both draw the same way. Once their ; position has been updated, all they are is a sprite that needs to be placed ; on the screen at a specified position. LD E,(IX+&03) ; Get Y position LD D,PIXEL / 256 ; Index into pixel-line table LD A,(DE) ; Get low byte of Y position address LD L,A ; Pass to L for later LD A,(IX+&02) ; Get X position AND &1F ; Reduce to 0-31 ADD A,L ; Add to Y position address LD L,A ; And store back into L LD A,E ; Convert this into an attribute buffer RLCA ; address AND &01 OR &5C LD H,A ; HL=address in attribute buffer 2 LD DE,&001F ; Set DE to 31 for later adding LD A,(IX+&01) ; Get guardian's attribute AND &0F ; b0-b3=INK, b4=BRIGHT ADD A,&38 ; Move BRIGHT up to bit 6 AND &47 ; Keep INK and BRIGHT LD C,A ; Save in C LD A,(HL) ; Get attribute in this position AND &38 ; Keep PAPER XOR C ; Merge in guardian's INK and BRIGHT LD C,A ; Pass back to C LD (HL),C ; Store attribute for 2x2 characters INC HL ; Set attributes for top two cells LD (HL),C ADD HL,DE ; Add 31 to move to next line LD (HL),C ; Set attributes for bottom two cells INC HL LD (HL),C LD A,(IX+&03) ; Get guardian's Y position AND &0E ; Has it moved away from pixel-line 0? JR Z,L920F ; No, so only 2x2 blocks to colour ; Guardian overlaps three character lines, so six attribute cells need to be ; set to set it's colour. ADD HL,DE ; Add 31 to move to next line LD (HL),C ; Set attributes on two move cells INC HL LD (HL),C L920F: LD C,&01 ; Don't ignore collisions LD A,(IX+&01) ; Construct sprite address AND (IX+&00) ; Sprite address subpage bit7 OR (IX+&02) ; X position top 3 bits AND &E0 ; 32 bytes per sprite LD E,A ; Set sprite address low byte LD D,(IX+&05) ; Get sprite address high byte LD H,PIXEL / 256 ; Pixel-line buffer high byte LD L,(IX+&03) ; Get guardian's Y position LD A,(IX+&02) ; Get guardian's X position AND &1F ; Resolve to 0-31 OR (HL) ; Add to pixel-line start address low INC HL ; Point to pixel-line address high LD H,(HL) ; Get high byte of address LD L,A ; HL is address to draw sprite CALL DRAWSPRITE ; Draw the sprite JP NZ,DIED1 ; If the sprite collided, kill Willy JP L93B3 ; Draw next guardian ; Arrow ; ----- L9237: BIT 7,(IX+&00) ; Arrow direction JR NZ,L9244 DEC (IX+&04) LD C,&2C JR L9249 L9244: INC (IX+&04) LD C,&F4 L9249: LD A,(IX+&04) CP C JR NZ,L9262 LD BC,&0280 LD A,(BORDER) L9255: OUT (&FE),A ; Squawk! XOR &18 L9259: DJNZ L9259 LD B,C DEC C JR NZ,L9255 JP L93B3 L9262: AND &E0 JP NZ,L93B3 LD E,(IX+&02) LD D,&82 LD A,(DE) ADD A,(IX+&04) LD L,A LD A,E AND &80 RLCA OR &5C LD H,A LD (IX+&05),&00 LD A,(HL) AND &07 CP &07 JR NZ,L9286 DEC (IX+&05) L9286: LD A,(HL) OR &07 LD (HL),A INC DE LD A,(DE) LD H,A DEC H LD A,(IX+&06) LD (HL),A INC H LD A,(HL) AND (IX+&05) JP NZ,DIED1 LD (HL),&FF INC H LD A,(IX+&06) LD (HL),A JP L93B3 L92A4: LD IY,PIXEL ; Pixel-line table LD (IX+&09),&00 LD A,(IX+&02) LD (IX+&03),A LD (IX+&05),&80 L92B6: LD A,(IY+&00) ADD A,(IX+&03) LD L,A LD H,(IY+&01) LD A,(ONROPE) OR A JR NZ,L92D6 LD A,(IX+&05) AND (HL) JR Z,L930E LD A,(IX+&09) LD (ONROPE),A SET 0,(IX+&0B) L92D6: CP (IX+&09) JR NZ,L930E BIT 0,(IX+&0B) JR Z,L930E LD B,(IX+&03) LD A,(IX+&05) LD C,&01 CP &04 JR C,L92FC LD C,&00 CP &10 JR C,L92FC DEC B LD C,&03 CP &40 JR C,L92FC LD C,&02 L92FC: LD (FRAME),BC DEFB &FD LD A,L ; LD A,IYL SUB &10 LD (YPOSN),A PUSH HL CALL L8E9C POP HL JR L930E L930E: LD A,(IX+&05) OR (HL) LD (HL),A LD A,(IX+&09) ADD A,(IX+&01) LD L,A SET 7,L LD H,&83 ; Rope structure table LD E,(HL) LD D,&00 ADD IY,DE RES 7,L LD A,(HL) OR A JR Z,L9350 LD B,A BIT 7,(IX+&01) JR Z,L9341 L9330: RLC (IX+&05) BIT 0,(IX+&05) JR Z,L933D DEC (IX+&03) L933D: DJNZ L9330 JR L9350 L9341: RRC (IX+&05) BIT 7,(IX+&05) JR Z,L934E INC (IX+&03) L934E: DJNZ L9341 L9350: LD A,(IX+&09) CP (IX+&04) JR Z,L935E INC (IX+&09) JP L92B6 L935E: LD A,(ONROPE) BIT 7,A JR Z,L936F INC A LD (ONROPE),A RES 0,(IX+&0B) JR L93B3 L936F: BIT 0,(IX+&0B) JR Z,L93B3 LD A,(L85D0) BIT 1,A JR Z,L93B3 RRCA XOR (IX+&00) RLCA RLCA AND &02 DEC A LD HL,ONROPE ADD A,(HL) LD (HL),A LD A,(UP) LD C,A LD A,(HERE) ; Can't go up if UP goes to same room CP C JR NZ,L939B LD A,(HL) CP &0C JR NC,L939B LD (HL),&0C L939B: LD A,(HL) CP (IX+&04) JR C,L93B3 JR Z,L93B3 LD (HL),&F0 LD A,(YPOSN) AND &F8 LD (YPOSN),A XOR A LD (FALLING),A JR L93B3 L93B3: LD DE,&0008 ADD IX,DE ; Point to next guardian JP L91C2 ; Nothing seems to call this ; ========================== ; This sets the INK six attribute cells. This would be appropriate for a 32x32 ; sprite that has moved away from pixel-line 0 in a character cell, and so ; overlaps three characters rows. L93BB: LD (HL),A LD A,(BACKGROUND) AND &F8 OR (HL) LD (HL),A LD DE,&001F INC HL LD (HL),A ADD HL,DE LD (HL),A INC HL LD (HL),A ADD HL,DE LD (HL),A INC HL LD (HL),A RET ; Process objects ; =============== CHKOBJECTS: IF MOREROOMS LD H,&A6 ; High byte of collection flags ELSE LD H,&A4 ; High byte of collection flags ENDIF LD A,(OBJECTS) ; Get 256-number of objects LD L,A ; HL points to collection flags L93D7: IF MOREROOMS BIT 6,(HL) ; Is this object collected? JR Z,L9452 ; Yes, jump to check next object DEC H DEC H ; Point to object position CALL L96F4 ; Draw this object if in this room ELSE LD C,(HL) ; Get collection/room value RES 7,C ; Lose bit4 of Y position LD A,(HERE) ; Get current room OR &40 ; Fake in a 'not collected' bit CP C ; In this room and not collected? ENDIF JR NZ,L9452 ; No, jump to check next object LD A,(HL) ; Get object Y position bit 4 RLCA ; Move to bit 0 AND &01 ADD A,&5C ; Object's screen half LD D,A ; Pass to D for later on INC H ; Point to position X and Y b0-b3 LD E,(HL) ; Pass to E IF MOREROOMS INC H ; Point back to collection flags ELSE DEC H ; Point back to collection flags ENDIF LD A,(DE) ; Get attribute at object's position AND &07 ; Keep INK CP &07 ; White indicating Willy there? JR NZ,L9430 ; Not white, draw the object LD IX,COLLECTED ; Point to collected items string L93F7: INC (IX+&02) ; Increment a digit LD A,(IX+&02) ; Get current digit CP &3A ; Wrapped past '0'? JR NZ,L9409 ; No, continue to make a sound LD (IX+&02),&30 ; Set current digit to '0' DEC IX ; Point to next higher order digit JR L93F7 ; Loop to increment this digit ; Make a sound and collect an object ; ---------------------------------- L9409: LD A,(BORDER) ; Get border colour LD C,&80 L940E: OUT (&FE),A ; Write to buzzer XOR &18 ; Toggle buzzer state LD E,A LD A,&90 SUB C LD B,A LD A,E L9418: DJNZ L9418 ; Pause a while DEC C DEC C JR NZ,L940E ; Loop to play a note LD A,(REMAIN) ; Get remaining items INC A LD (REMAIN),A ; Update remaining items JR NZ,L942C ; Jump if not all collected LD A,&01 LD (STATUS),A ; Set STATUS to 'collected' L942C: RES 6,(HL) ; Clear 'not collected' flag JR L9452 ; Loop to check next item ; Draw object and 'wobble' its colours ; ------------------------------------ L9430: LD A,(TICKER) ; Wobble colours with game ticker ADD A,L ; Add to object table offset AND &03 ; Keep b0-b1, colours are ADD A,&03 ; magenta, green, cyan, yellow LD C,A LD A,(DE) ; Get attributes under object AND &F8 ; Lose INK OR C ; Merge in wobbling INK LD (DE),A ; Store to attributes IF MOREROOMS LD A,D ; Get object's Y position bit 4 NOP ; Already in bit 0 ELSE LD A,(HL) ; Get object's Y position bit 4 RLCA ; Move to bit 0 ENDIF RLCA RLCA ; Move to bit 3, calculate screen RLCA ; address top third or middle third AND &08 ; E already holds position with third ADD A,&60 ; as it is the same as the attribute LD D,A ; address low byte PUSH HL ; Save object table pointer LD HL,OBJECT ; Point to object's bitmap LD B,&08 ; Eight pixel-lines CALL PRMATRIX ; Draw the object POP HL ; Restore object table pointer L9452: INC L ; Point to next item JR NZ,L93D7 ; Loop until end of table reached RET ; Draw a sprite ; ============= ; DE=sprite address ; HL=screen address ; C=0 - ignore collisions, C=1 - don't ignore collisions DRAWSPRITE: LD B,&10 ; 16 pixel-lines L9458: BIT 0,C ; Check collision flag LD A,(DE) ; Get byte from sprite JR Z,L9461 ; If ignoring collision, jump to store AND (HL) ; Mask with pixels on screen RET NZ ; If any overlap, exit with NZ set LD A,(DE) ; Get byte from sprite again OR (HL) ; Merge with pixels on screen L9461: LD (HL),A ; Store byte to screen INC L ; Point to right-hand screen byte INC DE ; Point to next sprite byte BIT 0,C ; Check collision flag LD A,(DE) ; Get byte from sprite JR Z,L946D ; If ignoring collision, jump to store AND (HL) ; Mask with pixels on screen RET NZ ; If any overlap, exit with NZ set LD A,(DE) ; Get byte from sprite again OR (HL) ; Merge with pixels on screen L946D: LD (HL),A ; Store byte to screen DEC L ; Point back to left-hand screen byte INC H ; Move down one pixel-line INC DE ; Point to next sprite byte LD A,H ; Check screen address high byte to AND &07 ; see if wrapped past pixel-line 7 JR NZ,L9486 LD A,H ; Adjust screen address SUB &08 LD H,A LD A,L ADD A,&20 LD L,A AND &E0 JR NZ,L9486 LD A,H ADD A,&08 LD H,A L9486: DJNZ L9458 ; Loop for 16 pixel-lines XOR A ; Ensure Z set RET ; Exit ; Move left ; ========= GO_LEFT: LD A,(LEFT) ; Get room through left exit LD (HERE),A ; Set current room LD A,(POSITION) ; Get Willy's position OR &1F ; Force into column 31 AND &FE ; Make it column 30 LD (POSITION),A ; Store Willy's position POP HL ; Lose return address JP PLAYROOM ; Enter the new current room ; Move right ; ========== GO_RIGHT: LD A,(RIGHT) ; Get room through right exit LD (HERE),A ; Set current room LD A,(POSITION) ; Get Willy's position AND &E0 ; Force into column 0 LD (POSITION),A ; Store Willy's position POP HL ; Lose return address JP PLAYROOM ; Enter the new current room ; Move up ; ======= GO_UP: LD A,(UP) ; Get room through up exit LD (HERE),A ; Set current room LD A,(POSITION) ; Get Willy's position low byte AND &1F ; Keep X position ADD A,&A0 ; Set Y position to line 13 LD (POSITION),A ; Store Willy's position low byte LD A,&5D ; Put Willy in bottom half of screen LD (POSITION+1),A ; Set Willy's position high byte LD A,&D0 ; Set Willy's Y position to line 13 LD (YPOSN),A XOR A ; Clear movement flag LD (FALLING),A ; Not falling, normal movement POP HL ; Lose return address JP PLAYROOM ; Enter the new current room ; Move down ; ========= GO_DOWN: LD A,(DOWN) ; Get room through down exit LD (HERE),A ; Set current room XOR A LD (YPOSN),A ; Set Willy's Y position to line 0 LD A,(FALLING) ; Get falling state CP &0B JR NC,&94E8 LD A,&02 LD (FALLING),A ; Set falling state to 2 L94E8: LD A,(POSITION) ; Get Willy's position low byte AND &1F ; Keep X position, set Y to 0 LD (POSITION),A ; Store Willy's position low byte LD A,&5C ; Put Willy at top of screen LD (POSITION+1),A ; Set Willy's position high byte POP HL ; Lose return address JP PLAYROOM ; Enter the new current room ; Update conveyors appearance on screen ; ===================================== UPDATECONV: LD HL,(CONV_PSN) ; Get conveyor's start position LD A,H ; Convert position to screen buffer 1 AND &01 ; address RLCA RLCA RLCA ADD A,&70 LD H,A ; HL=address in screen buffer 1 LD E,L ; Save in DE as well LD D,H LD A,(CONV_NUM) ; Get conveyor length OR A RET Z ; No conveyor, so exit LD B,A ; Pass to B as loop counter LD A,(CONV_DIR) ; Get conveyor direction OR A ; Is it moving right? JR NZ,L9526 ; Any non-zero direction rotates right ; Rotate conveyor leftwards ; ------------------------- LD A,(HL) ; Get conveyor top pixel-line RLC A ; Rotate left two bits RLC A INC H ; Point two pixel-lines down INC H LD C,(HL) ; Get conveyor's third pixel-line RRC C ; Rotate right two bits RRC C L951F: LD (DE),A ; Store updated top pixel-line LD (HL),C ; Store updated third pixel-line INC L ; Point to next conveyor character INC E DJNZ L951F ; Loop for conveyor length RET ; Rotate conveyor rightwards ; -------------------------- L9526: LD A,(HL) ; Get conveyor top pixel-line RRC A ; Rotate right two bits RRC A INC H ; Point to pixel-lines down INC H LD C,(HL) ; Get conveyor's third pixel-line RLC C ; Rotate left two bits RLC C JR L951F ; Jump to update whole conveyor ; Deal with special case rooms ; ============================ SPECIAL: LD A,(HERE) ; Get current room CP &23 ; Master Bedroom? JR NZ,L959A ; No, jump to check for Bathroom ; Special actions for The Master Bedroom ; -------------------------------------- ; Draw Maria according to how far into the room Willy has got. This is ; measured by Willy's Y position, so if he jumps, Maria raises her arm. LD A,(STATUS) OR A ; Normal gameplay? JR NZ,L9576 ; No, jump to check for the bed LD A,(TICKER) ; Animate Maria with the game ticker AND &02 ; Keep bit 1 RRCA RRCA RRCA RRCA ; Multiply by 16 to give 0 or 32 OR &80 ; &80, &A0 are the foot tapping Maria LD E,A ; Pass to E as sprite subpage value LD A,(YPOSN) ; Get Willy's Y position CP &D0 ; Is Willy on the floor - line 13? JR Z,DRAWMARIA ; Yes, jump to draw Maria LD E,&C0 ; Point to Maria raising her hand CP &C0 ; Has Willy started up the ramp? JR NC,DRAWMARIA ; Yes, jump to draw Maria LD E,&E0 ; Otherwise, Maria with outstretched arm DRAWMARIA: LD D,&9C ; Maria sprite page LD HL,&686E ; Maria's position in buffer 2 LD C,&01 ; Don't ignore collisions CALL DRAWSPRITE ; Draw Maria sprite JP NZ,DIED1 ; Kill Willy if Maria touched him LD HL,&4545 ; Bright cyan on black LD (&5D6E),HL ; Set colours of Maria's top half LD HL,&0707 ; White on black LD (&5D8E),HL ; Set colours of Maria's skirt RET ; Check if Willy has got as far as the bed ; ---------------------------------------- L9576: LD A,(POSITION) ; Get Willy's position AND &1F ; Get X coordinate CP &06 ; Further left than X=6? RET NC ; Not reached the bed yet LD A,&02 ; Reached the bed LD (STATUS),A ; Set STATUS to 2 to indicate fleeing RET ; Has Willy reached the toilet while fleeing? ; ------------------------------------------- CHKTOILET: LD A,(HERE) ; Get current room CP &21 ; The Bathroom? RET NZ ; No, exit LD A,(POSITION) ; Get Willy's position CP &BC ; Line 13 (or 5!), column 28? RET NZ ; No, exit XOR A LD (TICKER),A ; Force ticker to zero LD A,&03 LD (STATUS),A ; Set STATUS to 3 to indicate vomiting RET ; Special actions for The Bathroom ; -------------------------------- ; Animate the toilet without killing Willy, or animate Willy communing with ; the porcelain. L959A: LD A,(HERE) ; Get current room CP &21 ; Bathroom? RET NZ ; No, exit LD A,(TICKER) ; Animate toilet with the game ticker AND &01 ; Keep bit 0 RRCA RRCA RRCA ; Multiply by 32 to give &00 or &20 LD E,A ; Pass to E as the sprite subpage LD A,(STATUS) CP &03 ; Is Willy throwing up? JR NZ,DRAWTOILET ; No, animate toilet SET 6,E ; Point to Willy and the toilet DRAWTOILET: LD D,&A6 ; Toilet sprite page LD IX,&82D0 ; Pixel-line address for line 13 LD BC,&101C ; 16 pixel lines, X position is &1C CALL L9668 ; Draw toilet with the DRAWWILLY ; routine to merge with screen content LD HL,&0707 ; White on black LD (&5DBC),HL ; Set toilet's colours LD (&5DDC),HL RET ; Update Willy's position and draw him to screen buffer 2 ; ======================================================= UPDATEWILLY: LD HL,(POSITION) ; Get Willy's position LD B,&00 LD A,(SLOPE_DIR) AND &01 ADD A,&40 LD E,A LD D,&00 ADD HL,DE LD A,(SLOPE) CP (HL) JR NZ,L95F8 LD A,(FALLING) OR A JR NZ,L95F8 LD A,(FRAME) AND &03 RLCA RLCA LD B,A LD A,(SLOPE_DIR) AND &01 DEC A XOR &0C XOR B AND &0C LD B,A L95F8: LD HL,(POSITION) LD DE,&001F LD C,&0F CALL L961E INC HL CALL L961E ADD HL,DE CALL L961E INC HL CALL L961E LD A,(YPOSN) ADD A,B LD C,A ADD HL,DE CALL L961E INC HL CALL L961E JR DRAWWILLY ; Display Willy L961E: LD A,(BACKGROUND) ; Background CP (HL) JR NZ,L962F LD A,C AND &0F JR Z,L962F LD A,(BACKGROUND) OR &07 LD (HL),A L962F: LD A,(NASTY) CP (HL) JP Z,DIED2 RET ; Display Willy ; ============= DRAWWILLY: LD A,(YPOSN) ; Get Willy's Y position ADD A,B DEFB &DD LD H,PIXEL / 256 ; LD IXH,&82 ; Pixel-line buffer DEFB &DD LD L,A ; LD IXL,A LD A,(L85D0) AND &01 RRCA LD E,A LD A,(FRAME) AND &03 RRCA RRCA RRCA OR E LD E,A LD D,&9D ; Point to Willy sprites IF ROOMSPRITE LD A,(WILLYSP) ; Does room specify a player sprite? AND A JP P,L9660 ; No, use Willy sprite LD D,A ; Use specified sprite NOP ELSE LD A,(HERE) ; Get current room CP &1D ; In the Nightmare Room? JR NZ,L9660 ; No, use Willy sprite LD D,&B6 ; Point to flying pig ENDIF LD A,E ; Reverse the sprite direction XOR &80 ; Willy sprites are Right/Left LD E,A ; All other sprites are Left/Right L9660: LD B,&10 ; 16 pixel-lines LD A,(POSITION) ; Get Willy's position AND &1F ; X position LD C,A L9668: LD A,(IX+&00) ; Get pixel-line address of column 0 LD H,(IX+&01) OR C ; Add on X coordinate LD L,A ; HL=pixel-line address in buffer 2 LD A,(DE) ; Get byte from sprite OR (HL) ; Merge with screen content LD (HL),A ; Store to screen INC HL ; Move to next pixel cell INC DE ; Move to next sprite byte LD A,(DE) ; Get byte from sprite OR (HL) ; Merge with screen content LD (HL),A ; Store to screen INC IX ; Point to next pixel-line address INC IX INC DE ; Point to next sprite byte DJNZ L9668 ; Loop for 16 pixel-lines RET ; Write a string to the screen ; ============================ ; On entry: IX=>ASCII string ; DE=>Display address to write to ; C= String length PRMESSAGE: LD A,(IX+&00) ; Get a character CALL PRCHAR ; Write to screen at DE INC IX ; Move to next character INC E ; Move to next screen cell LD A,D ; DE has been left pointing to the SUB &08 ; 'ninth' pixel line, so adjust LD D,A ; D back to line 1 DEC C ; Decrement string length JR NZ,PRMESSAGE ; Loop for the string RET ; Write a character to screen ; --------------------------- PRCHAR: LD H,&07 LD L,A ; Copy character to L SET 7,L ADD HL,HL ; Multiple HL by 8 ADD HL,HL ADD HL,HL ; HL=>character matrix at &3C00-&3F00 LD B,&08 ; Eight pixel-lines PRMATRIX: LD A,(HL) ; Get character line LD (DE),A ; Store in screen INC HL ; Point to next character line INC D ; Point to next pixel line DJNZ PRMATRIX ; Loop for all eight lines RET ; Play introductory tune ; ====================== L96A2: LD A,(HL) ; Get current note CP &FF ; End of tune? RET Z ; Yes, so exit LD BC,&0064 XOR A LD E,(HL) LD D,E L96AC: OUT (&FE),A DEC D JR NZ,L96B4 LD D,E XOR &18 L96B4: DJNZ L96AC EX AF,AF' LD A,C CP &32 JR NZ,L96BE RL E L96BE: EX AF,AF' DEC C JR NZ,L96AC IF GAMEEXIT CALL L840D ; Check ENTER/FIRE/SS-SPACE ELSE CALL L96C9 ; Is ENTER/FIRE pressed? ENDIF RET NZ ; Exit if so INC HL JR L96A2 ; Check if ENTER/FIRE/0 pressed ; On exit, NZ=ENTER, FIRE or 0 pressed L96C9: LD A,(KEMPSTON) ; Is kempston present? OR A JR Z,L96D4 ; No, jump to look at keyboard IN A,(&1F) ; Read joystick BIT 4,A ; Check FIRE button RET NZ ; Return if pressed L96D4: LD BC,&AFFE ; Look at ENTER and 0 keys IN A,(C) AND &01 CP &01 ; Is ENTER/0 pressed? RET ; Play a note from the sliding scale as intro message scrolls past ; ================================================================ L96DE: LD E,A LD C,&FE L96E1: LD D,A RES 4,D RES 3,D LD B,E L96E7: CP B JR NZ,L96EC LD D,&18 L96EC: OUT (C),D DJNZ L96E7 DEC A JR NZ,L96E1 RET ; Unused code ; =========== ; This code space can be used for patches to the game engine. ; The 7-bit room patch uses the space at &96F4-&96FF, and room 87 uses &97xx. IF MOREROOMS L96F4: LD C,(HL) ; Get object's room number RES 7,C ; Remove screen address bit8 LD A,(HERE) ; Get current room CP C ; Is object in this room? RET Z ; Yes, exit to collect INC H ; Point HL back to 'collected' flags INC H RET ; and exit to check next object NOP ELSE L96F4: LD HL,&5E00 LD DE,&5800 LD BC,&0200 LDIR ; Copy buffer 2 attributes to screen LD HL,&4000 LD DE,&4001 LD BC,&0FFF LD (HL),&18 ; Set display to vertical lines LDIR LD BC,&FEFE ; Read keyboard L970F: IN A,(C) ; Read V-C-X-Z-SHIFT BIT 2,A ; Is X pressed? JP Z,&0000 ; If pressed, jump to reset JR L970F ; Loop if no key pressed ENDIF ; Screen attributes ; ================= ; The startup screen attributes define where the triangle will be displayed as well ; as forming the words JET SET WILLY. DEFS ATTRS-$ DEFB &00,&00,&00,&00,&00,&00,&00,&00,&00,&00,&00,&00,&00,&00,&00,&00 DEFB &00,&00,&00,&00,&00,&00,&00,&00,&00,&00,&00,&00,&00,&00,&00,&00 DEFB &00,&00,&00,&00,&00,&00,&00,&00,&00,&00,&00,&00,&00,&00,&00,&00 DEFB &00,&00,&00,&00,&00,&00,&00,&00,&00,&00,&00,&00,&00,&00,&00,&00 DEFB &00,&00,&00,&00,&00,&00,&00,&00,&00,&00,&00,&00,&00,&00,&00,&00 DEFB &00,&00,&28,&28,&05,&05,&00,&00,&00,&00,&00,&00,&00,&00,&00,&00 DEFB &00,&00,&00,&00,&D3,&D3,&D3,&00,&D3,&D3,&D3,&00,&D3,&D3,&D3,&00 DEFB &28,&D3,&D3,&D3,&25,&D3,&D3,&D3,&00,&D3,&D3,&D3,&00,&00,&00,&00 DEFB &00,&00,&00,&00,&00,&D3,&00,&00,&D3,&00,&00,&00,&00,&D3,&28,&28 DEFB &2D,&D3,&25,&25,&24,&D3,&00,&00,&00,&00,&D3,&00,&00,&00,&00,&00 DEFB &00,&00,&00,&00,&00,&D3,&00,&00,&D3,&D3,&D3,&00,&28,&D3,&2D,&2D DEFB &25,&D3,&D3,&D3,&24,&D3,&D3,&D3,&00,&00,&D3,&00,&00,&00,&00,&00 DEFB &00,&00,&00,&00,&00,&D3,&00,&00,&D3,&00,&28,&28,&2D,&D3,&25,&25 DEFB &24,&24,&0C,&D3,&24,&D3,&00,&00,&00,&00,&D3,&00,&00,&00,&00,&00 DEFB &00,&00,&00,&00,&D3,&D3,&00,&00,&D3,&D3,&D3,&2D,&25,&D3,&24,&24 DEFB &04,&D3,&D3,&D3,&24,&D3,&D3,&D3,&00,&00,&D3,&00,&00,&00,&00,&00 DEFB &00,&00,&00,&00,&00,&00,&00,&00,&29,&29,&2D,&2D,&2C,&2C,&04,&04 DEFB &00,&00,&09,&09,&24,&24,&00,&00,&00,&00,&00,&00,&00,&00,&00,&00 DEFB &00,&00,&00,&00,&00,&00,&00,&00,&09,&09,&29,&29,&2D,&2D,&05,&05 DEFB &00,&00,&09,&09,&24,&24,&00,&00,&00,&00,&00,&00,&00,&00,&00,&00 DEFB &00,&00,&00,&00,&00,&00,&D3,&00,&08,&08,&D3,&09,&D3,&29,&D3,&2D DEFB &05,&05,&D3,&09,&24,&D3,&00,&00,&00,&D3,&00,&00,&00,&00,&00,&00 DEFB &00,&00,&00,&00,&00,&00,&D3,&00,&00,&00,&D3,&08,&D3,&09,&D3,&29 DEFB &2D,&2D,&D3,&09,&24,&D3,&00,&00,&00,&D3,&00,&00,&00,&00,&00,&00 DEFB &00,&00,&00,&00,&00,&00,&D3,&00,&D3,&00,&D3,&00,&D3,&08,&D3,&09 DEFB &29,&29,&D3,&09,&24,&D3,&D3,&D3,&D3,&D3,&00,&00,&00,&00,&00,&00 DEFB &00,&00,&00,&00,&00,&00,&D3,&00,&D3,&00,&D3,&00,&D3,&00,&D3,&08 DEFB &09,&09,&D3,&09,&24,&24,&00,&D3,&00,&00,&00,&00,&00,&00,&00,&00 DEFB &00,&00,&00,&00,&00,&00,&D3,&D3,&D3,&D3,&D3,&00,&D3,&00,&D3,&D3 DEFB &D3,&08,&D3,&D3,&D3,&24,&00,&D3,&00,&00,&00,&00,&00,&00,&00,&00 DEFB &00,&00,&00,&00,&00,&00,&00,&00,&00,&00,&00,&00,&00,&00,&00,&00 DEFB &00,&00,&08,&08,&04,&04,&00,&00,&00,&00,&00,&00,&00,&00,&00,&00 ; The attributes for the bottom third of the screen are used for the room name, items ; collected line, and for the colours of the lives left Willys. DEFB &46,&46,&46,&46,&46,&46,&46,&46,&46,&46,&46,&46,&46,&46,&46,&46 ; Room name DEFB &46,&46,&46,&46,&46,&46,&46,&46,&46,&46,&46,&46,&46,&46,&46,&46 DEFB &00,&00,&00,&00,&00,&00,&00,&00,&00,&00,&00,&00,&00,&00,&00,&00 DEFB &00,&00,&00,&00,&00,&00,&00,&00,&00,&00,&00,&00,&00,&00,&00,&00 DEFB &00,&00,&00,&00,&00,&00,&00,&00,&00,&00,&00,&00,&00,&00,&00,&00 DEFB &00,&00,&00,&00,&00,&00,&00,&00,&00,&00,&00,&00,&00,&00,&00,&00 DEFB &01,&02,&03,&04,&05,&06,&07,&07,&07,&07,&07,&07,&07,&07,&07,&07 ; Items DEFB &07,&07,&07,&07,&07,&07,&07,&07,&07,&07,&06,&05,&04,&03,&02,&01 DEFB &00,&00,&00,&00,&00,&00,&00,&00,&00,&00,&00,&00,&00,&00,&00,&00 DEFB &00,&00,&00,&00,&00,&00,&00,&00,&00,&00,&00,&00,&00,&00,&00,&00 DEFB &45,&45,&06,&06,&04,&04,&41,&41,&05,&05,&43,&43,&44,&44,&02,&02 ; Lives left DEFB &45,&45,&06,&06,&04,&04,&41,&41,&05,&05,&43,&43,&44,&44,&02,&02 DEFB &45,&45,&06,&06,&04,&04,&41,&41,&05,&05,&43,&43,&44,&44,&02,&02 DEFB &45,&45,&06,&06,&04,&04,&41,&41,&05,&05,&43,&43,&44,&44,&02,&02 DEFB &00,&00,&00,&00,&00,&00,&00,&00,&00,&00,&00,&00,&00,&00,&00,&00 DEFB &00,&00,&00,&00,&00,&00,&00,&00,&00,&00,&00,&00,&00,&00,&00,&00