Learn Multi platform 68000 Assembly
Programming... By Magic!
Platform Specific Lessons
Introduction to the Platform
Specific Series...
In this series of tutorials we're going to cover how to do simple common
tasks on multiple 68000 systems...
The lessons will cover how to do each task (where possible) on all the
systems and create functions that can be called in a common way, so we
will create a 'common library' of functions you can use in your own
programs to write platform independent 68000 code...
You should know 68000 already, If you don't please go through the Basic
68000 Assembly Lessons first!
Each lesson will have a matching Video tutorial, and if you just want
the code, you can skip knowing how it works, and just download the code!
Enough talk, let's start creating code, and put those 68000 machines to
work!
 |
Lesson
P1 - Bitmap Functions on the X68000
The x68000 has a built in dedicated graphics card, it's memory is
accessed through the normal addressable range, but there are some
quirks!
In this lesson we'll learn how to set up the graphics screen,
print text from a 1bpp font, and draw a bitmap to the screen! |
 |
 |
|
 |
Setting up the screen on
the X68000
The X68000 sceen is a bit odd, it typically works at 256x256 or 512x512
- which is square (and causes a problem for many VGA monitors), it can
also do a 512x256 mode (which I believe is tecnically running at
512x512)...
To set up the screen we want need to write the correct byte data
to the memory mapped registers - a chart of the correct working values
is shown below, and you probably don't want to alter these if they work
for you!
|
High Resolution |
Low Resolution |
|
RegNum |
768x512 |
512x512 |
512x256 |
256x256 |
512x512 |
512x256 |
256x256 |
Register Purpose |
E80000 |
$89 |
$5B |
$5B |
$2B |
$4B |
$4B |
$25 |
R00 Horizontal total |
E80002 |
$0E |
$09 |
$09 |
$04 |
$03 |
$03 |
$01 |
R01 Horizontal synchronization end
position timing |
E80004 |
$1C |
$11 |
$11 |
$06 |
$04 |
$05 |
$00 |
R02 Horizontal display start position |
E80006 |
$7C |
$51 |
$51 |
$26 |
$45 |
$45 |
$20 |
R03 Horizontal display end position |
E80008 |
$237 |
$237 |
$237 |
$237 |
$103 |
$103 |
$103 |
R04 Vertical total |
E8000A |
$05 |
$05 |
$05 |
$05 |
$02 |
$02 |
$02 |
R05 Vertical synchronization end
position timing |
E8000C |
$28 |
$28 |
$28 |
$28 |
$10 |
$10 |
$10 |
R06 Vertical display start position |
E8000E |
$228 |
$228 |
$228 |
$228 |
$100 |
$100 |
$100 |
R07 Vertical display end position |
E80010 |
$1B |
$1B |
$1B |
$1B |
$44 |
$44 |
$24 |
R08 External synchronization horizontal
adjust: Horizontal position tuning |
E80028 |
$406
|
$405
|
|
|
$05 |
$01 |
$00 |
R20 Memory mode/Display mode control |
E82400 |
$04
|
$04 |
|
|
$00 |
$00 |
$00 |
R0 (Screen mode initialization) - Detail |
E82500 |
$2E4 |
$2E4 |
$2E4 |
$2E4 |
$2E4 |
$2E4 |
$2E4 |
R1 (Priority control) - Priority
(Sprites foreground) |
E82600 |
$DF
|
$DF |
|
|
$C1 |
$C1 |
$C1 |
R2 (Special priority/screen display) -
Screen On / Sprites On |
EB0808 |
$200 |
$200 |
$200 |
$200 |
$200 |
$200 |
$200 |
BG Control (Sprites Visible, slow
writing) |
EB080A |
|
$FF |
$FF |
$FF |
$FF |
$FF |
$25 |
Sprite H Total |
EB080C |
|
$15 |
$15 |
$0A |
$09 |
$09 |
$04 |
Sprite H Disp |
EB080E |
|
$28 |
$28 |
$28 |
$10 |
$10 |
$10 |
Sprite V Disp |
EB0810 |
|
$15 |
$11 |
$10 |
$05 |
$01 |
$00 |
Sprite Res %---FVVHH |
We're going to define a
'screenInit' routine which will turn on the graphics screen -
we can choose the resolution we want by defining a symbol, such
as Res256x256
These Tutorials are using a 16 color mode (as it's most common
on retro systems)
We need to set up the CRT parameters which will define how the
screen image is formed - we also need to define the registers
which will configure the screen layout.
Note: we're not doing anything with the sprite registers at this
time - at the time of writing, hardware sprites are not used in
these tutorials, though theyy may be added later!
if you want more info on the registers, please see the GamesX
website, or the (Japanese) X68000
technical
data book |
 |
The X68000 is capable of multiple color depths
and multiple layers of paralax,
In these tutorials we're only going to use one 16 color layer -
the reason for this is that we're going to do the same on a wide
variety of 68000 systems (and even the same tasks on z80 and
6502 systems) and the vast majority of these systems support 16
color, but few support 256 colors. |
 |
Drawing Bitmap Graphics on the X68000
The X68000 screen is memory
maps to the hardware, but it's slightly odd compared to other
systems...
Each screen pixel uses two bytes of memory WHATEVER the screen
mode! - so whether you're using 4bpp or 8bpp, you're still
going to use |
Address |
Purpose |
$c00000 |
Graphics
Vram
� Page 0 |
$c80000 |
Graphics
Vram
� Page 1 |
$d00000 |
Graphics
Vram
� Page 2 |
$d80000 |
Graphics
Vram
� Page 3 |
|
We're going to use the A6
register to write data to the screen - we'll use a
'GetScreenPos' command to convert an X,Y pos (in D1,D2)
We're using Graphics Screen 0 - which starts at $C00000...
Each X pixel takes 2 bytes - so we do a ROL to shift X left 1
bit
Each Y line is 1024 bytes, so we do 8+2 ROL's to shift Y left
10 bits... we can't do ROL #10, the command doesn't support it |
 |
We have a GetNextLine command
to move down a line as well... |
 |
Using the bitmap routine
We can show a bitmap sprite to
the screen, buy using the GetScreenPos command to set the
correct memory position in A6
We're going to read in a sprite in 'Packed format' where both
nibbles of the byte is used - we'll need to split these up and
write them in separate words to the screen memory
We need to use GetNextLine to move down after each complete
line. |
 |
Bonus!... waiting for VSync to slow down a game
VSync on the X68000 is possible
via the MFP (MC68901)... we can test bit 4 to see if the
screen is in Vsync... by waiting for Vsync to start and end,
we can make sure a new vsync starts before we continue
processing...
This was used in the Grime 68000 game |
 |
 |
This Vsync
routine was used in Grime 68000 to slow down the game, but
it's not perfect, on a 100mhz x68 it's much faster than a
1st gen machine... but this is the best the author has
managed to do so far! |
PrintChar - using out Bitmap font to print
characters
Our 'PrintChar' routine will
show an Ascii character in D0 to the screen...
Essentially we're using the same code as above, but now we're
using Cursor_X and Cursor_Y as 'character position' for
calculating the screen postion
When it comes to actually plotting to the screen, we're
popping of a bit of the font lines, and moving it to 'color
15' of the byte, before drawing it to the screen.
Once we've drawn the character to the screen, we increase the
X position - and see if we've reached the width of the screen,
if we have then we run the NewLine command to wrap to the next
line. |
 |
You'll
need
to define Cursor_X and Cursor_Y in ram somewhere... also not
there's some simple LOCATE and CLS commands not shown here,
download the sources.z7 if you want to see them! |
 |
 |
Lesson
P2 - Bitmap Functions on the Atari ST
The Atari ST has a ram based screen - we can just allocate
some memory, and write data to it to get it on screen...
The layout is a bit odd - it's split into bitplaines in 16
pixel Words... lets see what that means! |
 |
 |
|
 |
Screen Layout
Lets take a look at how bitmap data is stored on the Atari
ST...
The screen is 16 color - which means we need 4 bits for each
color...
These colors are stored in 'Bitplanes' - this means each byte
defines one of the color bits for 8 pixels.
On the Atari ST pixel data is stored in words... 16 pixels
worth of data for a single bitpalne are stored in a Word...
the next word will be the next bitplane for the same 16
pixels... and so on for all 3 bitplanes...
After all 4 bitplanes for the first 16 pixels, the next 16
pixels will start...
The screen is 320 pixels wide, so each line is 160 bytes
across... and Line 1 is 160 bytes after Line 0 in the memory. |
 |
Hardware Registers
The Atari ST uses memory mapped registers to control the
screen...
These registers are defined from $FF8200-$FF8260.... Note that because
the 68000 uses a 24 bit address bus, the byte is usused - you may see
in some documentation addresses like $FF8200 shown as $FFFF8200...
these are the same address.
We need to define an area of our memory for screen use... we use
$FF8200-$FF8202 to define it but only the bottom byte of each
word address ... the screen will take 32000 bytes of data, however we
can only specify the top 16 bits of the 24 bit address... this means
the bottom byte of the screen address must be Zero - eg : $????00
The Atari ST has 3 screen modes - we'll be using mode 0 in these
tutorials, which give 16 colors at 320x200... we set the screen mode
by writing to $FF8260
We can also set colors with addresses $FF8240-$FF825F - one word
defines each color.
Address |
Mode |
Bits |
Purpose |
Details |
FF8200 |
RW |
--------
HHHHHHHH |
Video
Base H |
Need
32256 bytes per screen |
FF8202 |
EW |
--------
MMMMMMMM |
Video
Base M |
Can�t
specify L byte |
FF8240 |
RW |
-----RRR
-GGG-BBB |
Palette
Color 0 |
|
FF8242 |
RW |
-----RRR
-GGG-BBB |
Palette
Color 1 |
|
FF8244 |
RW |
-----RRR
-GGG-BBB |
Palette
Color 2 |
|
FF8246 |
RW |
-----RRR
-GGG-BBB |
Palette
Color 3 |
|
FF8248 |
RW |
-----RRR
-GGG-BBB |
Palette
Color 4 |
|
FF824A |
RW |
-----RRR
-GGG-BBB |
Palette
Color 5 |
|
FF824C |
RW |
-----RRR
-GGG-BBB |
Palette
Color 6 |
|
FF824E |
RW |
-----RRR
-GGG-BBB |
Palette
Color 7 |
|
FF8250 |
RW |
-----RRR
-GGG-BBB |
Palette
Color 8 |
|
FF8252 |
RW |
-----RRR
-GGG-BBB |
Palette
Color 9 |
|
FF8254 |
RW |
-----RRR
-GGG-BBB |
Palette
Color 10 |
|
FF8256 |
RW |
-----RRR
-GGG-BBB |
Palette
Color 11 |
|
FF8258 |
RW |
-----RRR
-GGG-BBB |
Palette
Color 12 |
|
FF825A |
RW |
-----RRR
-GGG-BBB |
Palette
Color 13 |
|
FF825C |
RW |
-----RRR
-GGG-BBB |
Palette
Color 14 |
|
FF825E |
RW |
-----RRR
-GGG-BBB |
Palette
Color 15 |
|
FF8260 |
RW |
------SS |
Screen
Mode |
00=320x200
@4bpp
01=640x200 @2bpp
11=640x400 @1bpp |
Setting up the screen
We need to define some ram for the screen...
we're going to define a 'BSS' section in our code...
Data in this section does not exist in the compiled file...
but any data areas in it will be allocated by the
operating system ... they will ALLWAYS be initialized to ZERO,
so we can only use DS to define areas, not DC
Because we need the bottom byte of the screen start to zero,
we're going to allocate 256 bytes more than we need. |

|
We'll start by changing the screen resolution by writing $00
to $FF8260
Now we need to use the address of the ram we defined in the
BSS section... however we need to zero align it - as we can't
set the Low byte of the screen address
Lets assume the screen_mem symbol has the address $00123456...
Our aligned screen will be $00123500... we'll store this in
'ScreenBase' ... but when we store this into $FF8200 - we need
the two bytes to be in the low bytes of the words... so we
shift it... converting $00120035
Finally, we'll define some basic colors to start us off
|
 |
Calculating a screen position with GetScreenPos
We're going to define our GetScreenPos function we'll take
an Xpos (in bytes) in D1, and a Ypos (in lines) in D2
We'll have to calculate an offset to our screen base... as the
4 bitplanes of16 pixels (2x4 bytes) are grouped together, for
every 16 pixels, we move 8 byte... we do this by
ignoring the bottom bit of the Xpos, and doing 2 bit shifts
but we need to move along one, to the middle of the word if
the selected screen byte is odd... we do this by adding the
bottom bit of the XPos
Each line of the screen has 160 bytes... so we multiply the
Ypos by 160, and add this to the screen position...
This gives us the resulting position to write our byte data to |

|
If we want to move down a line, we can just add 160 to our
current position to do it. |
 |
Waiting for VBlank
We may need to wait for VBlank to delay our games and make
them run at even speed...
We can use function $25 of Trap 14 (Xbios) to wait for the
vblank. |
 |
Alternatively we can use the
Vblank counter at address $462 - this is a counter that's
updated by the firmware every time vblank occurs. |
 |
Ideally,
we'd rather go direct to the hardware and detect VBLANK
rather than using the firmware, but the author of these
tutorials is too stupid to figure it out!... if you know
how, please give him a clue - and tell him how it works! |
 |
Showing a Bitmap to the screen
We're going to define our
bitmap with all 4 bitplanes in concecutive bytes (this is the
same format we'll use for the amiga)
This means we'll have to write all the bytes to the 4
bitplanes - and we'll have to move in an odd pattern:
We'll write 4 bytes (offset to point to the bitplane) -
move 1 byte , then write another 4 bytes (also offset to point
to the bitplane),
Finally we'll move another 7 bytes... this will move to the
start of the next word... and we'll repeat the procedure
again! |
 |
 |
We
could
use Atari ST native format, but there are advantages to
storing the data byte aligned, and converting it to word
alignment.
The reason we're doing this is it allow us to write to any
byte aligned X position - whereas if we used native Atari ST
screen foramt we'd have to be word aligned. |
 |
Lesson
P3 - using the FIX layer to draw bitmaps on the NeoGeo
The NeoGeo is not a bitmapped screen - so we can't set pixels
in memory ... and unlike other consoles, the NeoGeo's
backgrounds are made up of 16x16 Sprites not an 8x8 Tilemap...
However the NeoGeo does have an 8x8 FIX layer - which is
deigned for onscreen text and other such stuff - and to get us
started, we can use it to do our Chibiko Bitmap! |
 |
 |
|
 |
What is the Fix Layer
Most Console systems will have
2 types of graphics layer... a 'Tile Map' which is a grid of
predefined 'tiles'... these are usually 8x8 in size... on a 16
bit system, usually multiple layers of tilemaps exist to
define parallax...
On top of this we would add sprites to make our player
character and other such things...
But the NeoGeo HAS
NO TILEMAP! |
 |
So how does the NeoGeo work
with no Tilemap?
Well... sprites on the NeoGeo are 16 pixels wide, and can be
up to 512 pixels tall - but they can be combined!... and
because the NeoGeo is capable of a whopping 380 tiles, we can
combine 20 of them together to 'simulate' a tilemap!... this
is how background graphics are drawn on the Neogeo!
On top of this are our 'normal' sprites - enemies, player
characters and such...
There is one final layer, the 'Fix Layer'... this is made up
of 16 color 8x8 tiles, in a simple grid... it's designed to do
onscreen text and the like... but I used it in GrimeZ80 to do
all the game graphics... so if your needs are simple, and you
want 8x8 block graphics, it can be used for the job...
but don't worry, we'll learn about sprites later |
 |
 |
Grime
68000 used the FIX map for all it's graphics... but we
should have probably used Sprites...
Sprites are 16x16 - but the NeoGeo has Hardware scaling, so
we could scale them down to 8x8 - We'll learn all about
Sprites in a later lesson! |
Mysteries of The
Fix Layer!
The Neogeo screen has a resolution of 320x224, and each tile
is 8x8 - giving an effective screen size of 40x28...
The actual tilemap is 40x32... the top and bottom two lines
are not show (Shown in red on the chart to the right)
However, because of the CRT layout - it is likely that the
left and rightmost 1 column will not be visible (Shown in
orange to the right)... this gives a visible screen of 38x28
The Fix Layer is positioned in VRAM at &7000 (each
position contains 1 word / 2 bytes)- each Tile is defined by
16 bits in the following format:
F |
E |
D |
C |
B |
A |
7 |
8 |
|
7 |
6 |
5 |
4 |
3 |
2 |
1 |
0 |
P |
P |
P |
P |
T |
T |
T |
T |
|
T |
T |
T |
T |
T |
T |
T |
T |
Where
P is the Palette
number, and T
is the Tile number
The fixmap appears at $7000 in Vram... tile data for the
Fixmap are in ROM
Each address contains a WORD...
Write a word to $3C0000 to select VRAM Address
Write a word to $3C0002 to Send data to VRAM
Tiles in memory are ordered in COLUMNS... so in memory (X,Y)
co-ordinate (0,1) comes after (0,0) ($7000)... and (1,0) comes
32 words ($7020) after (0,0)
For example - to set tile (0,2) to tile 256 in palette 1 (note
this is in the "May be offscreen" area, but should appear on
an emulator)
Move.W #$7002,d1
;Address - Tile 2
Move.W #$1100,d0
;PTTT
- Palette and tile
Move.w d1,$3C0000
;set address in vdp
Move.w d0,$3C0002
;set tile data in vdp |

Start |
End |
Words |
Zone |
Description |
$7000 |
$74FF |
4096 |
|
Fix map |
|
Fix Layer tiles are in an odd format!
Each tile is 8x8 at 4bpp ... so 32 bytes per tile...
The two nibbles of each byte represent 2 pixels , but they are
BACKWARDS... so the 1st (High) nibble is the 2nd pixel color,
and the 2nd nibble (Low) is the 1st pixel color
The bytes are stored in Columns, then rows... and the columns
are out of order too! .. Visible pixels ABCDEFGH are stored in
Ram in order FEHGBADC
The 8 bytes of each column are in normal top->bottom
format... so at least that:s something!
|
 |
My AkuSprite editor (used in
these tutorials) has support to export images in the correct
"FIX" format for the NeoGeo |
 |
 |
The
FIX and SPRITE formats are different on the NeoGeo which is
VERY ANNOYING!
You can use AkuSprite Editor to create valid files - and
remember, it's open source, so if it doesn't do the job well
enough, you can use the source to make something better! |
Defining our ROMS
via MAME XML
We need to define our FIX rom data in the NEOGeo.XML...
we'll split the fix data into 3 files...
NAME
specifies the filename
OFFSET specifies
the memory position in the 'ROM' of the Neogeo
SIZE
is technically the size of the file - but mame doesn't seem to
check if it's correct (Warning: it does with sprites!)
CRC /
SHA1 -
these are hashes of the rom - if they are incorrect MAME will
complain

we're defining 3 files
202-s1.s1 - This is the NEOGEO firmware
fonts, we'll leave them alone
FONT.FIX - This is the ChibiAkumas
font used by these tutorials, you'll need this for the
PrintChar routines to work
RawNEO.FIX - this is the bitmap data we're
going to use today to show 'Chibiko' onscreen! |
If we want
to create a 'proper' rom file, we'll need to calculate the
correct Hashes, but it's quicker to skip it, and the game
will run, so it's OK for our testing |
 |
Getting
our bitmap to the screen
We're going to put our
'Chibiko' character on the screen, the sprite is 48x48, so
it's made up of 6x6 FIX tiles...
We'll set the correct positions of the fix tilemap to the
parts of the bitmap. |
 |
We're going to use a function
called FillAreaWithTiles... this will take a start XY
position, a Width and Height, and a start Tilenumber... it
will fill the entire area with consecutive tiles
Left->Right, Top->bottom
Note... the NeoGeo works the opposite way: Columns first, then
Rows
We're allocating tiles 0-255 for our font, so we're using
tile 256 for our Chibiko Bitmap |
 |
When we call our
FillAreaWithTiles function, we'll need to convert the X,Y
co-ordinate into a memory
The first 2048 tiles
($800) are used by the firmware - we're also going to use Palette 1
- so we add $1800 to the
tile number to get the data we write into VRAM
We then need to calculate the offset in the FIX map for the
tile we want to change... the fixmap is organized in
Column/Row order (Going down then Across)...
We do this by adding X*32+Y to the start address of the fixmap
($7000)
We shift the Xpos left 5 times with ROL.L #5 - multiplying X
by 32...
Because the top 2 lines of the NeoGeo screen are not viewable,
we add 2 to the Ypos
To set the Tile, we write the address we want to change to
$3C0000.... and the TileNumber/Palette to $3C0002
We repeat the procedure for each of the tiles we want to draw
to the screen. |
 |
 |
Lesson
P4 - Bitmap Functions on the Sinclair QL
The QL's graphics are visually very different to the other 16
bits (if you count the QL as 16 bit!!)
It uses a fixed palette, of 8 primaries (RGBCMYWb) in mode 8,
or 4 in mode 4 (bRGW) |
 |

BitmapTest.asm |
|
 |
Screen Layout
The Screen is memory mapped
from $20000-$28000, it is also possible to have a second page
at $28000-30000, but this will render the OS unusable, as it's
fixed variables are in that memory
Lines are in a linear format, with each line 128 bytes below
the last... two concecutive bytes make up 4 pixels in 8
color, or 8 pixels in 4 color mode.
There are two possible screen modes, configured by bit 3 of
port $18063
setting a 0 give 4 colors at 512x256 with Black,Red,Green and
White
setting a 1 give 8 colors at 256x256 with Black, R, G B,
C, M, Y and White
There is no palette - the colors are fixed... it is not
possible to have 'brightness levels' - colors are on or off.
My AkuSprite Editor can export 4 or 8 color bitmaps, however
it does not support Flashing mode.
8
Color Mode
|
4 Color Mode
 |
|
Address |
Purpose |
Details |
$18063 |
|
Screen
Mode S---C-O-
O=On (doesn't actually work!)
C=Colordepth S=Screenpage |
$20000 |
Screen 1 |
Screen
Ram |
$28000 |
Screen
2 /
System |
system
(systemvars*) |
$2847C |
System
stack pointer* |
$28E00 |
Base
of Common Heap* |
$2BC00 |
Free
area* |
$30000 |
Running
Programs |
Free
area
|
4 Color mode:
F |
E |
D |
C |
B |
A |
9 |
8 |
|
7 |
6 |
5 |
4 |
3 |
2 |
1 |
0 |
G7 |
G6 |
G5 |
G4 |
G3 |
G2 |
G1 |
G0 |
|
R7 |
R6 |
R5 |
R4 |
R3 |
R2 |
R1 |
R0 |
8 Color mode: (F is flashing)
F |
E |
D |
C |
B |
A |
9 |
8 |
|
7 |
6 |
5 |
4 |
3 |
2 |
1 |
0 |
G3 |
F3 |
G2 |
F2 |
G1 |
F1 |
G0 |
F0 |
|
R3 |
B3 |
R2 |
B2 |
R1 |
B1 |
R0 |
B0 |
|
Flashing in 8 color mode
Flashing only works in 8 color mode -it's a HARDWARE flash,
and does not use th interrupts or firmware.
A flashing bit of 1 will toggle flashing ON or OFF... flashing
always starts as OFF at the start of a line
When a Flashing bit 1 is set - all subsequent pixels will
flash between their normal defined color and the color of the
pixel with the flashing bit set. |

F= Flashing bit of 1
|
 |
Many
Emulators don't bother to emulate FLASHing... QLAY2 doesn't
do it, and Q-Emulator doesn't seem to do it right except in
full screen mode... The latest versions of Zesarux DO seem
to do it well though...
That said, Falshing mode is pretty dumb anyway, so you're
not missing anything important if your emulator doesn't
support it!
|
Selecting Screen Mode
When we want to change screen mode, we just set bit of
$18063 according to the mode we want...
a 1 will set 8 color mode... a 0 will ser 4 color mode! |
 |
Calculating Screen position
When we want to calculate the
memory location of an X,Y position, it's pretty easy.
using an X position in 4/8 pixel blocks, and Y in lines we can
use following
Each 8 pixels uses 2 bytes, so we bit shift the X pos in D1 by
one bit.
Each line uses 128 bytes, so we bit shift the Y pos in D2 by 7
bits.
Then we add $200000 - the offset to the start ofthe screen. |
 |
When we want to use this, we
just need to copy the bytes from our source (created by
AkuSprite) to the screen,
After each line we use GetNextLine to move down the screen to
start drawing the next line...
Because 4 color mode has twice as many pixels per byte, we
need to alter our line total in D1 depending on if we're in 4
color mode or not. |
 |
This routine will show a simple
bitmap...
We also have 'PrintChar' routines, these use a 1bpp font, and
show letters from a common font (used on all the systems) |
 |
The
Sinclair
QL's screen is pretty limited for a 16 bit machine, but
remember... it's an 68008 - which has an 8-bit data bus, so
technically it's and 8 bit machine... this means it's much
slower than an Amiga or similar... and you'll have to
optimize yout code more to keep the games fast.
The reduced graphics were probably and unavoidable sacrifice
considering the price-point and early release of the
machine. |
 |
Vsync
We can use Vsync to slow down our game... by waiting for
Vsync, we can wait for the next screen redraw
If we want to test for Vsync (the start of screen redraw) we
need to use Port $18021... bit 3 will go high (1) when Vsync
starts... we need to write a 1 to that same bit at the same
port to clear the Vsync event...
Therefore, in effect we can write 255 to port $18021, then
read from $18021 until it's nonzero to get the Vsync event. |

|
|
7 |
6 |
5 |
4 |
3 |
2 |
1 |
0 |
|
$18021
Read |
B |
M |
R |
X |
F |
T |
I |
G |
B=Baud
state,
M=Microdrive inactive, R=Rtc
state,
X=eXternal
Interrupt,
F=Frame
vsync,
T=Transmit
interrupt,
I=IPC
Interface interrupt G=Gap
interrupt
(microdrive)
|
$18021
Write |
R |
F |
M |
X |
F |
T |
I |
G |
R=tRansmit
mask,
F=interFace
mask,
M=gap
Mask,X=reset
eXternal
Interrupt,F=reset
Frame
vsync, T=reset
Transmit
interrupt, I=reset
IPC Interface interrupt G=reset
Gap
interrupt |
 |
Lesson
P5 - Bitmap Functions on the Genesis
The Genesis is a Tile - Sprite based system... this means we
can't just plot data to the screen... we need to define our
bitmap data as tiles in vram, then set those tiles as visible
onscreen...
Lets give it a go! |
 |

BitmapTest.asm |
|
 |
Setting up our screen
We need to initilize our
screen, to do this we need to set some VDP settings... these
start with $8nvv - where n is the register number and vv is
the value for the register... we'll load these from the table
VDPSettings |
 |
We'll be using the values shown
for our tutorials...
you can change the values if you wish, but if you change
things like the name table positions, the tutorials will not
work correctly unless you also alter the code that draws
characters and tiles to the screen |
 |
We're going to set up a simple
palette to get us started...
Basically, each color in the palette has two bytes defining
it's RGB value, but we're not going to go into more detail at
this stage, as we'll be covering it in more detail later. |
 |
We've now done the basics... so we'll start the screen
display, then convert our 2 bit font to tiles in the tile
definitions. |
 |
 |
Setting
Reg 16 to 01 defines a tilemap of 64x32 - we could define
one that's 32x32 or 64x64, but as our visible screen is
40x28 so 64x32 seems a good choice!
If you change it, or the memory address of Scroll A, then
you're going to have to change the rest of the code in
todays example! |
Defining Tile patterns
When we want to write data to
the VDP, we just write the data value to address $C00000
(vdp_data).... but first we need to select the VDP destination
address using $C00004 (vdp_ctrl)
We'll need to select addresses from $0000-$C000 to define our
tiles and each tile takes 32 bytes.
Unfortunately, because the VDP address select is designed to
be backwards compatible with the SMS, the bytes we have to
send to select the port are a little odd!... Effectively the
top two bits of the address are moved to the bottom two, and
the top two bits are set to %01...
you can see some how various sample VDP addresses convert to
address select commands in the table to the right.
|
Vram
Memory Address |
Byte
Command |
$0000 |
$40000000 |
$1000 |
$50000000 |
$2000 |
$60000000 |
$3000 |
$70000000 |
$4000 |
$40000001 |
$5000 |
$50000001 |
$6000 |
$60000001 |
$7000 |
$70000001 |
$8000 |
$40000002 |
$9000 |
$50000002 |
$A000 |
$60000002 |
$B000 |
$70000002 |
$C000 |
$40000003 |
$D000 |
$50000003 |
$E000 |
$60000003 |
$F000 |
$70000003 |
$FFFF |
$7FFF0003 |
|
Vram Address |
Possible Use |
$0000 |
Pattern
definitions |
$C000 |
Scroll
A
� Tilemap |
$D800 |
Sprite
Attrib
table |
$E000 |
Scroll
B
� Tilemap |
$F000 |
Window
Map |
$FC00 |
Hscroll
Table |
|
Selecting VDP
memory
is really confusing... but don't worry, we're going to
create a little function that will do all the work for us!
The reason it's so confusing is the Genesis was designed to
be backwards compatible with the Master system... that's why
the genesis uses a Z80 for it's sound chip - it can be
used as the main CPU for SMS support! |
 |
To simplify sending data to the
VDP we'll create a command called 'PrepareVram'
This will calculate the correct address to write to within the
VDP |
 |
When we want to define tiles,
we'll just copy the tile data into the VDP...
We'll define the start of the tile definitions in A0, the
destination in vram in D2, and the bytecount in D1 |
 |
When we want to transfer our
tile definitions to VRAM, we can just call this 'Define Tiles'
command. |
 |
Of course, you'll need to
convert a bitmap into the correct format for the VDP... but
you can do this with my AkuSprite editor!
It's free and open source, and you'll find it in the
sources.7z |
 |
 |
AkuSprite
editor
supports all the systems covered by these tutorials... and
It'll be extended to cover any systems added in the future.
It's only a basic program, but you can load bitmaps in via
the clipboard so you can use a more advanced eitor like
Krita to do the hard work |
Getting our tiles on the screen
We've now got our 'Chibiko'
bitmap in vram as patterns, but we need to get it on screen...
the bitmap is 48x48 pixels... meaning it's made up of 6x6 (36)
tiles...
To show our bitmap on the screen we need to set the tiles in
the positions we want to the correct pattern numbers for our
bitmap! |
 |
We're going to define a command
called 'FillAreaWithTiles'
We'll pass it a Start XY position, a width and height, and a
start tilenumber (256 - the first 128 are used by our font)
The command will calculate and set the tiles to get our bitmap
onscreen! |
 |
We've defined our tile map as
64 tiles wide, and 32 tiles tall... each tile definition takes
2 bytes... this means each row of the tile map is 128 bytes...
Therefore, to calculate our memory address within VRAM, we use
the formula $C000+(Y*128)+(X*2) |
F |
E |
D |
C |
B |
A |
9 |
8 |
|
7 |
6 |
5 |
4 |
3 |
2 |
1 |
0 |
L |
P |
P |
V |
H |
T |
T |
T |
|
T |
T |
T |
T |
T |
T |
T |
T |
T=Tille
number
H=Hflip
V=vflip
P=palette
number
L=Layer (in front of /behind sprites)
|
We're going to merge our
calculation of the byte position in VRAM and our 'PrepareVram'
command to save CPU power,
For each line, we'll calculate the starting address of the
line and send the VRAM select command to the VDP
We'll then start writing our tile numbers to VRAM... The VRAM
address automatically increments after each write, so we don't
need to recalculate until we've completed the whole line, but
we do need to increment our tile number in D4
After each line we recalculate the memory position for the row
below,
We continue this procedure until the the bitmap is drawn to
the screen! |
 |
Of
course this is just a simple example... but you can use it
to create something better... Take a look at Grime
68000 if you want to see a more advanced example!
We've only looked at Tiles so far, but don't worry, we'll
look at sprites later! |
 |
VBlank
We can use Vblank as a way of slowing down our game...
Vblank occurs when the screen restarts drawing, so happens 60
times a second (50 on PAL systems)
We can detect Vblank using bit 3 of data read from the Control
port, we just read in a byte and test it, if the bit is 1
we're in VBlank.
We can wait until Vblank starts, and ends to ensure our game
isn't running faster than 50fps. |
 |
 |
Lesson
P6 - Bitmap Functions on the Amiga
The Amiga is essentially a 'bitmap' based system, we allocate
an area of memory to be our screen data, and we can write data
into that area to get our pixels on the screen,
However there are some complexities to getting things working,
so lets learn how to make it behave! |
 |

BitmapTest.asm |
|
 |
The Amiga screen is essentially a bit-planed bitmap within 'Chip Ram'
(the built in memory of the Amiga - not upgrade memory)... we can
write bytes to this ram, and they will immediately appear on the
screen - but first we need to set the screen up!
Graphics on the amiga are controlled by the ' Chip
Registers' a set of memory mapped registers which control the
screen... in theory we could access these directly... but in practice
we need to use the "Copper" graphics CO Processor...
We give this a set of commands defining the screens location and shape
in ram!
Bitplanes and colors
The Amiga works in
'Bitplanes'... this is where each byte of data for the screen
contains a single bit of 8 pixels - called a bitplane...
4 of these bitplanes combined will allow us 16 colors...
The Amiga 500 supports up to 6 bitplanes, We can optionally
configure these bitplanes into 2 layers in parallax...
The Amiga can use up to 32 colors in a single layer (5
bitplanes) ... or have two 3 bitplane layers with 8 colors
each (odd numbered bitplanes will be layer 1, even numbers
will be layer 2)
In these tutorials we'll be defining one 320x200 layer of 16
colors (4 bitplanes)... this is because it will give good
'compatibility' with the Atari ST, and the other systems we're
covering in these tutorials.
|
8
pixels we want to set:

Bitplanes compared to nibbles

|
Copper list commands
We're going to need to use a copperlist to set up our screen...
Copperlist commands can set a chip register, or wait for a
particular screen position
Word 1 |
|
Word 2 |
|
|
|
F |
E |
D |
C |
B |
A |
9 |
8 |
|
7 |
6 |
5 |
4 |
3 |
2 |
1 |
0 |
|
F |
E |
D |
C |
B |
A |
9 |
8 |
|
7 |
6 |
5 |
4 |
3 |
2 |
1 |
0 |
|
Command |
Details |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
n |
|
n |
n |
n |
n |
n |
n |
n |
0 |
|
D |
D |
D |
D |
D |
D |
D |
D |
|
D |
D |
D |
D |
D |
D |
D |
D |
|
Change setting |
n=
address to Change ($DFFnnn) D=new
data
for address |
V |
V |
V |
V |
V |
V |
V |
V |
|
H |
H |
H |
H |
H |
H |
H |
1 |
|
v |
v |
v |
v |
v |
v |
v |
v |
|
h |
h |
h |
h |
h |
h |
h |
1 |
|
wait for pos |
V=Vops
H=Hpos v=Vpos
Compare
enable h=hpos
compare
enable |
The copperlist should end with an infinite wait '#$fffffffe'
Setting up Chip Ram
We need our screen and the 'Copperlist' for the
co-processor to be contained within the 'Chip Ram'
To ensure this we need to define a 'ChipRAM'
section - the Assembler / Linker will create an
executable file, and the operating system will ensure this
area of the program is in the correct
We're also going to use the command 'CNOP
0,4'... this zero pads the area to a 32 bit boundary
- needed for the screen data
Now we have some memory, we can start setting up the screen! |
 |
 |
'Chip
Ram'
in the Amiga is the basic ram of the machine (not add on
upgrades)... we need it to do tasks like Graphics and Sound
FX...
Our main code probably isn't running in Chip ram, so we
can't 'tag' our screen buffer in there - we have to declare
proper section, so the Assembler will build the correct
executable and the OS will give us our memory |
Setting up our screen
The first stage of setting up
our screen is calling the os, and opening the graphics library
...
we'll use this to turn on the screen, but we'll do all the
other screen config by directly manipulating the hardware
registers!
You can see the different libraries the Amiga has here
... and the full contents of the Graphics library here |

 |
We're going do the basics of
defining the screen size, bitplanes and position.
we need to use the chip registers to do this (see here
for the full list)
We're going to set up our 320x200 16 color (4 bitplane) layer,
Next we'll set up the screen position
Finally we want to start the DMA control to handle the
screen...
we're now going to set up our copperlist that will define the
screen memory and colors!
|


|
Setting up our copperlist
We need to define our
copperlist, the Coprocessor will use it to draw each frame of
our screen.
As mentioned before, the copperlist commands are made up of
two words per command,
The first word (2 bytes) is a register number... for
example $00e2 is $DFF0E2
The second word (2 bytes) is the new value for the
command.
First we'll define the memory location of the 4 bitplanes that
make up our screen - they'll be 8000 bytes apart (40x200)
Then we'll define our starting color palette... we'll also
remember the memory position of the palette definitions, as we
can use it later to change our colors.
The last commands is a command to wait forever ($fffffffe) ...
the list will automatically restart when the next screen
redraw starts.
|
 |
When we've defined our copperlist, we load the address of the
copperlist into the pointer of the Chip Ram - our screen is
finally set up! |
 |
 |
The
example here is just creating a single plane, you could have
more colors, or more planes with a more advanced set up, but
that's beyond the scope of these tutorials - but feel free
to play around if you want! |
Getting our bitmap data to the screen
Our screen is split into
4 bitplanes, and as the screen is 320 pixels wide (40 bytes)
and the screen is 200 lines tall, then each bitplane will be
8000 pixels apart.
Of course, if we want to set the color of a pixel, we'll have
to set all these bitplanes, and we'll see that in our code |
Offset
from
Screen_Mem |
Bitplane Number |
0
0 |
Bitplane 1 |
40*200*1
8000 |
Bitplane 2 |
40*200*2
16000 |
Bitplane 3 |
40*200*3
24000 |
Bitplane 4 |
|
We can see this mimicked in our
bitmap copying code...
The byte data of the bitmap are in bitplane format (exported
by my AkuSprite Editor)
4 consecutive bytes bytes are copied to each bitplane.
We use the GetScreenPos to calculate the correct memory
position from a X,Y position (where X is in Bytes, and Y is in
lines)
We also use GetNextLine to move down a line at the end of each
line of our bitmap |
 |
The bitmap will be shown to the
screen at the position we specified! |
 |
Want to
convert a bitmap for use on the Amiga? The Free & open
source AkuSprite Editor can do it for you!
It's included in the sources.7z, so go get it if you want! |
 |
Calculating screen co-ordinates
Setting up the screen was hard,
but Calculating a screen position is easy.. all we need
to do is multiply our Ypos by 40 (the width of the
screen) and add X , and the base of the screen.
Moving down a line is also easy, we just add 40 to the current
position! |
 |
 |
Of
course, calling a routine to move down a line is a pretty
lousy to do things if you're just doing an Amiga game, you
should just put that ADDA straight in the loop!
The reason we do the call is we're supporting lots of
machines with the common drawing code... THEN it makes
sense!
|
Testing for Vblank
If we want to slow our game down, a good way is to wait for
Vblank (The start of screen redraw)
this will cap our game to 60/50 hz, and keep our game speed
under control
We can test for Vblank on the Amiga by testing $DFF004 (VposR) |
 |
Common Data format for Joypad controls used by these tutorials
On the Z80 We used to read in the buttons and fires into a
single byte... on the 68000 systems we're going to keep the
same format for ULDR Fire1+2 and start... but we'll also
extend into a second byte for any extra fire buttons we
have...
for each bit, a 1 means the button is up (unpressed)
a 0 means the button is down (pressed)
We'll load player 1's joystick data into D0... and player 2's
into D1 |
Bit |
F |
E |
D |
C |
B |
A |
9 |
8 |
|
7 |
6 |
5 |
4 |
3 |
2 |
1 |
0 |
Meaning |
- |
- |
- |
F8 |
F7 |
F6 |
F5 |
F4 |
|
Start |
F3 |
F2 |
F1 |
Rgt |
Lft |
Dn |
Up |
|
Our test program is very simple, all it does is read in the
two controllers, and show the value of the registers to the
screen. |
 |
 |
The
data
we're producing here is not just consistent across 68000
systems, but also the 6502 and Z80 systems in these
tutorials... this was used as part of Grime 68000 to make
converting the game easier. |
Ports relating to the Joypad
We're going to use 3 ports to access the two players
buttons...
$E9A001 reads player 1, $E9A003 reads player 2
$E9A005 selects which buttons we're reading in
|
Address |
Purpose |
Bits |
Notes |
$E9A001 |
Joystick
#
1 |
|
Data
from
Joy 1 |
$E9A003 |
Joystick
#
2 |
|
Data
from
Joy 2 |
$E9A005 |
Joystick
Control |
--MM---- |
00
Select
Normal / 11 alt keys |
|
Depending on the bits we write
to $E9A005, the data we get back from the two Joystick ports
will either return the basic joystick buttons, or the extra
buttons of a genesis joypad. |
Byte written to
$E9A005 |
Purpose |
$E9A001
/ $E9A003
Data Bits |
%00000000 |
Basic
Buttons |
%-21-RLDU |
%00110000 |
Extra
Buttons |
%-S3-M654 |
|
Lets get the data!
We're going to define a
function called "Player_ReadControlsDual"
This will read both joysticks, we load the address of the
joystick we want to read into A0... then we call
JoystickProcessOne
The JoystickProcessOne function will process all the buttons
from the port in A0... and return the buttons in D0 |
 |
To select the basic controls,
we write $0 to $E9A005... now we just
need to read in from (A0)... we need to shift the bits around
a bit to get them in the same format as the other systems...
Now we do the same with the extra buttons, we write %00110000
to $E9A005... and then we bit shift all the extra
buttons in the correct positions...
finally we OR all the unused bits to 1, to make the returned
data consistent. |
 |
The Theory!
We're going to need to turn on the Joystick, and add an 'event
handler' to the joystick, to read in the data from the Joystick.
We're going to need some TRAPS to call systems functions, We'll need
the BIOS trap (Trap #13) to turn on the joystick, and XBIOS trap (trap
#14) to get the vectors, so we can patch in our joystick handlers.
You can see all the Traps and options here,
The IKBD commands are here, and
the vectors that are returned are here,
This is just for reference, you won't need these to get things working
today
On our Atari ST we'll consider Port 1 to be the 1st joystick, and Port
0 to be the 2nd (as we'll assume it usually has a mouse)
Common Data format for Joypad controls used by these tutorials
On the Z80 We used to read in the buttons and fires into a
single byte... on the 68000 systems we're going to keep the
same format for ULDR Fire1+2 and start... but we'll also
extend into a second byte for any extra fire buttons we
have...
for each bit, a 1 means the button is up (unpressed)
a 0 means the button is down (pressed)
We'll load player 1's joystick data into D0... and player 2's
into D1 |
Bit |
F |
E |
D |
C |
B |
A |
9 |
8 |
|
7 |
6 |
5 |
4 |
3 |
2 |
1 |
0 |
Meaning |
- |
- |
- |
F8 |
F7 |
F6 |
F5 |
F4 |
|
Start |
F3 |
F2 |
F1 |
Rgt |
Lft |
Dn |
Up |
|
Our test program is very simple, all it does is read in the
two controllers, and show the value of the registers to the
screen. |
 |
Defining Data
We're going to define a few bytes of data for our use in
this example... two longs to back up the settings of the Bios
(we won't actually need them, but you might if you want to
undo things)...
We also need two bytes... one for each joystick's data during
the read operation |
 |
Our
principle is simple, get the bios to run our program code...
and use that code to copy the joystick data into a buffer...
When we want it, we'll sort the data into the format we
need. |
 |
Trap Attack! Getting the bios to do our bidding!
We're going to tell the BIOS that we want to use the
Joystick... we'll need Trap #13 to send a command to the
bios...
We want to use Command #3 to send a command via Bconout... we
push #13 as a word onto the stack before the trap command...
BUT... the BconOut also needs a device number and a character
to send...
We want device #4 - which is the keyboard (which the joystick
is connected to)... we push this onto the stack too...
We want to send #$14 (#20) to the keyboard - this command
tells the keyboard to send events (Every joystick
movement is automatically returned.)
When we've done all this, and the Trap #13 finishes, we need
to fix the stack, so we add 6 to the stack, to remove the 3
words we pushed
|

|
Now we're going to use the XBIOS... which uses Trap #14...
we want to use command #34 'KbdVBase' - so we push #34
onto the stack as a word...
|
 |
KbdVbase returns a vector table in D0 - this is a table of
calls for various purposes - we want to change entry 6...each
is a long so 6x4=24
We'll back up the address of the vector table,
Next We're going to back up the current Keyboard vector
(though in our code we won't ever undo it)...
Finally we'll copy the address of our new joystick code
(JoystickHandler) to the 24'th entry in the table...
This means our code can get the Joystick data. |

|
 |
Now we've
made the Bios do what we want, our Joystick handler will be
called automatically, and will grab the data for us...
That's all we need the Bios for... now we're in control
again!
|
Our Joystick Handler!
Because we patched it into the
Vector table, Our Joystick handler will be called by the
firmware, when it does, A0 will point to the data we
want...
We're going the data from both joysticks into our 2 byte
'Joystick data' cache... we'll process that when the game
actually wants to know what keys are pressed |
 |
Processing the joysticks
When it comes to processing the
joysticks, we'll transfer the byte from the buffer for each
joystick into D0, then we'll run 'ReadControlsProcessOne'...
this will shift the bits from the Atari ST Format (F---RLDU)
to our common format (---FRLDU)
We do this by shifting the bit we want to move into eXtend,
then shifting things around, and putting it back in the
position we want...
We do this twice, moving Player 2's data into D1 |
 |
 |
Lesson
P9 - Joystick Reading on the
NeoGeo
The Joystick on the NeoGeo makes things easy for us!
There are two ports, one contains Select/Start buttons,
and the other contains the joypad settings.
We can just read in from these to get the directions selected
on the Joypad. |
 |
 |
|
 |
Common Data format for Joypad controls used by these tutorials
On the Z80 We used to read in the buttons and fires into a
single byte... on the 68000 systems we're going to keep the
same format for ULDR Fire1+2 and start... but we'll also
extend into a second byte for any extra fire buttons we
have...
for each bit, a 1 means the button is up (unpressed)
a 0 means the button is down (pressed)
We'll load player 1's joystick data into D0... and player 2's
into D1 |
Bit |
F |
E |
D |
C |
B |
A |
9 |
8 |
|
7 |
6 |
5 |
4 |
3 |
2 |
1 |
0 |
Meaning |
- |
- |
- |
F8 |
F7 |
F6 |
F5 |
F4 |
|
Start |
F3 |
F2 |
F1 |
Rgt |
Lft |
Dn |
Up |
|
Our test program is very simple, all it does is read in the
two controllers, and show the value of the registers to the
screen. |
 |
Hardware Ports
Reading Joysticks on the NeoGeo requires reading from 3 memory
addresses... one for the start buttons, and two for each of the two
main Joysticks.
We actually have a choice - we can read in from the hardware directly
using the $3x0000 addresses, of via the bios updated ones,
The only real difference is that if we read directly from the
hardware, then 1
will be a button that is up, and 0
will be down...
Alternatively, if we read from the bios, it's backwards so 0 will be a button
that is up, and 1
will be down...
|
|
|
Bits |
Address |
Purpose |
Method |
7 |
6 |
5 |
4 |
3 |
2 |
1 |
0 |
$10FDAC |
Sel/Start |
Bios |
P4-Select |
P4-Start |
P3-Select |
P3-Start |
P2-Select |
P2-Start |
P1-Select |
P1-Start |
$380000 |
Sel/Start |
Direct |
AES/MVS |
WriteProtect |
CardInserted
2 |
CardInserted
1 |
P2-Select |
P2-Start |
P1-Select |
P1-Start |
$10FD96 |
Player
1 |
Bios |
D |
C |
B |
A |
Right |
Left |
Down |
Up |
$300000 |
Player
1 |
Direct |
D |
C |
B |
A |
Right |
Left |
Down |
Up |
$10FD9C |
Player
2 |
Bios |
D |
C |
B |
A |
Right |
Left |
Down |
Up |
$340000 |
Player
2 |
Direct |
D |
C |
B |
A |
Right |
Left |
Down |
Up |
 |
The NeoGeo
AES also has coinslots, these can be read in from $320001
Bits 0,1,3,4 are coins 1-4... bit 3 is the 'service button'
You'll need these if you're trying to crete an arcade game! |
Getting the data
As it's fairly easy, The example code today will support
reading from either the BIOS variables, or the direct ports,
to select the Bios we just need to define NeoJoy_UseBios, and
the code will use the bios ports instead. |
 |
First we're going to handle Joystick 1 - we'll load it's
data into D4...
We're also going to need Joystick 1's Start button, all the
start buttons are in the same port, so we'll load them into D3 |
 |
As both joysticks use the same layout, We'll use a common
function 'Player_ReadControlsOne' to shift the bits around
into the order we need... we'll run it first for joystick 1,
and store the result in D0...
We'll take a look at what the function does in a moment. |
 |
We're going to do the same now for Player 2.
We don't need to read the Start buttons in again, as the
second players start still remains in the byte in D3 we read
before. |
 |
We using the 'Player_ReadControlsOne' function to convert
the data for both players...
When this function starts, D4 will contain the joystick fires,
First we need to shift button D out into D2 - we'll need it
later, but we need to put the Start button into Bit 7
We'll shift the Start button into D4, we'll also 'skip'
the select button - The AES arcade machines do not have it
anyway.
Now we want to move that D button into our register - so we
shift it 8 bits to the left, and OR it into the correct
position in D4...
The last thing to do is ensure any unused bits are set to 1 -
if we're using the BIOS we need to flip all the bits, but if
we're not then we only flip the unused ones. |
 |
We're only
using two joysticks in these tutorials, but if that's not
enough you can theoretically have another have another 2
These are read in via the bios from $1OFDA2, $10FDA8...
Whether the emulators will actually support them in
another matter! |
 |
 |
Lesson
P10 - Cursor reading on the Sinclair QL
The Sinclair QL supports up to 2 joysticks via the CTL ports
(CTL1 and CTL2)... but these actually connect to the keyboard
keys!
Joystick 1 maps to the Cursor keys, and space for fire...
We're going to read the cursors, and use them as the same
joystick buttons on our other systems. |
 |

JoyTest.asm |
|
 |
Keyboard Layout
Reading in from the keboard has
to be done with Trap 1 - command 9
We have to send a sequence of command bytes - with byte 6 as
the row number - the trap will return a byte in D1 - with a
bit high when the button is down.
An example of the command is shown to the right - this example
will read in row
1
Joystick 1 (CTL1) uses Up, Down, Left,
Right and Space
Joystick 2 (CTL2) uses F4, F2, F1, F3
and F5 |
lea keycommand,a3
move.b #$11,d0
Trap #1
keycommand:
dc.b $09 ;0
- Command
dc.b $01
;1 - parameter bytes
dc.l 0 ;2345 - send option (%00=low nibble)
dc.b 1
;6 - Parameter:
Row
dc.b 2 ;7 - length of reply (%10=8 bits)
|
|
1
|
2
|
4
|
8
|
16
|
32
|
64
|
128
|
7
|
Shift |
Ctrl |
Alt |
X |
V |
/ |
N |
, |
6 |
8 |
2 |
6 |
Q |
E |
O |
T |
U |
5 |
9 |
W |
I |
Tab |
R |
- |
Y |
|
4 |
L |
3 |
H |
1 |
A |
P |
D |
J |
3 |
I |
Caps |
K |
S |
F |
= |
G |
; |
2 |
| |
Z |
. |
C |
B |
pound |
M |
~ |
1 |
Enter |
Left /
J1-L |
Up /
J1-U
|
Esc |
Right /
J1-R |
\ |
Space /
J1-F |
Down /
J1-D |
0 |
F4/ J2-U |
F1 /
J2-L |
5 |
F2 / J2-D |
F3 /
J2-R |
F5 /
J2-F |
4 |
7 |
 |
We're
only
going to read in one players controls in this example... we
could read in two players, but we would have the risk of
Keyclash..
When 3 buttons are pressed, that all make up corners of a
square , then a 4'th will 'fire' incorrectly...
EG: if buttons 1,3 and 2 are pressed then Q will fire even
though it wasn't pressed... ALL keyboards do this to some
extent due to the way they share lines. |
We're going to use a fixed read command, as we only need row
1 for our use, We'll use this with our Trap command later, |
 |
our 'Player_ReadControlsDual' function will read in the
input,
We're going to use Trap #1 to read in from the keyboard, we
need to pass the command we just defined to the trap in A3
The function will return the bits of the line in D1
We're going to use the bits in D1 to build up the result in D0 |
 |
We're going to move each of the
controls in one by one,
We'll use D2 as a temporary copy of the the register, and
shift the requred key bit into D0
We'll use Escape as a start button and Space,Enter and \ as
Fire 1-3, shifting them into D0
Finally we'll shift in all the bits of UDLR into D0 |
 |
Finally, we need to blank out D1 - as we're not reading a
second player,
We also need to flip the bits of D0 to make them 1 when a
button is not pressed, and 0 when it is. |
 |
All
of
our controls are being read from a single line... If you
wanted to use a key like TAB for start, you'd need to read
in from another line of the keymap with another TRAP
command...
Of course there's no reason you can't do this, but it would
take more code, and you'd have to start thinking about
keyclash |
 |
| |
Buy my Assembly programming book on Amazon in Print or Kindle!



Available worldwide! Search 'ChibiAkumas' on your local Amazon website!
Click here for more info!
|