Learn Multi platform ARM Assembly
Programming... For the Future! Platform Specific Lessons
In this Tutorial series we go over the
hardware of the platforms we're covering - each lesson will cover a
different platform, and function of the system
These tutorials assume you have a reasonable
understanding of ARM, and are looking to build up your own program.
Lesson
P1 - Bitmap graphics and Palette definitions on Risc OS
Lets start with RISC-OS... we'll learn how to enable graphics
mode, and draw bitmap graphics to the screen.
Let's make a start!
Turning on the screen:
To turn on the screen we'll use VDU commands...
These are 'control characters' we print to the 'screen' (They
never actually appear)... We Use SWI 0 to do this.
We write 22 (VDU 22) to the screen to select mode, we
want Mode 9 - this is 320x256 with 16 colors - so we write 9
next
The screen will have a blinking cursor, we use SWI 0x36 to
turn it off
Next we need to get the address of the screen, we use SWI 0x31 to
get the details of the screen,
we pass a bank of parameter numbers that we want, and get back the
address of the screen in 'ScreenStart'
There
are a wide range of screen modes available, but we'll only be
using 9!... the 256 color mode is a little weird as it uses 64
base colors with different brightnesses, so you'll probably find
this 16 color mode easier.
We're going to set the palette... RiscOS uses 1 nibble per color
(4 bit per channel)
When we want to set a palette entry we use VDU 19... we write
control code 19 to the screen with SWI 0.
We write the palette entry number...
Next we set the flashing mode (16 = turns it off)...
The top nibble of the next3 writes is the Red, Green then Blue
part
Calculating Screen Addresses and
plotting pixels:
When we want to draw to the screen, we'll first want to take our
X,Y screen position (in R1,R2) and convert them to a memory
address (returned in R10)
As the screen is 320 pixels wide, and 2 pixels are in a single
byte, The formula for calculating an address is
ScreenAddr = ScreenBase + (Ypos*160) + Xpos
We got 'ScreenAddr' when we turned the screen on.
When we want to move down a line we add 160 to the screen address.
When we want to draw to the screen, we calculate the screen
address, and copy each byte of the line to the screen,
We then move down the screen, and repeat until the sprite is done.
The sprite data will be included from a file... As the screen is
4bpp in linear format, each nibble in the file will define the
color of a pixel
Here is the result!
Want to create a valid file? you can use my AkuSprite
Editor, it's free and open source, and included in the
sources file.
Lesson
P2 - Bitmap graphics and Palette definitions on GameBoy
Advance (16 bit - 32768 colors)
The GBA was a huge upgrade over the Gameboy Color... Allowing
multiple layers and 32 bit color, we have an immense amount of
power for our ARM programming.
Lets look at the GBA and learn how to use 15 bit mode! (32768
colors)
Turning on the screen:
OK, First we need to set up our screen... we're going to use
'Screen mode 3'... this gives a 16pp bitmap screen (2 bytes per
pixel) at ram address 0x06000000
This screen mode only works on Background Layer 2
To turn it on we need to set graphics register x04000000 - bits
0-2 are the Screen mode, bit 10 turns on Background 2
Once the screen is on, we'll fill the screen!
The screen starts at 0x06000000 and has 256x192 pixels... where
each pixel takes 2 bytes in the format 'ABBBBBGGGGGRRRRR'... with
1 Alpha bit and 5 Blue, Green and Red bits
This screen
mode uses 2 bytes (a Half-Word) per pixel... but whatever screen
mode you use, you need to make sure you write to the screen in
WORDS - Writing individual bytes to the screen - even in 256
color mode will not work!
Calculating Screen Addresses and
plotting pixels:
When we want to draw to the screen we need to calculate the ram
address..
The screen is 240x192 pixels - each pixel is 2 bytes, and our
screen base is 0x06800000
Therefore our screen formula is:
Address = 0x06000000 + (Ypos * 240 * 2)+ Xpos * 2
When we want to move down the screen, we just add 480 to the
current screen address.
When we want to draw to the screen, we calculate the screen
address, and copy each byte of the line to the screen,
We then move down the screen, and repeat until the sprite is done.
We include the bitmap from a file
Here is the result!
Want to create a valid file? you can use my AkuSprite
Editor, it's free and open source, and included in the
sources file.
Lesson
P3 - Bitmap graphics and Palette definitions on GameBoy
Advance (8 bit - 256 colors)
The GBA was a huge upgrade over the Gameboy Color... Allowing
multiple layers and 32 bit color, we have an immense amount of
power for our ARM programming.
Lets look at the GBA and learn how to use 8 bit mode! (256
colors)
Turning on the screen:
OK, First we need to set up our screen... we're going to use
'Screen mode 4'... this gives a 256 color bitmap screen (1 bytes
per pixel) at ram address 0x06000000
This screen mode only works on Background Layer 2
To turn it on we need to set graphics register x04000000 - bits
0-2 are the Screen mode, bit 10 turns on Background 2
Once the screen is on, we'll fill the screen!
The screen starts at 0x06000000 and has 256x192 pixels... where
each pixel takes 1 byte and selects a color from the palette.
We're going to define a palette.
Each color is defined by a 15 bit value in the format where each
pixel takes 2 bytes in the format '-BBBBBGGGGGRRRRR'... with 5
Blue, Green and Red bits - the top bit is unused
We store these words to addresses 0x05000000+ - each word is a
palette entry
Even though 1
pixel takes a single byte we have to write in WORDS -
otherwise both pixels will be set at the same time!
You'll have to program accordingly to work around this
limitation!
Setting Palette Entries:
These tutorials use a 1 nibble per color standard palette
format in the layout 0x-GRB (one nibble is unused)
As mentioned, palette entries take two bytes ,and are kept at
address 0x05000000+ - each word is a palette entry
To calculate the destination address, we multiply our palette
number (in R0) by two and add to 0x05000000
Each color is defined by a 15 bit value in the format where each
pixel takes 2 bytes in the format
'0b-BBBBBGGGGGRRRRR'... with 5 Blue, Green and Red bits - the
top bit is unused
These tutorials use bit format 0b----GGGGRRRRBBBB ... so we need
to mask the bits of each channel and move them to the correct
position
Finally we write the word to the calculated address.
Calculating Screen Addresses and
plotting pixels:
When we want to draw to the screen we need to calculate the
ram address..
The screen is 240x160 pixels - each pixel is 1 byte, and our
screen base is 0x06800000
Therefore our screen formula is:
Address = 0x06000000 + (Ypos * 240) + Xpos
When we want to move down the screen, we just add 240 to the
current screen address.
When we want to draw to the screen, we calculate the screen
address, and copy each byte of the line to the screen,
We then move down the screen, and repeat until the sprite is
done.
We include the bitmap from a file
Here is the result!
Want to create a valid file? you can use my AkuSprite
Editor, it's free and open source, and included in the
sources file.
Lesson
P4 - Bitmap graphics and Palette definitions on the Nintendo
DS (16 bit - 32768 colors)
It's time to look at the Nintendo DS!... lets learn how we can set
up two 32k screens for 16 bit color on the NDS!
Enabling graphics hardware
The NDS has two graphics engines - Engine A is the most powerful
and can do 3D - Engine B is around the same spec as a GBA
Each Engine can only drive one screen - so we need both for dual
screens (That's why most games have simple graphics on one screen)
Before we can use either, we need to turn them on with reg
0x4000304
We'll allocate Engine A to the top screen, and
Engine B to the bottom
Though A is more powerful, we're only doing simple graphics so it
won't make much difference.
Turning on the first screen via
Engine A
Engine A can map an area of normal memory straight onto the
screen as a 16 bit 256x192 image (VRAM Display)
We do this by setting bits 16 and 17 of 0x4000240 to 'Mode 2'
The last command enabled the screen, but we must also turn on
it's ram...
We do this with 0x4000240
Now Words written to 0x06800000+ will set pixels of the top screen
in format -BBBBBGGGGGRRRRR
If you only want one screen - we're done!
If you want two, it's tricky... Engine B can't do RAM displays
as easily, but we can do it in a more tricky way!... read on!
Turning on the second screen via
Engine B
We're going to set up a 16 bit display on the other screen to -
but engine B cannot do VRAM display... however it can do Affine
mode - this is where an area of memory is used as a
rotatable/scalable bitmap.
Engine B uses ports 0x04001000+
We enable Affine on layer BG2 with port 0x04001000
We need to configure Affine mode with port 0x0400100C
We're setting the bitmap to 256x256 (to fill the width of the
screen) and 32k (16 bit) color
The screen is on, but it won't work without some default
rotate/scale settings.
Here are the basic ones to map a normal 1:1 screen
But wait! We still need to turn the memory on!
We do this with 0x4000242, The bottom screen is finally enabled!
Now Words written to 0x06200000+ will set pixels of the top screen
in format ABBBBBGGGGGRRRRR (A= Alpha - 1=visible)
Clearing the screens
We can write to both our screens in almost the same way - the
only difference is the bottom screen uses Alpha in bit 15
(1=Visible)... the top screen doesn't use it
Screen
Address
Pixel format
(Alpha Blue Green Red)
Top
0x06800000
-BBBBBGGGGGRRRRR
Bottom
0x06200000
ABBBBBGGGGGRRRRR
What a lot of effort!
The screen is finally, on, clear and ready for us to draw some
data!
Calculating Screen Addresses and
plotting pixels:
When we want to draw to the screen we need to calculate the
ram address..
The screen is 256x192 pixels - each pixel is 2 bytes,
Lines 0-191 have a screen base of 0x06800000
Lines 192-383 have a screen base of 0x06200000
Therefore our screen formula is:
Address = Screenbase +(Ypos * 256*2) + Xpos * 2
When we want to move down the screen, we just add 480 to the
current screen address.
When we want to draw to the screen, we calculate the screen
address, and copy each byte of the line to the screen,
We then move down the screen, and repeat until the sprite is
done.
We include the bitmap from a file
Here is the result!
Want to create a valid file? you can use my AkuSprite
Editor, it's free and open source, and included in the
sources file.
Lesson
P5 - Joypad & Pen on the GBA / NDS ... Key reading on
Risc OS
Lets learn how to read the keypad on the Gameboy
Advance... the Keypad and pen on the NDS, and the keyboard on
Risc OS... here we go!
Key reading on Risc OS
We can read from the keyboard with an OS Call with SWI 6
(OSByte)
Function 129 (0x81) allows us to read from the keyboard...
If we load R1,R2 with a 2 byte delay (R1=L byte R2=H byte) we
can get an ASCII key back from the keyboard in R1... the delay
can be up to 0x7FFF
Here we had space pressed (Ascii 32 - 0x20)
If we want to test a keypress we need to set R2=255... we need
to set R1 to the keypress EORed with 255 (255^keypress)
The Keypresses are not ASCII - they are keycodes.... R1 returns
255 if the key is pressed, 0 if it is not.
In this example we're testing Up, Down, Left and Right and
showing a Letter for each direction
Here UP and RIGHT were held down, so we show U and R
Keycodes
The RiscOS keycodes can be seen below:
Esc:112
F1: 113
F2: 114
F3: 115
F4: 20
F5: 116
F6: 117
F7: 22
F8: 118
F9: 119
F10:30
F11: 28
F12: 29
Print:
32
ScrlLk:31
Brk:44
`:
45
1: 48
2: 49
3: 17
4: 18
5: 19
6: 24
7: 36
8: 21
9: 38
0: 39
-: 23
=: 93
Pnd: 46
Bksp:
47
Ins: 61
Home:62
PgUp:63
NmLk:
77
/: 74
*: 91
#: 90
Tab:
96
Q: 16
W: 33
E: 34
R: 51
T: 35
Y: 68
U: 53
I: 37
O: 54
P: 55
[: 56
]: 88
\: 120
Del: 89
Copy:105
PgDown:78
7: 27
8: 42
9: 43
-: 59
Ctrl:
14
A: 65
S: 81
D: 50
F: 67
G: 83
H: 84
J: 69
K: 70
L: 86
;: 72
': 79
Retn:
73
4: 122
5: 123
6: 26
+: 58
Shift:
03
Z: 97
X: 66
C: 82
V: 99
B: 100
N: 85
M: 101
,: 102
.: 103
/: 104
Shift:06
U:57
1: 107
2: 124
3:108
Enter:
60
Caps:
64
Alt: 25
Spc: 98
Alt:28
Ctrl:
17
L:25
D:41
R:121
0:106
.:76
Joypad Reading on the Gameboy
Advance
Reading in the GBA buttons is super easy!... just read in from
0x4000130... you'll get a word back with all the keys in the
format %------LRDULRSsBA
Um... that's it!
A bit is 1 if the key isn't pressed.. 0 if the key is pressed
Here we pressed Down and A
The 'GBA' key reading we see here is the same
on the NDS... Which is nice and easy!
Unfortunately, reading the other keys is not!... for some
reason the NDS only keys are a right pain!
Joypad Reading on the Nintendo DS
Reading in the GBA buttons of the NDS is the same as the GBA...
just read in from 0x4000130... you'll get a word back with all the
keys in the format %------LRDULRSsBA
A bit is 1 if the key isn't pressed.. 0 if the key is pressed
Here we pressed Down and A (0x7BE)
We usually use the ARM9 for our program, but this CPU cannot read
the extra buttons... we have to write a separate program for the
ARM7 (specified by the ROM header) and write the read data to the
shared memory 0x02F00000+
To read in the extra keys we read from ARM7 address 0x4000136...
this reads X and Y , 'PenDown', a special 'Debug' function and the
'lid closed' sensor in the format %--------HP--D-YX
on the ARM7:
Here we pressed the X key (0x7E)
PEN Reading on the Nintendo DS
Accessing the PEN pos can only be done from the ARM7 - and its not
entirely easy!
the X and Y pos use 12 bits each, but
we can only load them 8 bits at a time, via a serial bus....
0x40001C0 - SPICNT - SPI Bus
Control/Status Register
0x40001C2 - SPIDATA - SPI Bus Data/Strobe Register
Here the X and Y position... We pressed the pen in the bottom
right of the touchpad.
Lesson
P6 - Sound on the Gameboy Advance
The GBA retains the same basic functions of the Gameboy Color...
Here we'll learn how to make simple sounds for our games.
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 musicbut it will give us
some simple SFX, and make it easy to compare doing simple tasks on
our various systems!
R0r 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
Sound Ports
Port
Name
Description
Bits
Details
4000060h
SOUND1CNT_L
Channel 1
Sweep register
---------TTTDSSS
S=sweep
shift D=direction T=Time
4000062h
SOUND1CNT_H
Channel 1
Duty/Length/Envelope (NR11
VVVVDSSSWWLLLLLL
L=length
W=wave pattern duty S=envelope Step D= env direction V=Volume
4000064h
SOUND1CNT_X
Channel 1
Frequency/Control
IL---FFFFFFFFFFF
I=Init
sound L=no loop F=Frequency
4000068h
SOUND2CNT_L
Channel 2
Duty/Length/Envelope (NR21
VVVVDSSSWWLLLLLL
L=length
W=wave pattern duty S=envelope Step D= env direction V=Volume
400006Ch
SOUND2CNT_H
Channel 2
Frequency/Control
IL---FFFFFFFFFFF
I=Init
sound L=no loop F=Frequency
4000070h
SOUND3CNT_L
Channel 3
Stop/Wave RAM select (NR30)
-------PBD-----
D=Dimension
B=Bank P=Play
4000072h
SOUND3CNT_H
Channel 3
Length/Volume
FVV-----LLLLLLLL
L=sound
Length V=volume F=Force
4000074h
SOUND3CNT_X
Channel 3
Frequency/Control
IL---FFFFFFFFFFF
I=Init
sound L=no loop F=Frequency
4000078h
SOUND4CNT_L
Channel 4
Length/Envelope
VVVVDSSS--LLLLLL
L=length
S=envelope Step D= env direction V=Volume
First we need to turn on the sound hardware, we do this with bit 7
of 0x4000084
Depending if we're making a tone or a noise, we need to branch
We'll use Channel 1 for tones, and Channel 4 for noise
If we're making a tone we use
channel 1 for the sound.
We need to select a volume for channel 1 with the top 4 bits of
0x4000062
We need to select a frequency for channel 1 with the 12 bits of
0x4000064 - we also need to set the top bit to 1 to make the sounds
start
We need to set the channel to on, by setting the Left+Right bits for
channel 1 to 1 (on) in the top 8 bits of 0x4000080h (the mixer)...
we also set the bits for the 'master volume' L+R to 1 in bits 0-7...
the max volume is 7
If we're making a noise we use channel 4
(the noise channel)
We need to select a volume for channel 4 with the top 4 bits of
0x4000078
The noise frequency is set with bits 4-7 of %400007Ch
Once again we need to set the channel to on, by setting the
Left+Right bits for channel 4 to 1 (on) in the top 8 bits of
0x4000080h (the mixer)... we also set the bits for the 'master
volume' L+R to 1 in bits 0-7... the max volume is 7
If we want to silence sound, we turn off all the channels of the
mixer with 0x4000080h
Of course, the GBA can
do more than just simple beeps, it's capable of playing digital
sound samples.
But for now we'll make do with some basic beeps.
Lesson
P7 - Sound on the Nintendo DS
The Nintendo DS is far more advanced, and uses sound samples!...
we'll change the playback frequency to alter pitch... lets make some
noise!
V1_ChibiSoundDriver.asm
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 but it will give us
some simple SFX, and make it easy to compare doing simple tasks on
our various systems!
R0 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
Sound Registers (ARM7)
Like pen control, the main ARM9 CPU cannot access the sound hardware...
so we'll program a routine on the ARM7 and use a couple of bytes of shared
ram to pass commands to the Arm7 routine
There are 16 sound channels, with addresses like 40004x0h
where x is a channel from 0-F
Address
Bytes
Name
Description
Bits
Notes
4000304h
2
POWCNT2
Sound/Wifi Power Control Register (R/W)
------WS
S=Sound on W=Wifi on
40004x0h
4
SOUNDxCNT
Sound Channel
X Control Register (R/W)
SFFRRWWW-PPPPPPPH-----DD-VVVVVVV
S=Start
F=Format R=Repeat W=Wave duty P=Panning H=Hold D=volume Div
V=Volume
40004x4h
4
SOUNDxSAD
Sound Channel
X Data Source Register (W)
-----AAAAAAAAAAAAAAAAAAAAAAAA00
A=Address of
sample
40004x8h
2
SOUNDxTMR
Sound Channel
X Timer Register (W)
FFFFFFFFFFFFFFFF
F=Frequency
40004xAh
2
SOUNDxPNT
Sound Channel
X Loopstart Register (W)
LLLLLLLLLLLLLLLL
L=Loop Start
40004xCh
4
SOUNDxLEN
Sound Channel
X Length Register (W)
LLLLLLLLLLLLLLLL
L=Length
4000500h
2
SOUNDCNT
Sound Control
Register (R/W)
M-31RRLL-VVVVVVV
M=Master on
31=output 31 to mixer RRLL=output from V=master Volume
4000504h
2
SOUNDBIAS
Sound Bias
Register (R/W)
-------BBBBBBBBBB
B=Sound Bias
4000508h
1
SNDCAP0CNT
Sound Capture
0 Control Register (R/W)
S---FEsC
S=Start
F=Format R=Repeat s=source C=Control
4000509h
1
SNDCAP1CNT
Sound Capture
1 Control Register (R/W)
S---FEsC
4000510h
4
SNDCAP0DAD
Sound Capture
0 Destination Address (R/W)
-----AAAAAAAAAAAAAAAAAAAAAAAA00
A=Address of
Capture
4000514h
2
SNDCAP0LEN
Sound Capture
0 Length (W)
LLLLLLLLLLLLLLLL
L=Length
4000518h
4
SNDCAP1DAD
Sound Capture
1 Destination Address (R/W)
-----AAAAAAAAAAAAAAAAAAAAAAAA00
400051Ch
2
SNDCAP1LEN
Sound Capture
1 Length (W)
LLLLLLLLLLLLLLLL
L=Length
Writing ChibiSound
We're using 2 bytes of shared RAM to pass commands to the ARM7
The first byte is the 'chibisound' byte in the format %TVPPPPPP
where T=tone (Tone/Noise) V=Volume (Low/High) P=Pitch
The second is the 'Action' byte - if it's zero, the ARM7 will do
nothing, if it's nonzero, the ARM7 will process the first byte, then
set the second back to zero
The ChibiSound Driver on the ARM7
We're going to need 2 sound samples.
One is a simple Tone (A square wave)... the other is a random noise
sample.
First we check the second 'action' byte
If it's zero, we have nothing to do, so we skip processing
If it's one, we have commands!
Next we check the ChibiSound byte, if it's zero, we're going to mute sound we do this by setting the master
volume to zero with the sound control register 0x4000500
if we're going to play a sound, we first make sure the sound
hardware is enabled by writing #1 to
0x4000304
Whatever sound we make, we'll use channel 0 for our sounds.
Next we need to decide whether to use our Tone sample or Noise sample
- this depends on the top bit of the ChibiSound command... we
do this with 0x4000404
Note: The sound sample must be aligned to a 32 bit boundary (4 byte)
Next we need to set the length of the
sample (in 32 bit words.... so Bytes /4)
Both our samples are 128 bytes - so 32 words.... we do this with
sound reg 0x400040C
We want the whole sample to loop (not just
part) so we set the 'Loop from' to #0 in 0x400040A
Ok, next we need to set the frequency (pitch)
of the playback, we do this with reg 0x4000408h
Next we need to set the channel Volume ,
set the Repeat (1=InfLoop),Panning
(64=center) and Format (0=8Bit
Signed)
We do all this with 4000400h
The sound will now play!
Remember... these commands only work from
the ARM7 - you need to define a separate program for that CPU to
run in the header...
Don't know how to do that? No problem! check out the files in the
sources download!
What? You can't be bothered? Sheesh! Well I guess you didn't
really want to know then!
Lesson
P8 - 16 color Tilemap on the Gameboy Advance and Nintendo DS!
We've looked at bitmap screens, but lets go more simple(?)... Lets
take a look at the Tilemap, we'll set up a 64x32 tilemap - big
enough to scroll the screen.
First we'll learn how to do this on the GBA - because the NDS
graphics hardware is very similar, we'll convert it to work on the
NDS too!
GBA_Tilemap.asm
Setting up the tilemap
First we need to enable the screen layer, and set screen mode 0
(16 color Tilemap) we do this with port 4000000h
(This command is slightly different on the NDS)
Next we need to configure the tilemap... We're going to set the
base for the tile patterns at address 6004000h...
We're also going to set the tilemap size to 64x32... The visible
screen is 32x24, but this isn't enough for the screen to scroll.
The
64x32 tilemap is actually made up of 2x 32x32 tilemaps!
The Left hand half of the tilemap is at 06000000h The Right hand half of the tilemap is at
06000800h
Setting up the tilemap
We're going to define a function LDIR16 to transfer data in 16 bit
chunks.
This will Transfer R3 bytes from [R1] to [R2]
Working in Halfwords rather than bytes works best on the GBA as some
VRAM functions will malfunction if we write bytes (We could also use
Words)
First we'll set up our palette - each palette uses 16 colors... we
write halfwords to 05000000h to define each color in the format
0b-BBBBBGGGGGRRRRR (5 bits per channel)
Next we're going to define our tile patterns (the bitmap data of
the tiles)
Each tile is 8x8 and uses 4 bits per pixel... They are in 'Linear'
format NOT bitplanes, so each nibble of a byte defines the color.
because of the settings we sent to 4000008h, Tile pattern 0 is at
address 06004000h
You can export Tile bitmap data in the correct format with my
AkuSprite Editor.
We're going to transfer the tile numbers we're going to show into
the two 32x32 tilemaps
The Left hand side half is at 6000000h
The Right hand side half is at 6000800h
Each tile is a single word in the format 0bPPPPVHTTTTTTTTTT, where
P=Palette HV=HV flip T=Tilenum
We can scroll the tilemap with two registers
4000010h will scroll horizontally
4000012h will scroll vertically
Here is the tilemap scrolling!
The
DS version is almost the same, we just need to do a bit more
initialization to get the system set up.
Using the Tilemap on the Nintendo DS
First we need to turn on the hardware, this is the same as our
bitmap example
Next we need to configure the tilemap with 4000000h, but this
register has changed slightly with the NDS
We also need to turn on VRAM for our tilemap, we do this with
4000240h
The Tilemap will now scroll on the NDS too!
Lesson
P9 - Hardware Sprites on the Gameboy Advance and Nintendo DS!
We got a Tilemap working last time, but this time lets extend it
with some simple sprites.
Sprites on the GBA/NDS are made up of 8x8 tiles (16 color ones are
in the same format as the tilemap)... Lets make them work!
1D Sprites and 2D Sprites
Sprite data for sprites larger than 8x8 can be organized on one of
two formats.
In 1D mode, Sprite patterns are organized in a linear format (this
is the format this example uses)
If we were to show a crosshair from sprite data it would use tiles
1,2,3,4
In 2D mode, Sprite patterns are organized according to a 32,32 grid.
If we were to show a crosshair from sprite data it would use tiles
1,2,33,34
1D Mode
2D Mode
Enabling Hardware Sprites
The Hardware sprites don't use the same color palettes as the
Tilemap,
Hardware Sprite palettes are defined by addresses 5000200h+
Next We need to define our sprite patterns (We're using the same
data as the tilemap.
On the GBA we need to transfer these to address 6010000h
On the NDS we need to transfer these to address 6400000h... BUT we
first need to turn on this VRAM with port 4000241h
We need to turn on the screen, and enable the sprites (selecting
1D tiles)
This is done with port 4000000h, but the settings are slightly
different
We're going to create some
simple (unrotated) sprites, The GBA and NDS are capable of
more, but it's tricky, and it's outside of the scope of what
we'll try to do here.
Enabling Hardware Sprites
Hardware sprites are defined by 3 words... there are 128 in total.
Sprites are defined with 6 bytes from 7000000h Onwards... there are two
bytes after each sprite definition which are used by rotation settings
Sprite 0 is defined by 7000000h+,Sprite 1 is defined by 7000008h+,Sprite
1 is defined by 7000010h+ and so on.
The basic size of a Square sprite can be 8x8, 16x16, 32x32 or 64x64,
defined by bits 15-16 of the second word.
However a sprite can be double width, or double height to make a rectangle
defined by bits 15-16 of the first word.
Sprites can be 16 color or 256 color - the pattern data for both can be
mixed.
For the convenience of setting sprites, we'll define a function to
calculate the memory address and transfer register values into the
VRAM addresses.
This "SetSprite" function will set sprite number R0 to attribs
R1,R2,R3
Lets create a 16x16 16 color sprite. This sprite will use tile 6+
(6,7,8,9)
The sprite is a crosshair icon
Lets create a 16x8 rectangular sprite, we'll use Tile 1,2.
This will use 2 of the tiles from the tilemap
Lets create a 16x16 16 color sprite. This sprite will use tile 10+
Each 256 color tile takes 2x 16 color ones.
The sprite is also a crosshair icon (exported as 8bpp)
Here are the Sprite patterns we're importing.
Here is the sample running on the GBA
Here is the sample running on the NDS
You can export files in the correct format with my AkuSprite
Editor (Included in the sources.7z)
Lesson
P10 - NativeSprite
on the Gameboy Advance and Nintendo DS!
Lets look at 'NativeSprite' on the GBA/NDS. It allows us to use
hardware sprites in a multiplatform way!
The hardware sprites on the NDS and GBA are basically the same, so
we'll do both in one source file!
SrcGBA/
V1_NativeSprite.asm
Sprites
On the GBA In Tilemap
mode, Sprite patterns are defined by by VRAM addresses 0x06010000+
On the GBA In
Bitmap mode, Sprite patterns are defined by by VRAM
addresses 0x06014000+, Due to the size of the bitmap screen ram, only tile
numbers 512+ are usable
On the NDS Sprite
patterns are defined by by VRAM addresses 0x06400000+
Sprites use the 256 colors in the OBJ palette
from address 0x05000200+
Sprites are made up of 8x8 patterns with
either 16 colors (4bpp) or 256 colors (8bpp) defined by the color bit in
the first parameter.
Sprite XY pos 0,0 is the top left corner of
the screen.
T=Tile Number
C=Color palette (16 color mode) P=Priority
7000006h
Unused by sprite
What Is NativeSprite?
Nativesprite allows us to use platform specific sprite
capabilities, to form 'objects' (grids of sprites which can be
used in the same way on all systems.
This allows us to write a multiplatform program, but gain the
benefits of the hardware capabilities.
Nativesprite splits the job of drawing sprites into three parts.
Part 1 is
multiplatform, it is a list of NativeSprite objects with X,Y
co-ordinates (in logical units - Pairs of Pixels)
Our game can change the co-ordinates and sprite object pointers to
change in-game graphics in a multiplatform way.
With ChibiVM, the 16 bit pointer is a 'numbered entry' in the
AddressRemapTable. This is to allow 16 bit ChibiVM to address the
32 bit address space of the MIPS
Part 2 is platform
specific, and defines the sprite pattern grid,
This defines how the object is made up of hardware sprite
patterns.
It also offers platform specific functions, like sprite scaling or
palettes where available.
On systems where no Hardware sprites are available, XOR software
sprites are used (made up of 8x8 tiles)... XOR is used because it
means we don't need to worry about redrawing the background.
On the NDS and GBA we define the top bits for the 'shared
attributes' and the tilenumbers for each sprite.
Note the X and Y bits will be set automatically depending on the
final position of the sprite object
Part 3 is also platform specific.
This is the bitmap pattern data used to draw the sprite, in the
format of the hardware sprites or screen memory.
NativeSprite
NativeSpr_Init
will prepare the system for drawing sprites.
On hardware sprite systems, we transfer patterns to VRAM
The VRAM destination is different depending on if we're coding for
the NDS or GBA
On the GBA In Tilemap
mode, Sprite patterns are defined by by VRAM addresses 0x06010000+
On the GBA In
Bitmap mode, Sprite patterns are defined by by
VRAM addresses 0x06014000+, Due to the size of the bitmap screen
ram, only tile numbers 512+ are usable
On the NDS Sprite
patterns are defined by by VRAM addresses 0x06400000+
We transfer our pattern data to the correct address for the
system.
The sprites are 16 color, so we need to define the color palette
at addresses 0x05000200+ in the format %-BBBBBGGGGGRRRRR
We also need to turn on the sprite layer with port 0x04000000 ,
the value we send is different on NDS and GBA
NativeSpr_DrawArray
draws the array of sprite objects defined in Part1
The Co-ordinates of the sprite (in pairs of pixels) and the
pointer to the sprite object (part 2)
There are two versions:
NativeSpr_DrawArrayReiKou is used with ChibiVM,
and uses the AddressRemapTable to convert a 16 bit pointer to 32
bits.
NativeSpr_DrawArray uses a 32 bit pointer... If
you're using NativeSprite on it's own, this is the one you would
use.
We load in the X,Y position the sprite should pbe drawn, and the
pointer to the sprite definition.
We use NativeSpr_DrawExtra to draw the sprite
to the screen.
NativeSpr_ClearUnused
removes no-longer needed sprites forom the screen, by setting the
X,Y pos to 255 (offscreen)
Here is the correct Object definition
for the NDS/GBA systems
The Width and Height in patterns is defined in the first two
bytes.
On the NDS and GBA we define the top bits for the 'shared
attributes' and the tilenumbers for each sprite.
Note the X and Y bits will be set automatically depending on the
final position of the sprite object
NativeSpr_DrawExtra
will draw object R5 at position R1,R4
We load the next free hardware sprite number into R0, and run NativeSpr_Draw
On the GBA and NDS there are two pairs of two bits that define
the sprite size
We have a lookup table to define the pixel sizes of these.
NativeSpr_Draw
will draw the soprite to position R1,R4 using definition R5 and
hardware sprite R0
First we load in the shared attribute bits and X,Y co=ordinate
We calculate the size in pixels of each hardware sprite using the
shape and size bits (and our lookup table)
The X,Y co-ordinate is in what I call 'logical units' (Pairs of
pixels), so we convert the into actual screen pixels!
We define the three 16 bit values for each hardware
sprite, loaded to 0x07000000+ (three 16 bit values, plus a fourth
unused 'spacer)
On the GBA the first 512 patterns are not available (as they
overlap bitmap memory)
We build up the sprite object using multiple hardware sprites,
with the 16 bit patterns defined in the SpriteObject
After each sprite, we move across the screen by adding the
calculated sprite size (from the shape/size bits)
We then move down the screen to build up the full sprite.
NativeSpr_HideAll
will remove all the hardware sprites from the screen, and should
be used for things like title screens. It should also be used
before redrawing the background with XOR sprites.
It uses the NativeSpr_ClearUnused routine to remove all sprites
from the screen
Lesson
P11 - NativeSprite
on RiscOS
Lets look at 'NativeSprite' on the RiscOS. It allows us to use
hardware sprites in a multiplatform way!
On this system we use XOR 'software' sprites.
SrcROS/
V1_NativeSprite.asm
What Is NativeSprite?
Nativesprite allows us to use platform specific sprite
capabilities, to form 'objects' (grids of sprites which can be
used in the same way on all systems.
This allows us to write a multiplatform program, but gain the
benefits of the hardware capabilities.
Nativesprite splits the job of drawing sprites into three parts.
Part 1 is
multiplatform, it is a list of NativeSprite objects with X,Y
co-ordinates (in logical units - Pairs of Pixels)
Our game can change the co-ordinates and sprite object pointers to
change in-game graphics in a multiplatform way.
With ChibiVM, the 16 bit pointer is a 'numbered entry' in the
AddressRemapTable. This is to allow 16 bit ChibiVM to address the
32 bit address space of the MIPS
Part 2 is platform
specific, and defines the sprite pattern grid,
This defines how the object is made up of hardware sprite
patterns.
It also offers platform specific functions, like sprite scaling or
palettes where available.
On systems where no Hardware sprites are available, XOR software
sprites are used (made up of 8x8 tiles)... XOR is used because it
means we don't need to worry about redrawing the background.
Part 3 is also platform specific.
This is the bitmap pattern data used to draw the sprite, in the
format of the hardware sprites or screen memory.
NativeSprite
We need to define some memory for our variables. nativespriteaddr is the
address of the tile pattern bitmap data
nativespriteactivebuffer is
the settings of the current sprites which have been
drawn to the screen, we need this to 'remember' what
sprites are there, so we can remove them if they move or
change.
Because we're using XOR, we just 'redraw' a sprite in
it's old position to remove it.
NativeSpr_Init
will prepare the system for drawing sprites.
On hardware sprite systems, we transfer patterns to VRAM
On the XOR sprite systems we just remember the address we were
passed, which contains the bitmap 'tile pattern' data.
NativeSpr_DrawArray
draws the array of sprite objects defined in Part1
The Co-ordinates of the sprite (in pairs of pixels) and the
pointer to the sprite object (part 2)
There are two versions:
NativeSpr_DrawArrayReiKou is used with ChibiVM,
and uses the AddressRemapTable to convert a 16 bit pointer to 32
bits.
NativeSpr_DrawArray uses a 32 bit pointer... If
you're using NativeSprite on it's own, this is the one you would
use.
We need to check if the sprite needs to be drawn, we use nativespr_testone
to check if the position or appearance of the sprite
has changed
We use NativeSpr_DrawOne to draw the sprite to
the screen, we also use it to remove any old copy of the sprite -
as it's an XOR operation drawing twice to the same position will
remove it.
NativeSpr_TestOne Checks the
'ActiveBuffer' to see if the XOR sprite is already drawn to the
screen at this position
If it is it doesn't need redrawing
If it has moved or changed, we remove the old sprite (by XOR
drawing it in the same position) then draw the new sprite.
Nativespr_drawone
will load in the X and Y position of the sprite, and the
SpriteObject definition
We use Nativespr_drawextra to draw the
SpriteObject at the required position
Here is the correct Object definition
for the XOR systems
The Width and Height in patterns is defined in longs the first two
.
The other longs are the pattern numbers, calculated as offsets to
the previously specified source pattern data.
Nativespr_drawextra
will draw the soprite to position R1,R4 using definition R5 and
hardware sprite R0
First we load in the shared attribute bits and X,Y co=ordinate
The X,Y co-ordinate is in what I call 'logical units' (Pairs of
pixels), so we convert the into actual screen pixels!
We use these to calculate the vram destination (in R10).
In 256 color mode each line is 320 bytes
In 16 color mode each line is 160 bytes
We also need to calculate the source address for the 8x8 pattern
tile (in R11)
In 256 color mode each pattern is 64 bytes
In 16 color mode each pattern is 32 bytes
We transfer the bytes from the source pattern to
the screen. We work in bytes so we don't have alignment issues (we
would have with Half or Words)
To move down a line we add 320 bytes (256 color) or 160 bytes (16
color)
On the GBA the first 512 patterns are not available (as they
overlap bitmap memory)
We build up the sprite object using multiple patterns
drawnative_overrow copes with times the sprite
has gone off the right hand side of the screen
drawnativeskipx skips patterns on the left, or
patterns which are defined as 'empty'
NativeSpr_HideAll
will remove all the sprites from the screen, by XOR drawing them
in their original position
We then clear the data in the buffer.
Lesson
P12 - Multiplatform Bitmap on the Gameboy Advance and
Nintendo DS!
'Multiplatform Bitmap' is a routine that allows us to work with
pixels, or 8x8 pixel 'tiles' (In bitplane format) in a common way on
all systems.
It supports up to 16 colors (4 bitplanes). While not allowing us to
use the full functionality of the hardware, it allows us to use the
same graphics routines and source data on every system supported by
the routine!
SrcGBA/
V1_MultiplatformBitmap.asm
What Is Multiplatform Bitmap?
Multiplaform bitmap allows us to draw graphics in a common way,
whatever the capabilities of the system!
It's intended for low speed drawing or real-time calculated
drawing, like drawing title screens, or graphs, or even making a
sprite editor or other graphics package!
There are 4 functions provided:
mpbitmap_setpixel - Set a pixel to a color (0-15)
mpbitmap_getpixel - Reads a pixel from the screen
mpbitmap_settile - Set an 8x8 pixel tile block (in bitplane
format, 1-4 bitplanes)
mpbitmap_gettile - Get an 8x8 pixel tile block from the screen (in
bitplane format, 1-4 bitplanes)
Pixel Commands
Multiplatform bitmap is designed to work with a 16
color palette, but the NDS and GBA screens are 16bpp color.
To allow Multiplatform bitmap to work, we define a 'palette' which
will be used to convert the 0-15 palette number to a RGB Screen
format color
mpbitmap_getpixel_FindColor will
scan the palette to find a matching color number (0-15) from a
source screen format 16 bit half.
We scan up to 16 entries, and return the matching color.
mpbitmap_GetScreenPos
calculates the VRAM destination
On the NDS the screen is 256 pixels
wide, each pixel is 2 bytes, and VRAM starts from 0x06040000, so our
formula is
VRAM = 0x06040000 + (Ypos * 256*2) + (Xpos*2)
On the GBA the screen is 240 pixels
wide, each pixel is 2 bytes, and VRAM starts from 0x06000000, so our
formula is
VRAM = 0x06000000 + (Ypos * 240*2) + (Xpos*2)
If we're asked for a pixel which is 'offscreen' we set the carry
flag, to warn the calling routine the draw or read is impossible
mpbitmap_setpixel draws a pixel to
the screen
The color to set is specified in S0
The Y position in pixels is specified in S5
The X position is specified in S2
We use GetScreenPos to calculate the VRAM destination, and lookup
the color in the palette.
We write the color to the screen. on the NDS we ensure the top bit
is set (Alpha) - otherwise the pixel will be transparent!
The mpbitmap_getpixel
function gets a pixel back from the screen
We use mpbitmap_GetScreenPos to calculate the
source VRAM address,
Tile Commands
The 'mpbitmap_settile'
command will take an 8x8 block of data in bitplane format, and draw
it to the screen.
Up to 4 bitplanes are supported, meaning 16 colors, 32 bytes for the
full tile.
We start by calculating the VRAM destination using
GetScreenPos
Next we load in the bitplanes we've been provided with from r6, The
source data may be less than 4 bitpanes (specified by r0) , so we
load any others from the default 'MpBitmap_TileTints'
To calculate the color number we take the most
significant bit from each bitplane byte.
We use this as a lookup in our palette, giving us a 16 bit
screen-format color, and store it to the screen
We do this 8 times per line
To move down a line we add 256*2 or 240*2 to move down a line,
depending on the screen.
mpbitmap_gettile will
read pixel data back from the screen, returning 1-4 bitplanes as
required.
We calculate the source VRAM address using mpbitmap_GetScreenPos
We read data from the screen, loading in a half, nad converting it
using mpbitmap_getpixel_FindColor.
We shift the 4 bits from the color number into the 4 bitplane
'buldup values in r1-r4.
We do this 8 times for the 8 pixels from the screen, completing the
4 bitplane values.
We store the data back to address r6, writing back as
many bitplanes as we were asked to specified by r0
Lesson
P13 - Multiplatform Bitmap on
RiscOS
'Multiplatform Bitmap' is a routine that allows us to work with
pixels, or 8x8 pixel 'tiles' (In bitplane format) in a common way on
all systems.
It supports up to 16 colors (4 bitplanes). While not allowing us to
use the full functionality of the hardware, it allows us to use the
same graphics routines and source data on every system supported by
the routine!
SrcROS/
V1_MultiplatformBitmap.asm
What Is Multiplatform Bitmap?
Multiplaform bitmap allows us to draw graphics in a common way,
whatever the capabilities of the system!
It's intended for low speed drawing or real-time calculated
drawing, like drawing title screens, or graphs, or even making a
sprite editor or other graphics package!
There are 4 functions provided:
mpbitmap_setpixel - Set a pixel to a color (0-15)
mpbitmap_getpixel - Reads a pixel from the screen
mpbitmap_settile - Set an 8x8 pixel tile block (in bitplane
format, 1-4 bitplanes)
mpbitmap_gettile - Get an 8x8 pixel tile block from the screen (in
bitplane format, 1-4 bitplanes)
Pixel Commands
mpbitmap_GetScreenPos
calculates the VRAM destination
The Low byte of the X position is specified in R2
The High byte of the X position is specified in R6
(The X position is split into two registers to maintain functional
compatibility with the Z80 Version!)
The screen is 320 pixels wide, but each byte contains 2 pixels,
therefore our VRAM formula is:
VRAM = ScreenBase + (Ypos * 160) + (Xpos/2)
If we're asked for a pixel which is 'offscreen' we set the carry
flag, to warn the calling routine the draw or read is impossible
mpbitmap_GetScreenPosTile is the
alternative version, which uses an X,Y co-ordinate in tile blocks,
rather than pixels
mpbitmap_setpixel
draws a pixel to the screen
The color to set is specified in S0
The Y position in pixels is specified in S5
The X position is specified in S2/S6
We use GetScreenPos to calculate the VRAM destination.
Each byte contains two pixels, so we need to keep one, and change
the other... We put a mask in R12, and shift the mask depending on
if the pixel we've been asked to change is the left one, or the
right one.
The mpbitmap_getpixel
function gets a pixel back from the screen
Once again We use mpbitmap_GetScreenPos to
calculate the source VRAM address,
We read in the byte from the screen, and mask to keep the pixel we
want.
Tile Commands
The 'mpbitmap_settile'
command will take an 8x8 block of data in bitplane format, and draw
it to the screen.
Up to 4 bitplanes are supported, meaning 16 colors, 32 bytes for the
full tile.
We start by calculating the VRAM destination using
GetScreenPos
Next we load in the bitplanes we've been provided with from r6, The
source data may be less than 4 bitpanes (specified by r0) , so we
load any others from the default 'MpBitmap_TileTints'
To calculate the color number we take the most
significant bit from each bitplane byte.
We use two bits from each bitplane of our source data to build up a
2 pixel byte.
We write this byte to the screen, and repeat 4 times for the line of
the tile
To move down a line we add 160 bytes, as each line is 320
pixels
mpbitmap_gettile will
read pixel data back from the screen, returning 1-4 bitplanes as
required.
We calculate the source VRAM address using mpbitmap_GetScreenPos
We read data from the screen, loading in a byte which contains two
pixels.
We get the 4 bits of the color (0-15) for the left and right pixel
into the bitplane data store in R1-R4.
We repeat this 4 times. to read 8 bytes and fill our bitplanes
We store the data back to address r6, writing back as
many bitplanes as we were asked to specified by r0