Pobtastic / Wheelie / Level Names

Created Sat, 02 Mar 2024 16:08:44 +0000 Modified Sat, 04 May 2024 21:31:28 +0000

The Level Names

  1. THE BOUNCING HEDGEHOGS
  2. THE WILY WALLABIES
  3. THE KILLER BEES
  4. ALL THAT BOUNCES
  5. THE SWARM
  6. SPRING AND STING
  7. NIGHTMARE PARK
  8. ABANDON ALL HOPE!

The Routine

Keep in mind hexadecimal is used here (Skoolkit syntax is e.g. $4000 which is equivalent to address 16384).

Data

The data structure is rigid; each level contains one attribute byte and three lines containing nine characters.

; Messaging: Level Names
;
; Level 1.
@label=Messaging_Level1
t$BBA0 DEFB $42         ; Attribute INK: RED, PAPER: BLACK (BRIGHT).
 $BBA1 DEFM "   THE   " ; {#UDGTABLE(default,centre)
 $BBAA DEFM "BOUNCING " ; { #FONT:(   THE   )$3D00,attr=$42(level-01-01) }
 $BBB3 DEFM "HEDGEHOGS" ; { #FONT:(BOUNCING )$3D00,attr=$42(level-01-02) }
                        ; { #FONT:(HEDGEHOGS)$3D00,attr=$42(level-01-03) }
                        ; UDGTABLE#}
; Level 2.
@label=Messaging_Level2
 $BBBC DEFB $45         ; Attribute INK: CYAN, PAPER: BLACK (BRIGHT).
 $BBBD DEFM "   THE   " ; {#UDGTABLE(default,centre)
 $BBC6 DEFM "  WILY   " ; { #FONT:(   THE   )$3D00,attr=$45(level-02-01) }
 $BBCF DEFM "WALLABIES" ; { #FONT:(  WILY   )$3D00,attr=$45(level-02-02) }
                        ; { #FONT:(WALLABIES)$3D00,attr=$45(level-02-03) }
                        ; UDGTABLE#}
; Level 3.
@label=Messaging_Level3
 $BBD8 DEFB $46         ; Attribute INK: YELLOW, PAPER: BLACK (BRIGHT).
 $BBD9 DEFM "   THE   " ; {#UDGTABLE(default,centre)
 $BBE2 DEFM " KILLER  " ; { #FONT:(   THE   )$3D00,attr=$46(level-03-01) }
 $BBEB DEFM "  BEES   " ; { #FONT:( KILLER  )$3D00,attr=$46(level-03-02) }
                        ; { #FONT:(  BEES   )$3D00,attr=$46(level-03-03) }
                        ; UDGTABLE#}
; Level 4.
@label=Messaging_Level4
 $BBF4 DEFB $50         ; Attribute INK: BLACK, PAPER: RED (BRIGHT).
 $BBF5 DEFM "   ALL   " ; {#UDGTABLE(default,centre)
 $BBFE DEFM "  THAT   " ; { #FONT:(   ALL   )$3D00,attr=$50(level-04-01) }
 $BC07 DEFM " BOUNCES " ; { #FONT:(  THAT   )$3D00,attr=$50(level-04-02) }
                        ; { #FONT:( BOUNCES )$3D00,attr=$50(level-04-03) }
                        ; UDGTABLE#}
; Level 5.
@label=Messaging_Level5
 $BC10 DEFB $70         ; Attribute INK: BLACK, PAPER: YELLOW (BRIGHT).
 $BC11 DEFM "   THE   " ; {#UDGTABLE(default,centre)
 $BC1A DEFM "  SWARM  " ; { #FONT:(   THE   )$3D00,attr=$70(level-05-01) }
 $BC23 DEFM "         " ; { #FONT:(  SWARM  )$3D00,attr=$70(level-05-02) }
                        ; { #FONT:(         )$3D00,attr=$70(level-05-03) }
                        ; UDGTABLE#}
; Level 6.
@label=Messaging_Level6
 $BC2C DEFB $56         ; Attribute INK: YELLOW, PAPER: RED (BRIGHT).
 $BC2D DEFM " SPRING  " ; {#UDGTABLE(default,centre)
 $BC36 DEFM "   AND   " ; { #FONT:( SPRING  )$3D00,attr=$56(level-06-01) }
 $BC3F DEFM "  STING  " ; { #FONT:(   AND   )$3D00,attr=$56(level-06-02) }
                        ; { #FONT:(  STING  )$3D00,attr=$56(level-06-03) }
                        ; UDGTABLE#}
; Level 7.
@label=Messaging_Level7
 $BC48 DEFB $44         ; Attribute INK: GREEN, PAPER: BLACK (BRIGHT).
 $BC49 DEFM "NIGHTMARE" ; {#UDGTABLE(default,centre)
 $BC52 DEFM "  PARK   " ; { #FONT:(NIGHTMARE)$3D00,attr=$44(level-07-01) }
 $BC5B DEFM "         " ; { #FONT:(  PARK   )$3D00,attr=$44(level-07-02) }
                        ; { #FONT:(         )$3D00,attr=$44(level-07-03) }
                        ; UDGTABLE#}
; Level 8.
@label=Messaging_Level8
 $BC64 DEFB $D6         ; Attribute INK: YELLOW, PAPER: RED (BRIGHT) FLASH: ON.
 $BC65 DEFM " ABANDON " ; {#UDGTABLE(default,centre)
 $BC6E DEFM "   ALL   " ; { #FONT:( ABANDON )$3D00,attr=$D6(level-08-01) }
 $BC77 DEFM "  HOPE!  " ; { #FONT:(   ALL   )$3D00,attr=$D6(level-08-02) }
                        ; { #FONT:(  HOPE!  )$3D00,attr=$D6(level-08-03) }
                        ; UDGTABLE#}

Fetching The Current Level Name

The routine finds the level data using this formula:

\( LevelData = 48032 + (CurrentLevelNumber * 28) \)

Obviously given this is z80 assembly, which does not have multiplication built-in, the code has to do this manually with addition.

; Print Level Name
;
; #UDGTABLE(default,centre)
; { #PUSHS #SIM(start=$75AA,stop=$7638)#SIM(start=$74DC,stop=$7518)
; #SCR$02(game) #POPS }
; UDGTABLE#
;
; This looks confusing, but it's basically #REGhl=#N$BBA0+(level*#N$1C).
;
; --------------------------------------------------------
; | Level | Address | Attribute | Level Name             |
; |-------|---------|-----------|------------------------|
; | $00   | $BBA0   | $42       | THE BOUNCING HEDGEHOGS |
; | $01   | $BBBC   | $45       | THE WILY WALLABIES     |
; | $02   | $BBD8   | $46       | THE KILLER BEES        |
; | $03   | $BBF4   | $50       | ALL THAT BOUNCES       |
; | $04   | $BC10   | $70       | THE SWARM              |
; | $05   | $BC2C   | $56       | SPRING AND STING       |
; | $06   | $BC48   | $44       | NIGHTMARE PARK         |
; | $07   | $BC64   | $D6       | ABANDON ALL HOPE!      |
; --------------------------------------------------------
@label=PrintLevelName
c$74DC LD A,($7820)  ; Fetch the current level and store it in #REGa.
 $74DF ADD A,A       ; {Multiply it by 4 and store the result in #REGl (level*04).
 $74E0 ADD A,A       ;
 $74E1 LD L,A        ; }
 $74E2 ADD A,A       ; Multiply it again by 2 and store this in #REGh (level*08).
 $74E3 LD H,A        ; }
 $74E4 ADD A,A       ; {Multiply it again by 2 (level*16), add this together
 $74E5 ADD A,H       ; with #REGh, #REGl and 160 and the store the result in
 $74E6 ADD A,L       ; #REGl.
 $74E7 ADD A,$A0     ;
 $74E9 LD L,A        ; }
 $74EA LD A,$00      ; {#REGh=187+carry.
 $74EC ADC A,$BB     ;
 $74EE LD H,A        ; }

All level names are printed in the same place.

Game HUD

 $74EF LD DE,$5A57   ; #REGde=#N$5A57 (attribute buffer location).
 $74F2 LD C,$03      ; #REGc=#N$03 (line counter; all levels are three lines).
@label=LevelName_AttributeLineLoop
*$74F4 LD A,(HL)     ; Fetch the attribute byte and store it in #REGa.
 $74F5 LD B,$09      ; #REGb=#N$09 (character counter; all lines contain nine characters).
@label=LevelName_AttributeLoop
*$74F7 LD (DE),A     ; Write the attribute byte to the attribute buffer.
 $74F8 INC E         ; Move one character block right.
 $74F9 DJNZ $74F7    ; Decrease the character counter by one and loop back to
                     ; LevelName_AttributeLoop until the counter is zero.
; Move down one line and reset the position; 9 + 23 = 32 (i.e. one full line).
 $74FB LD A,E        ; {#REGe+=23.
 $74FC ADD A,$17     ;
 $74FE LD E,A        ; }
 $74FF DEC C         ; Decrease the line counter by one.
 $7500 JR NZ,$74F4   ; Jump to LevelName_AttributeLineLoop until the line counter is zero.
---
title: "Subroutine: Set Attributes For Colouring The Level Name"
---
flowchart TD;

    DE["DE=23127 (in the attribute buffer)"]-->C["C=03 (line counter)"];
    C-->A[A=Attribute byte from HL];
    A-->B["B=09 (number of characters in each line)"];
    B-->Write["Write the attribute byte to DE (attribute buffer pointer)"];
    Write-->E
    E["DE+=1 (move the attribute buffer pointer right)"]-->Loop_B
    Loop_B[Decrease the character counter by one until it is zero]-->Write
    Loop_B-->|All character blocks have been processed for this line|Loop_C
    Loop_C[Decrease the line counter by one until it is zero]-->A
    Loop_C-->|All lines have processed|End

Then similarly, print the characters to the screen buffer.

 $7502 INC L         ; Move #REGhl to the start of the level string data.
 $7503 EXX           ; Switch to the shadow registers.
 $7504 LD DE,$5057   ; #REGde'=#N$5057 (screen buffer location).
 $7507 EXX           ; Switch back to the normal registers.
 $7508 LD C,$03      ; #REGc=#N$03 (line counter; all levels are three lines).
@label=PrintLevelName_Loop
*$750A LD B,$09      ; #REGb=#N$09 (character counter; all lines contain nine characters).
 $750C CALL $74D3    ; Call Print_Loop.
; Move down one line (and reset the position - #N$09+#N$17=#N$20).
 $750F EXX           ; Switch to the shadow registers.
 $7510 LD A,E        ; {#REGe'+=23.
 $7511 ADD A,$17     ;
 $7513 LD E,A        ; }
 $7514 EXX           ; Switch back to the normal registers.
; Have we printed all three lines of the level name yet?
 $7515 DEC C         ; Decrease the line counter by one.
 $7516 JR NZ,$750A   ; Jump to PrintLevelName_Loop until #REGc is zero.
 $7518 RET           ; Return.
---
title: "Subroutine: Printing The Level Name To The Screen Buffer"
---
flowchart TD;
    L[HL=Start of the level string data]-->|Shadow Registers|DE
    DE["DE'=20567 (in the screen buffer)"]-->|Normal Registers|C["C=03 (line counter)"];
    C-->Print_Loop;
    Print_Loop["Call Print_Loop subroutine (uses B=09)"]-->|Shadow Registers|E
    E["DE'+=23 (Move down one line in the screen buffer)"]-->|Normal Registers|Loop_C
    Loop_C[Decrease the line counter by one until it is zero]-->Print_Loop
    Loop_C-->|All level data has been printed to the screen|Ret[Return]

Wheelie Disassembly