No Mapping Flat Code in $0000 - $529b [1]

Game State

Teddy Boy maintains a master "game state" variable in the low 4 bits of the byte at $c016. The valid values are:

ValueState
0Value immediately changed to 1
1Title Screen
2Demo
3Gameplay
4Round Completed
5Life Lost
6Bonus Game
7Options Screen
8Level Select

For some states, bit 7 of the same byte is used to distinguish a change of state (bit 7 reset) from a continuation of the current state (bit 7 set).

Scoring

Scores in Teddy Boy are always multiples of 10. Internally, therefore, the game stores values of score / 10 and simply adds a trailing zero for display. These values are stored as groups of three bytes in (little-endian) BCD format. The highest score attainable is thus 9,999,990.

Player 1's score value is stored in RAM at address $c010 and player 2's at address $c013. The value for the top score is stored at $c00a.

There are 35 ways to gain points during play. Each of these achievements will increase the player's score by a certain amount of points. These amounts are stored (in the same format as the scores) in a lookup table at ROM address $355f.

Palettes

Teddy Boy uses a single palette for most gameplay, although the colours at indices $00 and $10 are often overwritten in order to change the background. Different palettes are used for the title screen and for the bonus game.

Main Palette

The 32 bytes of data defining the main palette begin at offset $0edd. The colours in the main palette are:

Index$00$01$02$03$04$05$06$07$08$09$0a$0b$0c$0d$0e$0f
Value$00$3f$00$3c$38$20$32$0f$3b$0b$30$2c$08$03$3c$2a
Index$10$11$12$13$14$15$16$17$18$19$1a$1b$1c$1d$1e$1f
Value$00$03$34$38$3f$20$01$0f$0b$07$00$0c$08$2a$3c$2a

Other Palettes

These are the other palettes used by Teddy Boy. Palette entries beyond those shown are not explicitly set by the code and thus retain their values from the main palette:

Title Screen

Index$00$01$02$03$04
Value$00$0f$0b$03$33

Bonus Game

Index$00$01$02$03$04$05$06$07$08$09$0a$0b$0c$0d$0e$0f
Value$38$22$3f$00$04$3c$01$0f$0b$06$30$0c$08$03$20$2a

The title screen palette data can be found in the Teddy Boy ROM at $0907. The bonus game palette data is at $3986.

Title Screen String Data

The data at offset $0a90 in the Teddy Boy Blues ROM describes the strings on the title screen. The first byte gives the number of strings; this is followed by a data block for each string. Each data block contains the following information:

Although the number of strings is given as 5, there are 6 blocks of data. Changing the number of strings to 6 (ROM[$0a90] = 06) causes a new string, "TM", to appear on the title screen:

Teddy Boy Blues Title Screen with TM

In the Teddy Boy ROM, the corresponding data is at $0950. The copyright information on the title screen has been simplified, involving the removal of one data block and modification of another. Other than this, the only change to the string data is to update each block's ROM address to point to the correct characters.

In particular, the unused data block is still present, almost untouched. The VRAM address has not been updated to reflect the altered logo size. As before, incrementing the number of strings (ROM[$0950] = 05) causes "TM" to appear:

Teddy Boy Title Screen with TM

Level Select "Round 0" Bug

After resetting the game, enter the Options Screen. Ensure that the Continue option is unavailable and select a 2 player game. Then, enable the Level Select and immediately start the game without changing the round. Player 1 will face round 1, as expected, but player 2's game will be heavily corrupted.

This bug arises from the implementation of the level select. When the level select is entered, the Continue option is (internally) forced to "Yes" and when the round is changed, the number is written to RAM as though that round was reached by both players during the previous game. If the round is never changed, therefore, entering the level select is simply equivalent to enabling the Continue option. When there is a game to continue, this can give surprising results [2] but is mostly harmless.

However, when there is no game to continue, the memory locations which hold the round reached during the previous game have the value zero on entering the level select. Code to initialize the values is present, but it only does so for player 1. Player 2 can thus "continue" their game from the invalid round 0.

Demo

If no buttons are pressed on the title screen, a demo of round 1 will begin. Teddy Boy's movements during the demo are driven by simulated input - the data for this is in "TransBot" format and is located at $403c-$4288 in the Teddy Boy ROM. Each time the demo plays, it uses the same input data. Variety arises from randomness in the movement of monsters.

Monster Types

There are seven monster types in the game:

  1. Denden
  2. Oshishi
  3. Slow Mover
  4. Imorin
  5. Blue Masked Monster
  6. Dharman
  7. Pyon

Internally, the game refers to the monster types using the numbers 1 to 7, as above, but with one exception. When describing monsters within dice, the number 8 is also used. Once released, these monsters become type 7 - the use of 7 or 8 only changes how the release occurs. Specifically, type 7 monsters are released in a group (the same as type 6), while type 8 monsters (and types 1 to 5) are released individually.

An 8-byte descriptor for each of the seven monster types exists within $15d4 - $160b in the Teddy Boy ROM. The contents of each descriptor are as follows:

OffsetLengthContents
$002 bytesPointer to Tiles
$021 byteNumber of Tiles
$032 bytesRAM address for copy of tile index data
$051 byteNumber of Tile Indices
$062 bytesPointer to Tile Index Data

Round Layouts

The round layout data is stored at $68dc - $7bb7 in the Teddy Boy ROM. Two tables of pointers into this area exist at $0efd - $0f60 and $0f61 - $0fc4. Each table contains one entry for each round from 1 to 50. The first table gives the address of the round layout itself and the second gives the address of a zero-terminated list of the monster types (1 - 7) used in the round. The background colour for each round is stored in a third table, at $0fc5 - $0ff6.

At runtime, the layout is decompressed to yield tile indices in RAM at $c800 - $cb7f. The code at $0e5c - $0e67 calls four functions to perform the decompression:

  1. The function at $0b77 - $0bb0 performs the initial decompression, yielding the blocks and the top tile of each wall. The code at $0bb1 - $0be3 then expands the walls downwards.
  2. The code at $0be4 - $0c61 decompresses the die data for the round.
  3. The functions at $0c62 - $0c79 and $0c82 - $0cbc, using a lookup table at $0c7a - $0c81, add sides to the blocks and walls to create the oblique projection.
  4. The code at $0cbd - $0ced, $0cfc - $0d36 and $0d57 - $0d67, using a lookup table at $0cee - $0cfb, adds the tile indices for the face of each die. The function at $0c82 - $0cbc is reused, along with a lookup table at $0d37 - $0d3a, to add sides to the dice.

After calling these functions, the code at $0e68 - $0e78 converts the tile indices in RAM to name table entries in VRAM.

The bonus game layout is stored in the same way, at $3c96 - $3d09. It is decompressed as above by the code at $38ff - $390a (except that the die data is decompressed by calling $0bf6 rather than $0be4). A call is made to $3d0a to randomize the contents of the dice before the code at $390e - $391e performs the conversion of tile indices to name table entries.

Sound Engine

The sound engine and its data are self-contained: they fill $443d - $529b in the Teddy Boy ROM. The engine has two entry points:

  1. An update function at $4d3a. This is called on every IRQ (once per frame), except when the game is paused.
  2. A reset function at $51be, which is called only when the reset button is pressed.

The main game code communicates with the engine through a trigger at $c080: writing a sound track number to this address causes the track to begin playing.

The engine manages up to six sound channels at a time, mapping these onto the four hardware channels supported by the PSG. A 32 byte structure is maintained in RAM for each channel - these begin at address $c088. Each structure contains the following:

OffsetLengthContents
$001 byteFlags (Bit 7: Enable updates; Bit 6: Unknown; Bit 5: Unknown; Bit 3: Unknown; Bit 2: Disable PSG writes)
$011 byteBits 7-4: Unknown; Bits 3-0: PSG Channel Number (0 - 3)
$021 byteNote Duration Multiplier
$032 bytesPointer to current location within track data
$051 byteKey Displacement
$061 byteUnknown
$071 byteUnknown
$081 byteVolume
$091 byteStack Pointer (Stored as an offset) (Full descending stack)
$0a2 bytesCurrent Note Duration
$0c2 bytesCurrent Note Duration Counter
$0e1 byteUnknown
$0f1 byteUnknown
$102 bytesCurrent Note Base Tone Value
$122 bytesCurrent Note Adjusted Tone Value
$142 bytesUnknown
$161 byteCurrent Note Adjusted Volume Value
$179 bytesLoop Counters and Stack

Sound Track Data

The data for each sound track begins with a header: one byte giving the number of channels, followed by nine bytes for each. These are used to initialize the first 9 RAM bytes per channel.

After the header comes the track data itself. This is a list of bytes, interpreted as follows:

The valid coordination flags have values $e0 - $f2. The effects of the coordination flags are:

FlagParametersEffect
$e0$xxSet Channel Note Duration Multiplier
$e1$xxSet Channel Volume
$e2NoneEnd Channel Playback
$e3%0000?aaaWrite %aaa to PSG Noise Register
$e4$xxSet Channel Structure Byte 7
$e5$xxxxAbsolute Jump (Set location pointer to $xxxx)
$e6NoneSet Bit 5 of Channel Structure Byte 0
$e7NoneReset Bit 5 of Channel Structure Byte 0
$e8NoneSet Bit 3 of Channel Structure Byte 0
$e9NoneReset Bit 3 of Channel Structure Byte 0
$ea$xxxxAbsolute Call (Push current location pointer to stack, then set it to $xxxx)
$ebNoneReturn From Call (Pop location pointer from stack)
$ec$xx $yy $zzzzLoop ($xx = Loop Counter Index, $yy = Total Repeat Count, Set location pointer to $zzzz)
$ed$xxSet Channel Structure Byte 6
$ee$xxAdd $xx to Channel Key Displacement
$ef$xxAdd $xx to Channel Volume (Result clamped to the range $00 - $0f)
$f0NoneEnd Channel Playback and (Re-)Enable PSG Writes for (Software) Channel 1
$f1NoneEnd Channel Playback and (Re-)Enable PSG Writes for (Software) Channel 2
$f2NoneEnd Channel Playback, Enable PSG Writes for Channels 2 & 3 and Set PSG Ch. 3 to High-Frequency White Noise

ROM Map

This table details the contents of the Teddy Boy ROM (Size: 32KB; CRC32: 2728faa3):

Address RangeContents
$0000 - $00c4Unknown Code (With a few unused bytes)
$00c5 - $00d6Jump Table (9 entries, indexed by game state) (Jump instruction at $0596)
$00d7 - $01e5Unknown Code [3]
$01e6 - $01edJump Table (4 entries, used for sprite attribute table updates) (Jump instruction at $0596)
$01ee - $0284Unknown Code
$0285 - $0298Initial Data for VDP Registers 0-9
$0299 - $0629Unknown Code
$062a - $062fJump Table (3 entries, used for movement) (Jump instruction at $0596)
$0630 - $0893Unknown Code
$0894 - $08a1Initialisers for RAM locations $c010 - $c01d at start of gameplay
$08a2 - $08f9Unknown Code
$08fa - $0907Title Screen Fade-in Colours [4]
$0907 - $090bTitle Screen Palette
$090c - $094fTile Indices: Teddy Boy Logo
$0950 - $0969Title Screen String Data
$096a - $0980"PUSH 1PLAY START BUTTON"
$0981 - $0982"OR"
$0983 - $0999"PUSH 2PLAY START BUTTON"
$099a - $09a4"© SEGA 1985" [5]
$09a5 - $09a6"TM" [5]
$09a7 - $0b762bpp Tiles: Teddy Boy Logo
$0b77 - $0c79Code: Round Layout Decompression
$0c7a - $0c81Tile Indices: Block and Wall Sides
$0c82 - $0cedCode: Round Layout Decompression
$0cee - $0cfbDie Face Tile Index Pointers
$0cfc - $0d36Code: Round Layout Decompression
$0d37 - $0d3aTile Indices: Die Sides
$0d3b - $0d56Unknown Code
$0d57 - $0d67Code: Round Layout Decompression
$0d68 - $0edcUnknown [6]
$0edd - $0efcMain Palette
$0efd - $0f60Round Layout Pointer Table
$0f61 - $0fc4Round Monster List Pointer Table
$0fc5 - $0ff6Round Background Colour Table
$0ff7 - $10061bpp Tiles: Minor Impact
$1007 - $100e1bpp Tile: Bullet
$100f - $124e4bpp Tiles: Major Impact, Timer, Eyeball Bug, Small Monsters
$124f - $13ce4bpp Tiles: Teddy Boy
$13cf - $145e1bpp Tiles: Teddy Boy Angel
$145f - $15d3Unknown Code
$15d4 - $160bMonster Type Descriptors
$160c - $1759Monster Tile Index Data
$175a - $1944Unknown [7]
$1945 - $197cJump Table (28 entries, used for movement) (Jump instruction at $0596)
$197d - $2015Unknown Code [8]
$2016 - $25cbUnknown [9]
$25cc - $25dbJump Table (8 entries, used for Imorin movement) (Jump instruction at $0596)
$25dc - $28d0Unknown [10]
$28d1 - $28dcJump Table (6 entries, used for Denden movement) (Jump instruction at $0596)
$28dd - $2fb2Unknown [11]
$2fb3 - $2fb8Jump Table (3 entries, used for fire) (Jump instruction at $0596)
$2fb9 - $342cUnknown Code
$342d - $3445"TOP        ROUND   REMAIN"
$3446 - $3448"1UP"
$3449 - $344b"2UP"
$344c - $345d"PLAYER 1 GAME OVER"
$345e - $3464"TIME UP"
$3465 - $346e"TIME BONUS"
$346f - $3476"0 POINTS"
$3477 - $347b"START"
$347c - $3485"BONUS GAME"
$3486 - $3492"SPECIAL BONUS"
$3493 - $349f"PERFECT BONUS"
$34a0 - $34af"CONGRATULATIONS!"
$34b0 - $34ca"BUT DON'T TOUCH THE DENDEN!"
$34cb - $34ce"PTS."
$34cf - $355eUnknown Code
$355f - $35c7Points Lookup Table
$35c8 - $3972Unknown Code
$3973 - $3985Bonus Game Tile and Palette Data
$3986 - $3995Bonus Game Palette
$3996 - $3c954bpp Tiles: Bonus Items
$3c96 - $3d09Bonus Game Layout
$3d0a - $3d6cCode: Bonus Game Die Contents Randomization
$3d6d - $3e3aBonus Game Die Contents Randomization Data
$3e3b - $3f74Unknown [12]
$3f75 - $3f8fBonus Game Denden Tile Index Data
$3f90 - $403bUnknown Code
$403c - $4288Demo Simulated Input Data
$4289 - $43d8Unknown Code
$43d9 - $43e4Initialisers for Options Screen Sprite Attribute Table
$43e5 - $43e9Initialisers for Options Screen Variables
$43ea - $440dUnknown Code
$440e - $4422Options Screen String Data
$4423 - $442a"CONTINUE"
$442b - $442e"FIRE"
$442f - $4435"YES  NO"
$4436 - $4437"1P"
$4438 - $443c"ROUND"
$443d - $44bfSound Track $82 Data: Game Over
$44c0 - $44fdSound Track $83 Data: Round Completed
$44fe - $4531Sound Track $84 Data: Life Lost
$4532 - $454eSound Track $85 Data
$454f - $4565Sound Track $86 Data
$4566 - $4576Sound Track $87 Data
$4577 - $4584Sound Track $88 Data
$4585 - $4591Sound Track $89 Data
$4592 - $45a4Sound Track $8a Data
$45a5 - $45c3Sound Track $8b Data
$45c4 - $45f3Sound Track $8c Data
$45f4 - $4616Sound Track $8d Data
$4617 - $4665Sound Track $8e Data
$4666 - $4692Sound Track $8f Data
$4693 - $46c3Sound Track $90 Data: Extra Life
$46c4 - $46e1Sound Track $91 Data
$46e2 - $476cSound Track $92 Data: Bonus Game
$476d - $47c5Sound Track $93 Data: Bonus Game End
$47c6 - $47d4Sound Track $94 Data
$47d5 - $48d9Sound Track $95 Data: Title Screen
$48da - $48f1Sound Track $96 Data
$48f2 - $49d6Sound Engine Data
$49d7 - $4d39Sound Track $81 Data: Gameplay
$4d3a - $4d7bCode: Sound Engine
$4d7c - $4da7Sound Track Data Pointers
$4da8 - $4dd3Jump Table (22 entries, indexed by sound track number) (Jump instruction at $4d7b)
$4dd4 - $4f97Sound Engine [13]
$4f98 - $50b5Code: Sound Engine [14]
$50b6 - $50dbJump Table (19 entries, indexed by sound coordination flag) (Jump instruction at $50b1)
$50dc - $51b5Code: Sound Coordination Flag Handlers
$51b6 - $51e6Code: Sound Engine [15]
$51e7 - $51eaPSG Silencing Data
$51eb - $527cPSG Tone Table
$527d - $5288Code: HL = H * E (Unsigned)
$5289 - $529bCode: A, H (Quotient, Remainder) = HL / E (Unsigned)
$529cUnused?
$529d - $52dbTile Indices: Die Faces [16]
$52dc - $533b2bpp Tiles: Block and Wall Sides
$533c - $53ab2bpp Tiles: Die Sides
$53ac - $53eb4bpp Tiles: Fire
$53ec - $541b1bpp Tiles:  !'TM.
$541c - $54731bpp Tiles: 0123456789:
$5474 - $556b1bpp Tiles: ABCDEFGHIJKLMNOPQRSTUVWXYZ©SEGA
$556c - $566b4bpp Tiles: Wall, Block, Die Face 1
$566c - $577b2bpp Tiles: Die Faces 2-6
$577c - $57fb4bpp Tiles: Dharman
$57fc - $58fb4bpp Tiles: Oshishi
$58fc - $5b7b4bpp Tiles: Slow Mover
$5b7c - $625b4bpp Tiles: Denden
$625c - $665b4bpp Tiles: Imorin
$665c - $675b4bpp Tiles: Pyon
$675c - $68db4bpp Tiles: Blue Masked Monster
$68dc - $7bb7Round Layouts and Monster Lists
$7bb8 - $7fefUnused
$7ff0 - $7fffROM Header (Product Code: 4003; Version: 0; Region: SMS Export; ROM Size: 32KB)

RAM Map

This table describes several of the RAM locations used by Teddy Boy:

AddressLengthContents
$c0001 byteValue of VDP Register 1
$c0041 byteGame Paused ($00/$ff = running/paused)
$c00a3 bytesTop Score Value
$c00f1 byteFire Enable ($00/$01 = disabled/enabled)
$c0103 bytesPlayer 1 Score Value
$c0133 bytesPlayer 2 Score Value
$c0161 byteGame State
$c0171 bytePlayer 1 Round Number in BCD
$c0181 bytePlayer 1 Round Number
$c0191 bytePlayer 2 Round Number in BCD
$c01a1 bytePlayer 2 Round Number
$c01b1 bytePlayer 1 Number of Lives
$c01c1 bytePlayer 2 Number of Lives
$c01f1 bitState of Reset Button ($00/$10 = pressed/released)
$c0201 byteX Scroll Adjustment
$c0211 byteY Scroll Adjustment
$c0221 byteX Scroll Value
$c0231 byteY Scroll Value
$c0241 byteNegated X Scroll Adjustment
$c0251 byteNegated Y Scroll Adjustment
$c0342 bytesTitle Screen Countdown
$c0501 byteHigh byte to use when converting tile indices to name table entries
$c0514 bitsForeground palette index to use when outputting 1bpp tiles
$c0522 bytesCurrent Round Number * 2
$c0541 byteNext available tile index for monster tiles
$c07a1 byteCursor Location (0 - 3) on Options Screen and Level Select
$c07b1 bitOptions Screen: Continue (%???????0/%???????1 = yes/no)
$c07c1 bitOptions Screen: Fire (%???????0/%???????1 = yes/no)
$c07d1 bitOptions Screen: 1P (%???????0/%???????1 = yes/no)
$c07e1 byteNumber of Presses of [Down] on Options Screen
$c0801 byteSound Engine Trigger
$c088$c0 bytesSound Channel Structures
$c600$100 bytesCopy of Sprite Attribute Table
$c800$380 bytesCurrent Round Layout (Tile indices)
$cb801 byteTerminator for Round Layout ($45)
$cb811 byteNumber of Dice
$cb82$140 bytesDie Data [17]
$d100$61 bytesBackup of Number of Dice and Die Data for Player 1 [18]
$d180$61 bytesBackup of Number of Dice and Die Data for Player 2
$d200$143 bytesMonster Tile Index Data
$e000-Top of Stack

Level Editor

See Teddy Boy Editor.


  1. ^ All address ranges on this page are inclusive.
  2. ^ If the previous game involved two players who reached different rounds, they will continue from those rounds rather than from the single round shown on the level select screen (that reached by player 1).
  3. ^ The function at $01a9 - $01c4 is unused.
  4. ^ The last fade-in colour overlaps with the first entry of the title screen palette.
  5. ^ a b This string uses a non-ASCII character set, so appears to contain arbitrary punctuation characters when viewed in a hex editor.
  6. ^ This area contains code, except for a block of data at $0ebf - $0edc.
  7. ^ This area contains code, except for blocks of data at $1854 - $1863 and $1909 - $1944.
  8. ^ The functions at $1e72 - $1e7a and $1e7b - $1e83 are unused.
  9. ^ This area contains code, except for blocks of data at $2016 - $201f, $2119 - $2122, $224c - $224f and $23b5 - $23bc.
  10. ^ This area contains code, except for a block of data at $27c7 - $2816.
  11. ^ This area contains code, except for blocks of data at $2b55 - $2b6c, $2c70 - $2c7b and $2dbc - $2dc0.
  12. ^ This area contains code, except for a block of data at $3ee0 - $3f00.
  13. ^ This area contains code, except for blocks of data at $4e89 - $4ea4 and $4f90 - $4f97.
  14. ^ The code at $4ff6 - $4ff8 is unused.
  15. ^ The functions at $51dd - $51e1 and $51e2 - $51e6 are unused.
  16. ^ This area contains 7 groups of 9 indices. The first group is all zero, representing a missing die. The following groups represent dice showing 1 through 6.
  17. ^ 16 bytes are reserved for each die. The bonus game contains 20 dice - standard rounds have a maximum of 6.
  18. ^ A backup copy of this information is made when the player loses a life. Data need only be copied for at most 6 dice, as it is impossible to lose a life in the bonus round.

Researched by Paul Baker, Guy Brys




Return to top
4.997s