Tutorial.

This is a breakdown of the homebrew game Racer. Please refer to the game's discussion thread [here]. Download the final game, including source and assets, [here].

Content:
Introduction
General technical notes
Overview of the program flow - full game
Step 0 - The assets
Step 1 - A vertical scrolling road
Step 2 - A player controlled car
Step 3 - Introducing Ash - the first enemy car
Step 4 - Introducing Mae - the second enemy car
Conclusion

I have included five major steps. After the last step, only scoring, death-handling (including super simple collision detection) and title screen are missing compared to the full version. The ambition is to take out some of the complexity as one person is exposed to another person's source code of a full game. At least I wish there had been something like this to go to after doing Maxim's Hello World tutorial.

Upon request I will happily make more detailed explanations if required. I can also add the scoring and/or collision detection as separate steps.

Not tested on real hw. RAM init, etc. Bare bones.

Software used
The setup from hello world
- wla-dx, with Maxim's syntax highlight
- Context notepad with execute keys 
- Wla-dx compile on F9
- Meka on F10
- Emulicious on F11
- Paint Shop Pro 7 (PSP7) for graphics
- Deflemask for tracking/music
- PSGLib, incl. vgm2psg for converting Deflemask vgm output to psg format, to be played in the code using psglib.
- Photoshop for cover art creation

Documents for reference
- The official SEGA software reference guide.
- Charles Mac Donalds VDP documentation
- Maxim's overview of SMS colors
- Wla dx documentation
- z80 opcode overview
- Jonathan Cauldwell for style guide, and struture


Technical notes.
For reference. This was discussed on the forum, but I have included it here for reference.

The 6 phases of the raster line / display.

; ---------------------------------------------------------------------
NTSC, 256x192 ------------- 
 Lines     Description 

 000-191  Active display (192 lines) 
               Update VRAM very carefully! Don't touch graphics 
               currently being drawn by/on the raster line. Beware 
               of bandwidth (?) limitations. Work with line interrupts 
               and/or the counter, i.e. in a,($7E). 'Race the beam'. 

 192-215  Bottom border (24 lines) 
               Start of VBlank interrupt (also called frame interrupt) 
               Write sprites, tiles and tilemaps 

 216-218  Bottom blanking (3 lines) 
               Write everything, including colors (CRAM) 

 219-221  Vertical sync (3 lines) 
                 Write everything. Beam moves back. 

 222-234  Top blanking (13 lines) 
                 Write everything 

 235-261  Top border (27 lines) 
               Write sprites, tiles and tilemaps 
;--------------------------------------------------------------------------

About writing to CRAM and the snow effect:
 the snow effect should disappear if I time my CRAM writes to occur between scanlines 216 and 235? 


General structure for loops (5 phases in a loop):

1. Wait for vblank (frame interrupt).

2. Write to vram from buffers
- sat buffer
- scroll reg. buffer.
(finishes at raster line 218)
Note, vram writes must be finished by raster line 0 (last line is 261).
cram updates should happen between 216 and 235. (NTSC here).
In title screen I have very little colorful snow near the bottom. I think it is because I write too early to vram (before line 215, actully it is at line 194, right when vblank starts.). I have not addressed in a proper way, because of my whish to keep the source simple. And I did not want too much extra stuff going into the title screen (essentials!). So I made an ugly hack with four nops being djnz looped 200 times (see source). Then the cram writing starts at line 219, and yes, it removes the snow.

3. Resolve issues rising from current state of game objects
- collision
- timers

4. Update game objects (set new states/positions, etc.)
(scoring is not tied to this).
- get and respond to user input
- enemy AI, etc.

5. Update vram buffers


Overview of program flow.

1. Game initialization
    - init ram and vram
    - init variables
    - load game assets

2. Prepare title screen
    - load title screen assets

3. Title screen loop
    - play music
    - flash hiscore
    - wait for button 1 press
         ----> go to prepare main loop

4. Prepare main loop
    - load main loop assets
    - init variables
    - update sat buffer

5. 'Ready loop'
    - wait for keypress
         ----> go forward to main loop
    - wait for timer
         ----> go back to prepare title screen

6. Main loop
    - write to vram
    - compare score to hiscore
    - test for score = 9999
         ----> go to 'player dies'
    - detect collision
         ----> go to 'player dies'
    - read and respond to controller input
    - update enemy positions
    - add to score
    - update the sat and scroll buffers

7. Player dies (prepare death loop)
    - make crash noise and trash player car

8. Death loop
    - update enemy positions (now moving up)
    - wait for timer
         ----> go back to prepare main loop



Step 0. Creating assets.
PSP7 for tile editing. Palette control. Set grid to 8x8.
BMP2Tile for making include files out of the bmp files.
Deflemask to compose the title music.
vgm2psg tool included with PSGLib to create psg out of vgm.

Step 1. Vertical scrolling road.
Utilize the wrap-around effect.
Point to note: Ram and vram cleaning. The ghost in the shell.

Step 2. Add a player controlled car.
Note:
Now we have the sat buffer, and the subroutines to update it.
Update means taking game object x,y, i.e. the player's car,
and then adjust the 4x4 sprites' vpos and hpos. [include the SAT buffer map image]

Step 3. Add one enemy - meet Ash.
Enemy Ash is the first enemy.
Point to note: The enemy AI (?) routine is introduced here. Enemy ash uses the same functions as the player to make sprites in the buffer from y,x positions.

Step 4. Add another enemy - meet Mae.
Mae is the second enemy. She appears after a while.
Point to note: Mae is made of transparent tiles at start. Then
she cannot collide with the player, but she is moving and in
the game right from the start. Remember. Ghost in the shell.
Adding Mae is pretty simple, because she shares a lot of the routines with Ash.