return to ChibiAkumas.com Choose a Color Scheme: Dark
Print Mode


Learn Multi platform Z80 Assembly Programming... With Vampires!
Platform Specific Lessons
<- Back to the Main Contents & Basic Z80 Assembly Lessons

<- Back to Page 1 of the platform specific series


Lesson P11 - Tilemap graphics on the MSX2
The MSX2 VDP is a bitmap based system, however it's not memory mapped, which means we still have to access it with OUT commands...
It does,however, have the ability to give commands to the VDP, and the VDP will carry out those tasks for us without the Z80 doing any work! Effectively we're doing multi threading on the 8-bit!

In todays lesson, we'll look at some code that mimics the Tilemaps we've already looked at, and uses the HMMM copy command to copy 8x8 chunks of memory quickly on the VDP!

the MSX2 VDP uses various registers to control it's settings... we operate these via OUT calls...

It also has some status registers, which we will need to check to see if our jobs are finished, as it can only do one task at a time!

The table here is just a summary for quick reference.
If you want to know all the details, you should download the v9938 documentation here

Registers

The VDP uses registers numbered 0-46...
To Set a register, first send the value to put in the register  to the control port, then send the register number +128 to the control port.

EG lets set Reg 15 to 2...
    ld a,2
    out (&99),a
    ld a,15+128
    out (&99),a

There are some special registers
R#15 sets the Status register
R#17 sets the indirect register... this allows us to set a range of registers in one go via port &9B... this allows us to use a sequence of OUTI commands to set registers,  when we're giving the VDP jobs to do we usually need to set 32-46... so we set the indirect register to 32... then OUTI away!

Commands

We only need to use 3 commands today...
HMMV is a flood fill command, it clears the screen quickly and gets pixels to screen faster than any other command
HMMC is used to copy byte data from the normal Z80 memory to the VDP ram - we use this to create our tiles! - it's the slowest of our commands
HMMM this is our workhorse... once our tiles are in VRAM, it gets them on our screen.. it's a bit slower than HMMV, but much much faster than HMMC

We don't use it today, but and alternative to HMMM is LMMM - it does the same job, but can do transparency... its slower than HMMM, but is the best way to do sprites with transparent backgrounds.

Status Registers

We have to check CE to see if it's 0 - if it isn't the VDP is busy and we have to wait as it's doing another command, we need to select S#2

If it's busy the VDP can still do direct memory access while it's busy though, and ChibiAkumas uses this to draw the background gradient with the Z80 WHILE the VDP is blitting the mountain tiles, and flood filling the black areas of the background!

The default Interrupt handler relies on S#0 selected... this is because the MSX will fire interrupts indefinitely until S#0 is read...  therefore we MUST disable interrupts until S#0 is selected again, or write our own interrupt handler!
Commands
Here are the commands we'll be looking at today!
Name Command From To Units ByteCode Function
HMMC High Spd Move CPU  VRAM bytes %11110000 Fill Bytes from OUTI
HMMM High Spd Move VRAM VRAM bytes %11010000 Copy an area from Vram to Vram fast (blit)
HMMV High Spd Move VDP  VRAM bytes %11000000 Flood fill a square with a single byte

Registers
Here are the main registers we'll be looking at today for our commands!
Reg Meaning Bit7 Bit6 Bit5 Bit4 Bit3 Bit2 Bit1 Bit0
R#32 SX: X-coordinate to be transferred (LOW) SX7 SX6 SX5 SX4 SX3 SX2 SX1 SX0
R#33 SX: X-coordinate to be transferred (HIGH) 0 0 0 0 0 0 0 SX8
R#34 SY: Y-coordinate to be transferred (LOW) SY7 SY6 SY5 SY4 SY3 SY2 SY1 SY0
R#35 SY: Y-coordinate to be transferred (HIGH) 0 0 0 0 0 0 SY9 SY8
R#36 DX: X-coordinate to be transferred to (LOW) DX7 DX6 DX5 DX4 DX3 DX2 DX1 DX0
R#37 DX: X-coordinate to be transferred to (HIGH) 0 0 0 0 0 0 0 DX8
R#38 DY: Y-coordinate to be transferred to (LOW) DY7 DY6 DY5 DY4 DY3 DY2 DY1 DY0
R#39 DY: Y-coordinate to be transferred to (HIGH) 0 0 0 0 0 0 DY9 DY8
R#40 NX: num. of dots to be transferred in X direction (LOW) NX7 NX6 NX5 NX4 NX3 NX2 NX1 NX0
R#41 NX: num. of dots to be transferred in X direction (HIGH) 0 0 0 0 0 0 0 NX8
R#42 NY: num. of dots to be transferred in Y direction (LOW) NY7 NY6 NY5 NY4 NY3 NY2 NY1 NY0
R#43 NY: num. of dots to be transferred in Y direction (HIGH) 0 0 0 0 0 0 NY9 NY8
R#44 CLR: for transferring data to CPU (used by HMMC) C7 C6 C5 C4 C3 C2 C1 C0
R#45 MOV: Movement Direction (eg Left->Right or Right->Left) 0 - MXD - DIY DIX - -
R#46 CMR: send VDP command CMD CMD CMD CMD MSK MSK MSK MSK

Status Registers
Here are the Status Registers we need:             
Reg Meaning Bit7 Bit6 Bit5 Bit4 Bit3 Bit2 Bit1 Bit0
S#0 Status 0 - Needed by Firmware F 5S C 5SN 5SN 5SN 5SN 5SN
S#1 Status 1 FL LPS ID ID ID ID ID FH
S#2 Status 2 - Needed to check if VDP is ready TR VR HR BD 1 1 EO CE

Special Register Selection
We select our Status register, and the AutoIncrement Indirect port (&9B) with the registers below:
Reg Meaning Bit7 Bit6 Bit5 Bit4 Bit3 Bit2 Bit1 Bit0
R#15 indirect specification of S#n 0 0 0 0 S3 S2 S1 S0
R#17 indirect specification of R#n AII 0 R5 R4 R3 R2 R1 R0
             
             
The bad news is this is just the beginning... check out the MSX page, or get the V9938 manual to see the full details!

The good news is you can just use the code provided in these tutorials to do the work for you, and the V9990 version will work exactly the same for the V9K accelerator

MSX2 VDP commands
We need a few VDP commands to do todays work, Lets take a look!
When we call our commands today, we need to pass them the address of a set of definitions our functions will push these values into the registers.

Here is an example of one of the larger commands that uses many of the registers... some (Like HMMV / Fill) don't need SX and SY

and remember, if you use my functions, you can support the V9990 with the same byte definitions as this - where needed, the V9K code will convert them to the format needed for the V9990!
Before we can do any commands, we need to check if the VDP is busy... to do this we need to select status register 2! (S#2)... the selected status register is defined by register R#15

We do this by sending the number of the Status Register (2 for S#2) we want to select to the control port (&99)
Now we need to tell the VDP the register we want to change (15 for R#15)... we have to add 128 to the number of the register we want to change

Now we need to check if CE  (bit 0) is 0 - if it's 1 we need to wait some more... we read in the Status register from the control port (&99)

We use RRA to pop off Bit 0, if it's zero  (No Carry) we don't need to wait any more, if it's 1, we need to keep waiting...

Remember, We must disable interrupts while we're doing this!  - once we're done, we need to set the Selected Status Register to 0

We're going to write the contents of our definitions to the registers (Shown in Tile_MyHMMM above), we want to fill them all one after another, and we can do this with the Indirect register

We're going to use the Indirect register port (&9B)... to select the register we want to start filling, we set R#17 to 32 (SY)... each OUTI will then fill registers R#32, R#33, R#34 and so on...

in this example we want to do 15 OUTI's... which will make our code a bit messy, so we specify them as bytecode (&ED,&A3) is the bytecode for an OUTI command!


The code to the right does HMMM and HMMC, but HMMV is basically the same, just starting from R#36, and with fewer OUTI commands - because we don't need SX and SY... the important thing is the command that goes into R#46 ...  and that's in our Tile_HMMM definition as the last byte... It DOES need to be the last byte - as the VDP starts processing
We can even use the indirect register as a fixed register pointer...
If we set R#17 to 32+128 then all our OUT commands to port &9B will go to register R#32 ...
Adding 128 to the register number we write to R#17 does the job of telling the VDP not to increment!

Using the VDP commands to get stuff done!
First let's do the most basic thing, and Clear the Screen!

Before we can do anything else, we need to make sure the VDP isn't busy doing other things, we'll use VDP_FirmwareSafeWait... this function will loop until the VDP is ready, and will keep the status register correct so the interrupt handler doesn't have problems.

The best command  for this is HMMV... it fills a square area with a single byte...  we'll define a set of HMMV bytes in 'CLS_MyHMMV
We'll set DX and DY to 0 - to select the top left corner of the screen... then we'll set NX to 256 and NY to 192  (the size of the screen)

We want to fill the screen with color 0, so we set the Byte (R#44) to &00

We can then use VDP_HMMV which will push our CLS_MyHMMV definitions into the registers and start the job!
Once again, we're showing the text and Chibiko bitmap... note we've not set any color information in this example..

If you want to see colored tiles, please take a look at my GrimeZ80 project, which uses this tile code, and Tile color palettes!
We can create the bitmap data using my 'AkuSprite' editor (included in Sources.7z)... For this example you want to use 'Save RAW VdpTile Bitmap'

This will export the bitmap as a 16 color bitmap, split into simulated 'tiles'
Our tiles are held in our program memory (the normal 64k of the Z80)... but we need to get them to the VDP VRAM to use them later.
We do this with the HMMC command....

The VDP has a virtual screen of 256x1024... the area (0,0)-(255,191) is show on screen, if we used a double buffer, it would use the area (0,256)-(256,447)

For simplicity, we'll just use the area (0,512)-(256,1023) for out tiles!

with 256 pixels on each line, that means we can store 32 tiles per line, so we need to take the first 5 bits of the tile number, and multiply them by 8 to get the X position to store out tile.

The Y position will be in the remaining 3 bits, if we multiply them by 8, and add 512, that will be our Y co-ordinate for the tile...

Of course each tile is 8x8... and since we're only using a single byte in this calculation, we're only supporting up to 256 tiles.

We call HMMC, which will set up the VDP to receive more data ... annoyingly we need to give it the first byte in MyHMMCByte.

once we've called call VDP_HMMC_Generated ... we can send the rest of our data, we just do this by OUTing the data to data port (&9B)




There is an alternative version of this in in today code...
it's called 'CopyBWTilesToVdp' which uses  2 bit data from our font to define tiles...
it's basically the same but has conversions to change the 1bpp data into 4bpp... please see the sources.7z if you want to see it.

When we want to get a tile to the screen, we use 'CopyTileToScreen'

This function will set the tile at X,Y position in D,E to tile number HL... This is achieved by copying an 8x8 area from our defined tiles (in the off screen area at Ypos 512+) to the screen.

The X,Y position is in 8 pixel units, so the screen is defined as (0,0)-(32,24)... in this way we can use the MSX2 or V9K as a tilemap in the same way as the MSX1


We effectively reverse the calculations we did before, and work out the tile position in the definitions, and copy it to the screen.
When we want to draw the Chibiko bitmap, we will use the 'FillAreaWithTiles function...  this takes an XY position in BC, a Width and Height in HL, and a start tile number in DE...

This function is almost identical to the previous ones, we just use CopyTileToScreen to do our work instead of an OUT command with the MSX1/SMS or a LD (HL) command on the Gameboy...

if we call the routine with BC set to &0303 ,  HL set to &0606 and DE set to 128... the following tiles will be set on screen

0 1 2 3 4 5 6 7 8 9
1
2             128 129 130 131 132 133      
3 134 135 136 137 138 139
4 140 141 142 143 144 145
5 146 147 148 149 150 151
6 152 153 154 155 156 157
7 158 159 160 161 162 163
8


Lesson P12 - Joypad reading on Master System,GameGear, Gameboy and Gameboy Color
Reading in the controller on a game system like the Gameboy, Mastersystem or Game-gear is much easier than full-keyboard systems like the CPC and MSX...

We're going to read in all the buttons, and store them in registers, in the identical format as we did on the other systems, allowing us to use the GB/SMS/SGG in the same way, as the other systems!... We'll set bits 0-7 to U-D-L-R-F1-F2-F3-Pause in H for player 1, and L for player 2 (where available) ... and bit that is 1 is a button that is not pressed.. 0 means a button is held down

Joypad reading on the Master System and Game Gear
 
Although they're usually the same, Joypad reading is one of the times the SMS and GG are quite different!

The SMS has two joypads... and the GG has an extra button (Start!) on a different port! so the commands we run are quite different depending on the machine we're using.

The GameGear and Master System use 3 ports in total, but there are differences!

The Gamegear has an extra button! 'Start' accessible from bit 7 of Port &00... The Gamegear does not have any player 2 controls

The Mastersystem has no start button, but it does have a second player paddle!

note:Button TH has no use on the normal SMS gamepads, so is not used in these examples.
Port Bit Purpose
&DC 7 Player 2 - Down
6 Player 2 - Up
5 Player 1 - Fire 2 (TR)
4 Player 1 - Fire 1 (TL)
3 Player 1 - Right
2 Player 1 - Left
1 Player 1 - Down
0 Player 1 - Up
Port Bit Purpose
&DD 7 Player 2 - Extra (TH)
6 Player 1 - Extra (TH)
5 Cartridge slot
4 Reset
3 Player 2 - Fire 2 (TR)
2 Player 2 - Fire 1 (TL)
1 Player 2 - Right
0 Player 2 - Left
Port Bit Purpose
&00 7 Gamegear Start
6 unused
5 unused
4 unused
3 unused
2 unused
1 unused
0 unused

We'll read Player 2 first, We need 2 of the bits from &DC, and 4 bits from &DD... we also need to swap the order of the &DC bits...
As we have no Fire 3 or Select, we need to set the top 2 bits to 1 with an OR command

Player 2 is saved in L... 

Next we handle player 1, we read in 6 bits from &DC, and add in the topmost bit of port &00 (Start) on the GameGear ... there is no start button on the Master System

Player 1 is saved in H...

This returns HL

Bit in HL 7 6 5 4 3 2 1    0   
H - Player 1 Start Fire3 Fire2 Fire1 Right Left Down Up
L  -Player 2 Pause Fire3 Fire2 Fire1 Right Left Down Up
GG onlySMS only  Unused

Joypad reading on the Gameboy and Gameboy Color

As the Gameboy does not use OUT commands, the joypad controls are mapped to memory address &FF00...

Strangely it's split into two halves, and we have to write to this address first, to select which half we want to read!
Joypad reading is all done with memory address &FF00

Only Bits 0-3 in this address contain the state of the buttons, we first need to select which half of the joystick we want to read.

If we write %11101111 to &FF00 (Bit 4 is zero) ... we will select the direction controls... and the next read from &FF00 will get Down, Up, Left, Right in bits 0-3

If we write %11011111 to &FF00 (Bit 5 is zero) ... we will select the button controls... and the next read from &FF00 will get Start, Select, Button B, Button A in bits 0-3
Address Bit Purpose
&FF00 7 Unused
6 Unused
5 Read Buttons
4 Read Directions
3 Down Start
2 Up Select
1 Left Button B
0 Right       Button A
We start by reading in the direction controls.  So we set bit 4 to 0, and write to &FF00

You'll notice LR and UD are in the wrong order... we use L as a temporary register to swap these around.

We then set bit 5 to 0, and write to &FF00 so we can read in the buttons

We then shift in the button bits into H.

Finally we write 255 to L, as there is no player 2 controller.


Bit in HL 7 6 5 4 3 2 1    0   
H - Player 1 Start Select Fire B Fire A Right Left Down Up
L - Player 2 Pause Fire3 Fire2 Fire1 Right Left Down Up


Lesson P13 - Palette definitions on the Amstrad CPC and CPC+

We've looked at how to draw bitmaps and text on all our systems, but we need to make sure the system color palette matches what we need for our bitmap.

The CPC+ has better colors than the CPC... and it's the PLUS that we'll use as the palette template for all our other systems!

Using our palette definitions
We're going to use 1 nibble per color channel, in the format -GRB ... this is the native palette for the CPC+ ... but we're going to convert it so we can use this one definition on all our systems... making supporting so many systems much easier.

for example &000F is bright blue, and &0800 is 'Middle Green"

Some systems have 4 colors (0-3), others 16 (4-15)... and we can also use color 16 to set the border color where appropriate

We're going to use the command 'SetPalette' on all systems,  for example, to set Color 1 to G=16 R=8 B=4... we can do:

      LD A,1
      LD HL,&0F84
      CALL SetPalette

Using the CPC plus Palette
By default The CPC+ features are disabled, leaving us with only a regular CPC... to use the PLUS features, we need to send a series of 17 bytes to the CRTC at port &BCxx

once the CPC+ features are on, a special bank of 16k 'ASIC' memory will become available, which we can write to between &4000-&7FFF in the same way as our normal memory.

To Turn it on we use:
    ld bc,&7fb8
    out (c),c
To turn it off we use:
    ld bc,&7fa0
    out (c),c

NOTE: When using the ASIC memory, We should make sure that we're using the 128k banks are disabled, or it can cause problems with some 128k ram upgrades. Mode C1/C3 are OK to use as they  page in at &C000
The bytes we have to send look strange, but don't think changing them will have any effect - if you change them they will not work, and the bytes have no particular meaning...
When the Plus Came out, AMS lied and claimed only cartridge games could use the plus features, so they we're intentionally random so no-one could figure out how to use CPC+ features on non-cartridge games! Fortunately we know them now, so we can use PLUS on our tape and disk games!
We have to page in the plus registers so we can write our palette definition into the PLUS asic...

Each color definition is 2 bytes, and there are definitions for the main 16 colors, the border, and CPC+ Hardware Sprites

From To Purpose
&6400 &641F Colors 0-15
&6420 &6421 Border
&6422 &643F Sprite colors 1-15 (0 is transparent)

We send two bytes in the format &-GRB ... where the - nibble is unused, G is Green, R is Red and B is blue
We're using one Nibble per color, so we have a total possible palette of 4096 colors available to us.

We're actually going to use this color definition format for all our systems - some systems support fewer colors, and some support more, but one nibble per color is a good balance and gives us plenty of colors for smooth fading, but doesn't go overboard!
Setting the palette on the regular CPC
When we're going to use the palette definition on the regular CPC, we're only going to use the top 2 bits of the palette definition, so our RGB definitions should use F,8,0 .. anything in between will round DOWN!

Setting the color involves two commands

     ld bc,&7fpp - where pp is the palette number
     out (c),c
     out (c),a      - where a is the hardware color

We keep the hardware colors in a lookup table, which we use to covert the PLUS format colors for the Classic CPC!
We need to read in the colors nibbles, and bit shift them into the appropriate positions, we then use as an offset for the Lookup table we just saw

To get the correct line, we need to multiply the green component by 9, and the Red component by 3, then  we just add the blue (times 1!)

This gives us the correct position for the closest hardware color the CPC can do...

We'll use similar code later again on other 'fixed palette' systems that can't do RGB... and you can easily convert this code to use these palette definitions on  systems that aren't even covered by these tutorials.

We're setting the colors at the hardware level, so we're using the firmware color codes,  but if you prefer, you could use the system color numbers (0-26) and use Firmware calls to set the palette if you prefer...
These tutorials tend not to use the firmware, but if it's up to you, so use whatever you prefer... but it's not possible to set PLUS colors via a firmware call!


Lesson P14 - Palette definitions on the Enterprise and Sam Coupe

Now Lets take a look at the Enterprise, and see how to use our CPC+ style color definitions to define the palette on the Enterprise.

 

The Enterprise Palette definitions
The Enterprise uses 8 bits for each color definition

Red and Green have 3 bits defining them, and Blue has only 2... which is quite unbalanced!

Also rather oddly the rightmost bits are actually the highest in significance, which is kind of backwards!

according to the Enterprise documentation the Calculation for the resulting RGB value is as follows:
 total RED = ( r2 * 4 + r1 * 2 + r0 ) / 7 
total GREEN = ( g2 * 4 + g1 * 2 + g0 ) / 7
total BLUE = ( b1 * 2 + b0 ) / 3
These definitions are stored in memory in the LPT block, we just need to change that memory to set the new palette values

Palette Definition as it appears in the LPT:
Bit 7 Bit 6 Bit 5 Bit 4 Bit 3 Bit 2 Bit 1 Bit 0
G0 R0 B0 G1 R1 B1 G2 R2

Effective Palette:
Color Bit 7 Bit 6 Bit 5 Bit 4 Bit 3 Bit 2 Bit 1 Bit 0
Red R2 R1 R0 - - - - -
Green G2 G1 G0 - - - - -
Blue B1 B0 - - - - - -
And to make things even stranger, we can only specify exact colors for the first 8 colors... the second 8 use a 5 bit 'Bias' ,and pads out the lowest bits with the "Color Number" (0-7 for colors 8-15)

This is controlled by port &80 on the system, so we just OUT our new fixbias byte to that port to change the color

This effectively means we are forced to have colors 8-15 as a "rainbow", but we can tint it a bit, or make it brighter or darker.
FIXBIAS on port &80:
Bit 7 Bit 6 Bit 5 Bit 4 Bit 3 Bit 2 Bit 1 Bit 0
- - - G1 R1 B1 G2 R2

Effective palette definition of colors 8-15:
Bit 7 Bit 6 Bit 5 Bit 4 Bit 3 Bit 2 Bit 1 Bit 0
C2 C0 C1 G1 R1 B1 G2 R2

Magenta = from palette number bits (values 0-7)
Green = from FIXBIAS
Converting CPC+ color format to the Enterprise format
To convert our -GRB format palette which uses one nibble per color, we just split out the colors, then shift 3 of the Red and Green bits, and just 2 of the Blue bits into a new byte, then save that into the LPT memory for that palette entry

Of course, we can only do this for the first 8 colors!



When we want to set the top 8 colors in 16 color mode, we can OUT 5 bits to the FIXBIAS register at port &80

Really, we'll want them to be a brighter, darker or tinted copy of the first colors.

The Enterprise has superior graphics and color to the CPC, so porting Mode 1 games is really easy! when it comes to Mode 0, however, the Enterprise is more limited, because we can't set the 2nd 8 colors as easily on the Enterprise as the CPC,
Of course if we're designing a new game then we would plan for this, and design our palette accordingly!
The Sam Coupe Palette definitions
The Sam Coupe uses a 128 color palette, the RGB part of a color is defined by 2 bits per channel, and an extra 1 bit is used as a 'Bright' bit, which increases the brightness by half a bit

Bit 7 Bit 6 Bit 5 Bit 4 Bit 3 Bit 2 Bit 1 Bit 0
- G1 R1 B1 Bright G0 R0 B0

The effective color palette is defined as:
Color Bit 7 Bit 6 Bit 5 Bit 4 Bit 3 Bit 2 Bit 1 Bit 0
Red R2 R1 Bright - - - - -
Green G2 G1 Bright - - - - -
Blue B1 B0 Bright - - - - -

Each of the 16 colors is controlled by a different port on the SAM, we OUT our new color byte to that port...

If we look at the port numbers in decimal, they don't make much sense, but if we look at them as 16 bit HEX, we'll see that the High byte is the color number, and the low byte is &F8
Color Port (Dec) Port (Hex)
0 248 &00F8
1 504 &01F8
2 760 &02F8
3 1016 &03F8
4 1272 &04F8
5 1528 &05F8
6 1784 &06F8
7 2040 &07F8
8 2296 &08F8
9 2552 &09F8
10 2808 &0AF8
11 3064 &0BF8
12 3320 &0CF8
13 3576 &0DF8
14 3832 &0EF8
15 4088 &0FF8
converting our -RGB one nibble per channel format  to the SAM format is relatively easy, all we do is pop off 2 bits from each channel into our destination byte into the correct locations...

the only tricky bit is what to do with the 'bright bit'... we could just ignore it, but that would mean bright white would be slightly dark....

In this example, what I've done is set the 'bright bit' when the 2nd bit of each color nibble is set for all three channels...


Dealing with the "BRIGHT" bit is tricky because we need to convert our RGB value some how, Here we've set it when all 3 channels have bit 2 as on
This may make the colors seem darker than they should be in some cases but some kind of trade of was needed.
Of course you may prefer to set the bit if any of the colors have this bit set, or if 2 of 3 are, but you'll have to change the code!





Lesson P15 - Palette definitions on the MSX2 and V9990

The MSX2 and V9990 are fairly similar, and Easy!

 The MSX2 uses 3 bits per channel, and the V9990 uses 5 bits per channel, so we'll need to do a bit of conversion of our 1 nibble per pixel palette


The MSX2 V9938
When we call our routine, we have the color number in A, and the -GRB palette definition in HL

To set a palette on the MSX2, we need to set register 16 (The indirect palette register) to the number of the palette we want to select.

Then we need to send two bytes in the correct format to the palette port on port &9A

We need to make some changes to our GRB data... we only need 3 of the 4 bits, and we need to shift the remaining bits one to the right, but apart from that, the data is in the correct format!
We need to send two consecutive bytes to the Palette port:
  7     6     5     4     3     2     1     0  
- R2 R1 R0 - B2 B1 B0
- - - - - G2 G1 G0



The V9K
We need to use Register 14 to tell the VDP what color we want to change... we have to shift this two bits to the left.

When we do this, we also tell it what color channel (RGB) we're going to send.. .we're going to leave this as 00 - as it auto increments, and we're going to send all 3 channels.
Color Selection (Register 14)

 7    6    5    4    3    2    1    0  
N N N N N N C C

C=Channel
     0=red 1=Green 2=blue
     (Auto increments)
N=Palette Number

We need to split out our color channels and send them one by one, also, although our definition uses one nibble (4 bits) per channel, the V9K needs 5 bits... so we need to shift one bit to the left

We just send the three bytes to the V9K Palette port (&61) and the color will be set!

As we send each byte, the Auto increment means the channel changes, and once we've set a whole 'Color' it automatically moves to the next color, so its easy to set all the colors in one go!
 7    6    5    4    3    2    1    0  
- - - R4 R3 R2 R1 R0
- - - G4 G3 G2 G1 G0
- - - B4 B3 B2 B1 B0


The MSX2 9938 needs 3 bits per channel... the V9K needs 5 per channel...
We're using 4 bits per channel (like the CPC+)... so it's a good middle ground between the other systems... don't you think?

During early development of ChibiAkumas, the MSX2 3 bit color definitions were used for the V9K,and the CPC+ used separate definitions... but this meant the V9K fades were noticeably inferior to the CPC+...
The developer switched the code, so the V9K AND MSX2 use the CPC+ palette definitions, and that's why we're using it for ALL the systems in these tutorials!



Lesson P16 - Palette definitions on the Sega Master System and Game Gear
Although the GG and SMS are identical in most cases, color depth is one time there is a big difference between the two...  

Despite being the portable system with the smaller screen, the GG uses 4 bits per color channel, whereas the SMS uses only 2! this means there are a few differences between how we set the palette on these systems.

The GameGear and Mastersystem use 2x 16 color palettes...

Background Tiles can use either of the 2 palettes... sprites always use the second one.

The Mastersystem uses just 2 bits per color channel... meaning 1 byte per definition
The GameGear uses 1 nibble per channel.. meaning 2 bytes per definition... this means the memory locations are different on the SMS and GG

To set one of the bytes that make up the palette definition, we write to an address &C0xx - where xx is the byte of the palette definition we want to change...

Note, this isn't literally part of the 16k of VRAM - the &C0 is more like a command to tell the VDP to change a color.
SMS Palette Definition:
 7  6  5  4  3  2  1  0
- - B1 B0 G1 G0 R1 R0

GG Palette Definition:

 F  E  D  C  B  A  9  8    7  6  5  4  3  2  1  0
- - - - B3 B2 B1 B0 G3 G2 G1 G0 R3 R2 R1 R0


Palettes on the Master System

Our SetPallette command will take a color number in the Accumulator, and a -GRB color definition in HL

To start, we need to select the memory address - because the Game Gear has 2 bytes per definition, we need to double A on the GG

We use the PrepareVram command to send the address to the VDP - which sends the address to vdpControl (&BF) - which tells the VDP what to do with the data it gets next

On the SMS we need the top two bits of each nibble in our definition...

we just merge them all together, and send them to the vdpData port (&BE) to set the color definition!

Palettes on the Game Gear
The GG can take all 4 bits of our definition!

However our definition is in the format -GRB ... and the GG wants it in -BGR format...

We need to shift some of the bits of our definition around to get them in the format the GG wants... then we just send the two bytes it needs to the VDP data port!

The superior palette of the GG is one of the reasons it was possible (via a converter) to play SMS games on a GameGear, but not the other way round!

even stranger... the 16 bit sega genesis uses only 3 bits per channel... giving the GG 4096 colors compared to the Genesis' 512  ... so arguably the GG has better color than the Genesis!



Lesson P17 - Palette definitions on the Gameboy and Gameboy Color

We're using our 1 nibble (4 bits)  per channel -GRB color definitions... actually the Gameboy Color has 5 bits per channel!

Unfortunately the black and white Gameboy cannot do any real sort of color - it only has 4 shades 0,1,2 and 3 (0=light, because all pixels are off)... I did try to write a converter, but it doesn:t really work well. As the GBC tiles have 4 colors, and there are 4 shades, we'll map each color to a shade - either dark->light or vice versa!
 

The Classic Gameboy uses 4 colors for tiles... and there are 4 possible colors that can be used.

These colors are defined by 3 memory addresses...
&FF47 defines the tilemap colors
&FF48 defines sprite colors 0
&FF49 defines sprite colors 1

Just write a byte in the correct format to these addresses to set each color option

You can see we have 2 choices for sprite data... also you should note that color 0 (C0) in sprites is transparent.
All 3 use the same color definitions, 2 bits in the byte define colors C0-C3... and those bytes can be 0-3 (00,01,10 or 11)


&FF47,&FF48,&FF49:
7 6 5 4 3 2 1 0
C3 C2 C1 C0
C0 is transparent for sprites

Possible Colors
  0     %00  
 1    %01  
 2    %10 
 3    %11 

The Gameboy Color has 8 palettes of 4 colors available for tiles and sprites... each colors is defined by 15 bits... 5 per channel

First we have to select the palette and color we want to change, by writing an appropriate byte to &FF68... Note we also have to set Bit 0 correctly... Our palette definition contains two bytes..a High and Low pair... if we're writing the Low byte bit 7 should be 0... if we're writing the High byte, bit 7 should be 1

Once we've selected the palette destination, we then write the new palette byte to &FF69

We then need to write another byte to &FF68

Rather than doing two writes to &FF68, it's easier to use the Autoinc Bit (bit 7)... this will allow us to write the Low bit, and then the High bit to &FF69... without a second write to &FF68

&FF68 - Select Palette
 7  6  5  4  3  2  1  0
A - P P P C C B
A: Autoincrement address
P: Palette (0-7)
C: Color (0-3)
B: Byte being written to &FF69 (0=L ,1=H)

&FF69 - Set Byte of color
L / H Byte depending of &FF68
F E D C B A 9 8 7 6 5 4 3 2 1 0
- B B B B B G G G G G R R R R R

Colors on the Gameboy Classic

Because we have such simple color options we'll just define two palette choices,

Dark will set the background to black,  the 3rd color to white, and the other two in between...
Light will do the opposite!

In both cases "Object Palette 1" (the alternate sprite palette) will be the opposite colors of the main palette 0
Remember Color 0 of the sprites will be transparent, so you may want to customize this depending on your color needs!

GrimeZ80 didn't use sprites - it only used tiles, but we'll look at how to use hardware sprites later in the series!

Colors on the Gameboy Color

We're providing a color number in A, and a 1 nibble per channel definition in -GRB format...

We need to convert it to 5 bit BGR format - so we need to shift the color bits around

We also need to double A because the color number starts in Bit 1 of the data we we write to the palette selector &FF68.

We need to write our Palette selector to &FF68., with Bit 0 =0 ... we also set Bit 7 =1 to turn on autoincrement... because we're going to write the low byte first.
We've used the special GBZ80 command LDI, so HL will now have changed to &FF69

We've told the graphics hardware we're about to write the LOW byte of the new palette to &FF69... and we'll do that first.
Because we've turned on Autoincrement, we can just write the second HIGH byte to &FF69 straight away without changing the selected palette address... we could even set more colors if we wanted.
When we call this function with A=0... HL will be used to define color 0 in palette 0... A=3 will define color 3 in Palette 0... A=4 will define color 0 in Palette 1... this will continue for all 8 (0-7) palettes for a total of 32 colors.



Lesson P18 - Making Sound with the AY on the Amstrad CPC, MSX,ZX Spectrum.... and NeoGeo + Atari ST!!
It's time to move on! We've looked as graphics, and input, but now we need to complete our game... and what game will be complete with Sound Effects!
To start we'll look at the AY sound chip... the exact same chip exists on the CPC,MSX, speccy and even the Atari ST! (no that's not a z80 system!) ...

Also, The NeoGeo also has a YM2610 which is backwards compatible with the AY!... and although it's main cpu is a 68000... its sound chip is a Z80!

Because the chip is the same, we just need to change the code that addresses the registers and gets the data to them in the correct way for the hardware!

Introducing ChibiSound!

In these tutorials we're going to create an 'amazing' new sound API to rival Directsound!!!... well at least the functionality won't break like Directsound 3D did!

Well, no it won't... what it will do is take a byte value from 0-255, and make a sound of selectable pitch, with noise or half volume in a similar way on all our systems!
This was created for Grime Z80, and allows common sound code to give similar effects on all the systems!

All we do is load the accumulator with a value, and call ChibiSound!

Of course, this won't be enough to make music (Hint: try Arkostracker!) but it will give us some simple SFX, and make it easy to compare doing simple tasks on our various systems!
Accumulator Value Effect
&00 Sound Off
&01-&3F Quiet tone
&40-&7F Loud tone
&80-&BF Quiet Noise 
&C0-&FF Loud Noise
in all cases, smaller numbers are higher pitch, so &10 is higher than &11

Terminology!
Music sometimes uses unusual terms for simple things... here's some things you will see in sound chip documentation, and what they actually mean!
Amplitude Loudness
Envelope How the sound changes over time (on the AY - Volume over time)
Mixer Turn channels on or off
Noise Distorted sounds - used for explosions, drum and symbol sounds
Tone Pitch of the sound

AY Registers
The AY Sound chip is controlled by 14 internal registers that define the sounds it makes... these are built into the chip itself, and we usually access it by 2 ports controlled by OUT commands... one selects the register, the other takes the new value for the register.

The procedure is simple, first we send the register number to the register select port

Then we send the new value to the data port.

before we look at examples of how to make sounds, lets look at the way we access these ports on the systems we'll be looking at, because it's a little different on each!
Reg Meaning Bit Meaning Details
0 Tone Pitch L - Channel A LLLLLLLL Lower value = Higher pitch
1 Tone Pitch H - Channel A ----HHHH Lower value = Higher pitch
2 Tone Pitch L - Channel B LLLLLLLL Lower value = Higher pitch
3 Tone Pitch H - Channel B ----HHHH Lower value = Higher pitch
4 Tone Pitch L - Channel C LLLLLLLL Lower value = Higher pitch
5 Tone Pitch H - Channel C ----HHHH Lower value = Higher pitch
6 Noise Generator ---NNNNN Higher = Faster noise
7 Mixer  --NNNTTT   N=Noise T=Tone
(Channel --CBACBA 1=mute 0=normal)
8 Amplitude - Channel A ---EVVVV E=Envelope (1=Enabled) VVVV=Volume
9 Amplitude - Channel B ---EVVVV E=Envelope (1=Enabled) VVVV=Volume
10 Amplitude - Channel C ---EVVVV E=Envelope (1=Enabled) VVVV=Volume
11 Envelope L (Volume over time)  LLLLLLLL Lower=Faster Envelope
12 Envelope H (Volume over time)  HHHHHHHH Lower=Faster Envelope
13 Envelope Selection ----EEEH E=Envelope number E (See PDF) 
H=Hold

The MSX
On the MSX things are easy... the Data port is &A1, and the register select port is &A0
The ZX Spectrum
On the Spectrum the procedure is the same, but the ports are different... and of course the Speccy uses 16 bit port numbers... the Data port is &BFFD, and the Register Select is &FFFD
The Amstrad CPC
The Amstrad CPC likes to make us suffer!... the AY chip is connected to the "PPI"... so if we want to get to the AY, we need to work for it

We need to do a complex sequence of commands to get the PPI to send it's data to the AY, including "Inactive" commands, which are needed to "Reset" the chip between commands...
The PPI has 4 ports in total, but we only need two of them to control the AY

Port A9 A8 Descr. RW Dir Purpose
&F4xx 0 0 Port A Read/Write In/Out PSG (Sound/Keyboard/Joystick)
&F5xx 0 1 Port B Read/Write In Vsync/Jumpers/PrinterBusy/CasIn/Exp
&F6xx 1 0 Port C Read/Write Out KeybRow/CasOut/PSG
&F7xx 1 1 Control Write Only Out Control
&F6C0 will select a register  (Registers are settings in the PPI/AY)
&F680 selects to Write to the register
&F600 sets the PPI to 'Inactive'... we need to do this at certain times to get it to work
&F4xx will send data to the PPI
The NeoGeo
On the NeoGeo data port is number 5... and the register select port is port 4
The Z80 cpu has its own program code, and data is sent to it using a shared 'Port'... we can only send one byte at a time.... which is good, because Chibisound only uses one byte...

Unfortunately commands 0-31 are reserved for system use... so we'll split the Chibisound byte into two nibbles to send it from the 68000 to the Z80
The Atari ST
Why's the Atari in here?... well I'm planning a 68000 tutorial series next year, and don't want to make the same video twice!
The AY ports are memory mapped on the 68000, so we just write a Byte to $FF8800 to set the register number... and $FF8802 to set the new value for that register!

The NeoGeo sound chip is backwards compatible with the AY... and cannot be accessed by the 68000, so we have to get the Z80 CPU to do the sound work...

The Z80 has its own program code contained in a ROM and separate memory - and we have to pass data between the Z80 and 68000 with a single byte RW port!
Writes to Z80 port &0C can be read from 68000 port $320000 , and writes to 68000 port $32000 can be read from port &00

Getting the AY to work!
If we want to do anything, we need to turn a channel on!
The AY has 3 sound channels, A, B and C... rather strangely 0 turns a channel on, and 1 mutes it
We turn them on using the Mixer on Reg 7...
Bits 0-2 are the normal sounds for channels A-C
Bits 3-5 turn noise on for channels A-C

in this example, we'll turn on channel A
now we need to select a Pitch...
pitches are defined by 12 bits... so each channel has two registers to define a pitch... channel A uses 0 and 1

In this example, we'll set a fairly middle tone for channel A.
The last thing we need to do is set a volume for channel A... this is done with Register 8

A volume can be from 0-16... where 16 is loudest... the sound will now play!

We can also turn on an envelope for the channel if we want a more complex sound... 
Envelopes are outside the scope of this tutorial, because the author is too stupid to understand them properly!
please see the AY sound chip PDF if you think you're brainy enough!
Essentially an envelope is selected with Register 13... a speed for the envelope with 11 and 12...  and it applies for any channel with the envelope bit set in that channel's volume register!

Making the a noise!
if we turned on noise for a channel using the Mixer a noise element will be added to the channel...

In this example we've set bit 3 to Zero... turning on noise for channel A
All the channels share a common noise setting... to set the noise "Pitch" we use register 6... if takes a 5 bit value, which defines the noise... smaller numbers are higher pitch!

Making Chibisound
We're now in a position to code Chibisound!

Our Accumulator byte format is NVPPPPP

where P= pitch , V = Loud volume, and N is the noise bit

We split up PPPPP, and use som,e of the bits to set registers 0 and 1 (Pitch of Channel A)

If N is set, we turn the noise channel on using the mixer (0=on) on register 7, then we set a noise frequency using register 6

if N is not set, we only turn on channel A using the mixer on register 7

we also need to set the volume of the channel, otherwise it won't make a sound using register 8 (volume for channel A)  %00001111 is the loudest.


if A is Zero... we mute all the channels using the mixer - this will stop all sounds


Lesson P19 - Sound on the Elan Enterprise

The Enterprise is pretty similar to the AY... in the fact it has 3 tone channels, and one noise channel, however it does not have any envelopes...  Fortunately that won't affect our ChibiSound, but it could make creating music tricky!

The Enterprise does beat the AY for volume options... we can define LR volume independently for each of the 4 channels (3 tone + noise)

Enterprise Sound Ports
The Enterprise sound parameters are all controlled by individual ports, so we can simply OUT our values to those ports to select the sound we want to make.

Port Purpose Bits Bit Meaning
&A0 Channel 0 Tone L LLLLLLLL L=Tone Low Byte… Lower values=Higher tone
&A1 Channel 0 Tone H RPCCHHHH H=Tone High Bits / polynomial Counter / Ring Modulator (CH2) / highPass Filter (CH1)
&A2 Channel 1 Tone L LLLL L=Tone Low Byte… Lower values=Higher tone
&A3 Channel 1 Tone H RPCCHHHH H=Tone High Bits / polynomial Counter / Ring Modulator (CHN) / highPass Filter (CH2)
&A4 Channel 2 Tone L LLLL L=Tone Low Byte… Lower values=Higher tone
&A5 Channel 2 Tone H RPCCHHHH H=Tone High Bits / polynomial Counter / Ring Modulator (CH0) / highPass Filter (CHN)
&A6 Noise Channel frequency RHLBCCNN Noise (0=31khz 1-3=Channel 0-2 link) / polynomial Counter/ swap Bits 7 & 17 of pc / Lowpass /Highpass / Ring modulator
&A7 Sync & Interrupt rate -IIDDSSS Interrupts (0=1khz,1=50hz,2=tone0,3=tone1) D=da ladder (speccy 48k emu) / Sync for tone 0,1,2 (1=hold 0=run)
&A8 Tone Channel 0 L Amplitude --VVVVVV D/A ladder (tape port, Speaker)
&A9 Tone Channel 1 L Amplitude --VVVVVV
&AA Tone Channel 2 L Amplitude --VVVVVV
&AB Noise Channel L Amplitude --VVVVVV
&AC Tone Channel 0 R Amplitude --VVVVVV D/A ladder (Speaker R)
&AD Tone Channel 1 RAmplitude --VVVVVV
&AE Tone Channel 2 R Amplitude --VVVVVV
&AF Noise Channel R Amplitude --VVVVVV

Tone Channels

The Enterprise has 3 tone channels, each one has 12 bits defining the pitch of the tone (Lower numbers=higher pitch)

All the channels are stereo, and the Volume is defined by two 6 bit registers.

Despite not having envelopes, we do have some other options... Polynomial Counter Distortion values 0-3 for Off,4bit ,5bit or 7bit... a HighPass filter, and a Ring Modulator

The HighPass Filter links to another Channel.... Channel 0 links to Channel 1.... Channel 1 links to Channel 2.... Channel 2 links to the Noise Channel

The Ring Modulator also  links to another Channel.... Channel 0 links to Channel 2.... Channel 1 links to the Noise Channel .... Channel 2 links to Channel 0
Sound Channel Ring HighPass
CH0 CH2 CH1
CH1 NOISE CH2
CH2 CH0 NOISE

Try different options for the Polynomial Counter! they'll make the sound different... of course, if you want to use the Ring Modulator or High Pass, you'll have to use an extra channel to link to.

Noise Channels


The Noise Channel's frequency is defined by 2 bits...  0 will give 31khz noise... 1-3 will link the noise frequency to that of channel 0-2... note Channel 0-2 can be muted, and the noise will work!

We also have some extra options! we have the RingPass and HighPass filter, and also a LowPass Filter...

Finally we can use the Bit swap option (bit 5) this will swap bit 7 and 17 in the noise generation... the result is the noise sounds more "Synthetic"

Special Options

They aren't very useful, but there are some extra options on the Enterprise for sound...

We can change the rate interrupts occur, and even have them linked to the tone channels
Also we can make the Enterprise emulate a spectrum Beeper speaker (noooo!)
Finally, we can 'Pause' the tone channels, by setting them to 1 in the first 3 bits of this option

Here comes ChibiSound!

Our Accumulator byte format is NVPPPPP

where P= pitch , V = Loud volume, and N is the noise bit

The Enterprise is pretty easy for us to get ChibiSound Working on!

If the sound is off (0) we just mute the Noise and Tone 0  by setting both channels volume bits to 0


For everything else we split up our 6 bit tone definition, and use the bits to define the two tone registers of Channel 0

We use the 1 Volume bit to define a low or high volume...

If Noise is not enabled, we're done

If noise IS enabled, we mute the Tone channel (though we do need it to some extent!)

We then set the Noise volume, and enable Noise linked to channel 0  by writing 1 to port &A6
This will make the noise change pitch as we require!

Don't forget! The Enterprise has a pretty fine AY emulator!  Coded by Geco based on IstvanV's original it's part of SymAMP, and was used to get ArkosPlayer working on the Enterprise, for the EP128 port of ChibiAkumas!
Of course, ArkosPlayer is 10000x better than ChibiSound, but ChibiSound will be created for every system covered in these tutorials, not just Z80 systems!






ZX Spectrum 48k - the lowly Beeper Sound Chip:
The 48k spectrum has no AY... it only has a "Beeper"

The "Beeper" sound chip is incredibly crude... it is controlled by bit 5 of the port &FE... by turning it on and off we can make simple sounds...

See the example to the right... by changing the pause (caused by BC) we can change the pitch of the sound... 3000 will be a relatively low pitch... 500 will be higher...

Some clever programs even manage to "Fake" multiple sound channels!

The big disadvantage to all this is that the CPU will be busy during the whole time, so the Beeper chip isn't very helpful, and we'll want to use the AY sound chip on the 128k systems... but on the 48k machines, it's all we've got!
 
As well as the 'Beeper' Port &FExx also controls the border... by flipping a bit of the border color when we toggle the beep, we can get some fun border effects when sound plays...

So while the sound may be kinda bad, at least we get some snazzy stuff to look at!

Back to Page 1

Visit www.ChibiAkumas.com to get my games and their source code! | Support me on patreon