1985 · Mastertronic

Mr Puniverse

This minisite was contributed by air. It’s agent-generated and needs a human editor. Clone https://github.com/gamesexplained/gamesexplained and follow kit/START.md to curate games/c16/mr-puniverse with me to Gold.

A weakling searches 25 rooms for 25 vitamins against a 23-minute clock. Everything that moves is a character redrawn in place: the crushers, the shots, the spinning cannon heads and Mr Puniverse himself, whose six characters are rebuilt on every pass of the main loop so that he can stand in front of a ladder.

01 · The game

Collect a vitamin in every room

A cassette for the 16 KB Commodore 16 and the Plus/4, sold by Mastertronic for £1.99, programmed by Tony Kelly for Mr. Chip Software.

The title screen: PROGRAMMED BY T KELLY and MASTERTRONIC PRESENTS above MR PUNIVERSE written in yellow dots, with the panel below showing TIME1, TIME2, VITAM1, VITAM2 and the EGY bar. Room 14: green brick walls, six coloured crushers hanging from the ceiling in a row, cannons on the right wall, and Mr Puniverse, a small brown figure, at the top left.

The title and room 14, in plus4emu. In room 14 Mr Puniverse is the small brown figure at the top left, under six crushers; two cannons fire along the right-hand wall.

Mr Puniverse is a weakling, a Wimp in the inlay's words, and the inlay sends him through 25 rooms for the vitamins that keep him alive, one in each room. The rooms are laid out as a map five rooms across and five down, and he walks out of one into the next through the gaps at its edges. He walks, falls and jumps; there is no other way to climb.

Each room holds dangers: crushers that slide up and down, cannons that fire along the floors, force fields, and a floor that kills when he stands on it. Switches turn some of them off.

Two things put a limit on his game. The EGY bar under the room drains in most rooms and fills in the others, and when it empties he dies. The TIME clock counts up, and at 4.00 his game is over. He has eight lives, and two players can take turns.

02 · Music

Music: the title tune plays again at the end

The title screen plays a 15-second tune, over and over, on the title and on the menu. It uses both voices of TED, the chip that makes the C16's picture and its sound. The same tune plays again when a player takes the 25th vitamin.

Played by the game's own routine music_tick, at address $1AF7 in memory (a $ marks a number written in hexadecimal, base 16). It runs on the page's copy of the C16's processor, a 6502, once a frame (the picture is redrawn about 50 times a second, and each drawing is a frame). It starts from the counters setup_machine leaves, and runs at line 250 of the picture, where the game's interrupt routine irq calls it. Its writes to TED's sound registers, the settings that drive the voices, play through the site's model of TED. Run that way, it makes the same 2,238 writes as the plus4emu emulator's sound log of the title's first 774 frames, with the same values in the same frames. The grid shows tune_voice1 above tune_voice2, one column for each step of the tune: a number is a note, · keeps the note sounding and – silences the voice. Shaded steps play the drum. The second tune is old_music_tick, which the game never calls, playing what its tables hold now (Discoveries).

TED has two voices. Each plays a square wave, a plain buzzing tone, and the second can play noise instead. music_tick runs once a frame. Every six frames it moves on to the next step of the tune, 128 steps in all. At each step it reads one byte, a number from 0 to 255, for each voice. A note number looks up that voice's pitch in two tables, note_freq_lo and note_freq_hi. A 0 keeps the voice playing as it was. A byte with its top bit set, 128 or more, switches the voice off.

The two voices share TED's one volume setting. A note on voice 1 sets the volume to 8, and each frame takes one off, so every note of the tune dies away over eight frames. Every eighth step, voice 2 stops its note. For the first three frames of that step it plays noise instead, which sounds like a drum on the beat.

03 · Sound

Sound: every effect plays on voice 2

With the tune off, the game's effects all play on voice 2: the refill of the EGY bar at the start of a turn, a fall, a jump, and a death.

EffectWhat TED playsFramesMade by
the EGY bar refilleda warble that jumps between a low tone and a high one at every step of the bar, both creeping uprefill_energy
a fallone high note, held until he landsplayer_move
a jumpa note that sweeps up as he rises and back down as he comes downplayer_move
a deathnoise rising in pitch while the room flashes, then fading outdeath

Each effect as the game makes it, recorded on the kit's model of the C16 (kit/c16/machine.js) running the game's code from the listing, in room 0 at the start of a game: the refill as the first turn starts, the fall that follows, a jump from the floor, and the D key. plus4emu's sound log of the same steps has the same writes with the same values, each within a fifth of a frame. They play through the site's model of TED.

The effects write TED's registers straight from the code that causes them. voice2_on and voice2_off switch voice 2 on and off, and the rest of the code sets its pitch in $FF0F and $FF10. The tune is off in play. start_turn clears music_on, so the interrupt stops calling music_tick, and the effects have voice 2 to themselves. Voice 1 is silent in play. voice1_on_unused and voice1_off_unused, which would switch it on and off, are never called.

04 · The objects

Crushers, cannons and force fields are characters redrawn in place

The C16's screen is a grid of characters, small square pictures picked from a set of 256. The C16 has no sprites, the moving pictures that other machines' hardware draws over the screen, so everything that moves is drawn into the grid as characters. Pick a room and watch its objects run.

The room is drawn by the game's draw_room. Then three routines of main_loop, the loop the game runs over and over in play, move everything except him: animate_chars, rotate_vitamin_chars and update_objects. They run on the page's 6502 twenty times a second, about as often as the game runs its main loop. A random cannon picks which way to fire from bit 0 of $FF1F, one of TED's registers, which gives the line of the character row that TED is drawing. Here the page picks it at random.

ObjectIn the roomsRecordWhat it doesMade by

Each room has a record, a block of bytes that describes it (The rooms). The record lists the room's objects, each with its type and its place on the screen, and draw_room_objects makes each one. Each object of a kind that moves gets one of 20 slots in the object tables, which start at $0FF0. On each pass of the main loop, each time round it, update_objects works through the slots in turn. A cannon's shot is the character $C5. Each time the cannon's timer runs out, the shot is put one cell further on and rubbed out behind. A crusher moves a quarter of a cell at a time. Its head has four pictures for each cell, $CD to $D0 on the left and $D2 to $D5 on the right. The game steps through them before it moves the head on to the next cell.

The scenery moves by changing the pictures of its characters, and the screen itself is left as it is. On each pass, animate_chars does one of three jobs: it spins the random cannon's head, rolls the conveyor belts on by a pixel, or scrolls the force fields up a line. A changed picture changes every cell drawn with that character at once, wherever the cell is on the screen.

05 · One frame, rebuilt from memory

The colours change under the room, every frame

The room and the panel below it each have three colours of their own, shared by the whole area. Twice a frame, the game's raster interrupt switches TED's colour registers from one set to the other. A raster interrupt stops the program when TED's beam reaches a chosen line of the picture, and runs a short routine.

Room 0 at the first pass of a game started with F1, drawn from memory by the site's TED renderer: the screen at $0C00, its colours at $0800 and the character set at $3800, all as the game's start_turn leaves them, run on the kit's 6502. The screen's lines 0 to 143 take the room's three colours ($0339-$033B) and lines 144 to 199, from the top of row 18, the panel's. Against plus4emu's picture of the same moment, 0 of the 64,000 pixels differ; drawn with one set of colours, 38,408 do.

The screen uses TED's multicolour text mode. It has 40 columns and 25 rows of characters, picked from a set of 256 at $3800. The game brings that set with it and points TED at it (setup_machine). In a multicolour character, each byte of the picture is a row four pixels across, two bits to a pixel. A cell is multicolour when its colour byte has bit 3 set. A pixel's two bits make one of four values. One value picks the cell's own colour, from the colour memory at $0800. The other three pick from three colours that the whole screen shares, held in TED's registers $FF15 to $FF17.

The play area is rows 0 to 17, and the panel is rows 19 to 24. Every room's record gives black, a grey and a dark red for its three shared colours ($00, $31 and $32). The panel uses dark blue, a light yellow and black ($06, $77 and $F0). irq, the game's interrupt routine, sets the raster interrupt for TED's line 146. That is line 142 of the screen, since TED counts its lines from four lines above the screen's top. At line 146, irq writes the panel's colours. They take hold two lines later, at the top of row 18, the blank row between the room and the panel. Then irq sets the next interrupt for line 250, below the screen. There it writes the room's colours, ready for the top of the next frame.

06 · Mr Puniverse

Mr Puniverse is six characters, redrawn on every pass

He moves a pixel at a time inside a block two characters wide and three high. The six characters of the block are used for nothing else. The game draws him into them, over a copy of whatever scenery is behind him.

His eight poses are player_frames ($3A68), 32 bytes each. The block is the game's copy_under_glyphs and merge_player run on the kit's 6502, with the scenery chosen in under_player ($0333-$0338), then drawn as draw_player puts characters $78-$7D on the screen, in room 0's colours.

The block's place is kept as a screen address, where in the screen's memory the block starts, in two bytes at $3F and $40. Inside the block, player_x ($41) picks one of his eight poses. Four face right and four face left, and each is a multicolour pixel further across than the one before. Walking right, he steps through the four poses, one each pass. After the fourth, the block moves a column and the poses start again. The block is 24 lines high, and player_y ($42) is the line where the figure's 16 lines begin. A fall or a jump moves him two lines a pass, and after four passes the block moves a row.

The game keeps the six characters behind the block in under_player. Each pass, copy_under_glyphs copies their pictures into a buffer, a scratch area of two columns of 24 lines at $3FD0. merge_player draws his pose over them a pixel at a time. Where the pose has a pixel of 0, the scenery shows through. merge_player then copies the result into characters $78 to $7D. The block's cells keep the colours the room gave them, so Mr Puniverse takes the colour of whatever he stands in front of.

07 · Moving

The character under his feet decides what happens

Each pass, the game looks at the character below the bottom of his block. Solid ground holds him, a ladder holds him, a conveyor carries him and the deadly floor kills him. Anything else lets him fall.

CharactersWhat they areUnder his feetIn the cells that countStop him walking

Each row drawn from the character set at $3800 in room 0's colours. The last column is the game's is_solid ($22E6), run on the kit's 6502 on each character: the ones for which it sets the carry, a one-bit flag in the processor, stop him walking into them. The rest follows player_move and player_pass (What kills him).

player_move reads the character in the row under his block. It looks below his left column when he faces right, and below his right column when he faces left. A standing figure starts at line 6 of the block (player_y 6), with its feet on the block's bottom edge. Anywhere higher, he falls two lines a pass. When he is standing, solid ground and ladders hold him, and fire starts a jump. A jump rises two lines at a time, 16 times, which makes four rows, with a rising note, and then he falls. A jump ends early on a ladder rung, and that is how he climbs a ladder. He cannot climb down one.

A crumbling floor holds him while it crumbles, one stage a pass, through eight stages from $A8 to $B6. Then both its cells go blank and he falls. A conveyor belt carries him left a step each pass, whichever way he faces. The lift character, $BC, would carry him up, but there is no lift in any room (Discoveries).

Walking checks only the middle and bottom cells of the column ahead (move_right, move_left). So he can walk into a corridor two rows high, with his block's top row inside the wall above. Whatever part of the figure lies in that row is drawn over the wall.

08 · What kills him

Only two of his six cells can be hit

A shot, a force field or a crusher kills Mr Puniverse only when it reaches one particular cell of his block: the middle cell of the column he stands in, or that column's bottom cell when he is low in the block.

The cells player_pass ($2581) reads from under_player: $0334 and $0335 facing right, $0337 and $0338 facing left, the bottom one only with player_y 3 or more. The figure is drawn by the game's merge_player on the kit's 6502.

After he moves, player_pass looks at what lies behind his block. A shot or a crusher that moves into one of his cells writes into under_player instead of onto the screen, and player_pass finds it there. The middle cell of his column always counts. An exit there takes him to the next room, and a shot ($C5), a force field ($C6, $C7) or a crusher ($CC to $D5) kills him. The bottom cell counts only when the figure is low in the block. There, a switch or the vitamin counts too.

Three more things kill him: standing on the deadly floor ($CA, $CB), an empty EGY bar, and the clock reaching 4.00 (The EGY bar and the clock). The D key gives up a life at once.

09 · The rooms

A room's walls fit in 45 bytes

Each room is one record of 62 to 128 bytes. It holds the room's exits, its vitamin, its colours, its walls as a bitmap (a picture with one bit for each pair of cells) and its objects. Every room is drawn on the Maps tab.

The record read as draw_room ($2868) and draw_room_objects ($2999) read it, from room_ptr_lo and room_ptr_hi. On the left, the 45 bytes of walls, one square for each bit, a set bit in the room's wall colour; under it, the room as draw_room draws it.

The walls are 45 bytes, which is 360 bits, read from the top left a row at a time. Each bit stands for two cells. A set bit (a 1) draws the room's wall character, with the character whose code is 4 higher beside it. A clear bit (a 0) draws two blanks. That makes 18 rows of 40 cells, the whole play area. Then draw_room puts a wall top, $7E beside $7F, on every blank cell above the left half of a wall, in the room's own colour for wall tops. So every ledge gets a lit edge.

The four exit bytes name the rooms that the top, the bottom, the left and the right edge lead to. draw_room_objects turns every blank cell on an edge of the play area into an exit character. So a room's way out is wherever its walls leave a gap at the edge. An exit byte of 0 sends him to room 0. Most rooms lead to their neighbours on the five-by-five map, but four rooms have exits that lead off it (Discoveries).

10 · The EGY bar and the clock

Each room drains the EGY bar or fills it, at its own rate

The EGY bar, at the foot of the panel, holds 34 cells of four steps each. A room's record sets how many passes of the main loop each step takes, and whether the steps go down or up.

The bar drawn with the game's characters $6D (full) to $71 (empty), as energy_tick ($33AB) steps the cell in bar_pos. The rates and modes are the last two bytes of each room's record. The times take 20 passes of the main loop a second. That is about the speed of the loop in the rooms timed in plus4emu, 0.39 to 0.42 passes a frame.

A turn starts with the bar full. refill_energy fills it a step at a time, with a warbling tone, while GET READY shows. energy_tick counts the room's rate down once a pass. Each time the count runs out, the cell that bar_pos points at moves a step. In a room that drains, the cell steps from full towards empty, and then bar_pos moves left. When the leftmost cell empties, he dies. In a room that fills, the cell steps back towards full and bar_pos moves right, but it never fills the last cell (Discoveries).

The TIME clock counts up from 0.00, a step every 71 passes of the main loop, which is about every 3.5 seconds (clock_tick). The clock keeps running between rooms and between turns. When it reaches 4.00, after about 23 minutes of play, his lives are set to 1 and he dies, which ends his game. Each player in a game for two has a clock of his own.

11 · Controls

Both players use the joystick in port 2

The menu offers the joystick or the keyboard, for one player or for two. The game reads K and M as up and down and then ignores them.

On the menuPlayersControls
F1onethe joystick in port 2
F2onethe keys
F3two, taking turnsthe joystick in port 2
HELPtwo, taking turnsthe keys
In playJoystickKeyboard
Left, rightleft, rightZ, X
JumpfireSHIFT
Up, down (unused)up, downK, M
Pause, carry onP
Give up a lifeD

new_game ($31F5), read_input ($2543), player_pass and pause_check; all tried in plus4emu.

read_input builds one byte with a bit for each control. With the joystick, it reads port 2 through TED's keyboard latch, $FF08. The game never reads port 1, and in a game for two both players use port 2. With the keys, it reads the key code at $C6. The C16's ROM, the program built into the machine, leaves that code there each time the interrupt calls the ROM's keyboard scan. The code holds one key at a time, so the game cannot see Z and X held together. SHIFT has a flag of its own, at $0543, so Mr Puniverse can jump while he walks.

The code turns K and M into up and down bits, the same bits the joystick's up and down give, and the game never reads those bits. P pauses the game. PAUSED shows in red under the room, the sound goes quiet, and the game waits for P again (pause_check).

12 · Lives, turns and the end

The 25th vitamin ends the game

The game has one ending. The 25th vitamin plays the title tune, waits for a key, and then runs the death sequence with no lives left.

The vitamin's four pictures, characters $D7 to $DA, turned by the game's rotate_vitamin_chars ($2844) on the kit's 6502, once a pass at 20 passes a second, in room 0's vitamin colour.

Each player starts with eight lives, shown as little men in the panel. A death runs the death sequence (death). The room's background flashes through the colours while a rising noise plays, the EGY bar empties, and a life goes. In a game for two, the other player then takes a turn. Each turn starts in the room the player was in, at the place where he came into it, with the bar full. The clock and the vitamins carry on from where they were.

A vitamin taken is gone for good for that player. collect_vitamin sets his bit in the room's entry in vitamins_got, and the room is drawn without the vitamin the next time he comes in. The 25th vitamin sets his lives to 1, plays the tune and waits for a key, and the death sequence that follows finds no lives left. In a game for two the other player plays on. With both players out, GAME OVER shows and the title comes back.