Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- ; 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
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement