The Atari Lynx uses the 65c02...
coupled with a 16 bit GPU, the Atari Lynx is was far more powerful
than the Gameboy and Sega Master system
Although it's large size, and high power draw made it a commercial
failure, it's an interesting system to develop for!
The official cartridges are encrypted, but we can make a binary that
the emulator Handy can run quite easily!
Unlike most other systems, the Lynx does not have a tile array, a
chunk of about 8k of internal memory is used for the screen, and we
can write bytes directly to it (like on the CPC)... the hardware
sprite function also writes to this buffer, so we have no effective
sprite limit!
There were two models of the atari
Lynx, but there's no real difference between them.
Cpu
4mhz 65C02
Ram
64k
Vram
Uses internal memory
Resolution
160x102
Sprites
Unlimited... blits sprites to buffer in system memory
While real cartridges are encrypted (causing copyright problems
for the hobbyist), the Handy emulator can work with an unencrypted O
file... this also means we do not need an official rom to run the
emulator!
There is a 10 byte header... FixedBytes:Many
of
the bytes are fixed, and should not be changed StartPoint:
Bytes 2 and 3 are the startpoint in Big Endian format.. . in our
example the first program byte is $0300 Length:
Bytes 4 and 5 are the length of the file.
Hardware Sprites
Unlike other systems, Lynx hardware sprites are not an extra layer! the
'Suzy' graphics chip draws the sprite into the Vram area of the 6502's
addressable range
This may leave you wondering why not just do our sprites in software with
the 6502... but the Suzy chip is VERY fast... it's a 16mhz 16 bit chip...
and can even do dynamic scaling of sprites!
Sprites for the Suzy chip have to be held in RAM, and need a 'Sprite control
block' to define the drawing of a sprite... this pointer is passed to the
Suzy chip to get it to draw a sprite
Sprites can be 'Literal' (plain bmp) or 'RLE compressed' (defined
by bit 7 of byte two of the SCB - SCBCTL1).... the colordepth is
defined in SPRCTL0 (See later)
Each line of a sprite starts with a byte - this an offset to the
next line... effectively the number of bytes in the line +1
.... effectively the pointer to the next line.
1 or 0 in this position have special meanings!... 0 means the end of
the sprite... 1 means the end of the 'quardrent'... note this is
optional! Akusprite does not use it!
Quadrent rendering is where the sprite is drawn in 4 sections from
the middle... with a 1 byte marking each 1/4 of the sprite...
(followed by another 'offset to next line' byte)
the first quadrent is DownRight (default)... the second quadrent is
UpRight
the third quadrent is UpLeft)... the fourth quadrent is DownLeft
Apparently there is a bug in the hardware - the last bit of each
line must be 0! - we should always have a 0 at the end of our
sprites to counter it - color 0 is transparent anyway!
You can see aLiteral Sprite to the right...
the Literal bitmap data is in green, and the header bytes are
in cyan
Literal
Sprite Example (BMP)
LynxSprite:
db $8,
$11, $11, $11, $11, $11, $10,0
db $8, $10,
$0, $0, $0, $0, $10,0
db $8,
$10, $04, $44, $44, $0, $10,0
db $8, $10,
$04, $3, $04, $0, $10,0
db $8, $10,
$04, $3, $04, $0, $10,0
db $8, $10,
$04, $44, $44, $0, $10,0
db $8,
$10, $0, $0, $0, $0, $10,0
db $8, $11,
$11, $11, $11, $11, $10,0
db 0
RLE Sprite
Data is a bit more tricky....
The first byte in a line is again an offset to the next line as
before
The next BIT will be a 'block definition'... defining what the
following data is...
1 marks that the next data will be LITERAL
0 marks that the next data will be RLE
The next 4 bits will be the number of pixels to
draw-1... so 0 means 1 pixel, and 15 means 16 pixels... we will call
this N
If the block is RLE
the next 1/2/3/4 bits (depending on bitdepth) will be used for the
color to fill the next N
pixels
If the block LITERAL
the next N
*(1/2/3/4) bits (depending on bitdepth) will be used for the color
of the next N pixels
the next bit will be the next 'block definition'... this pattern
repeats until the line is done.
4bpp
RLE Sprite Example db $8
(offset to next line)
db %01111000,%00000000
(RLE block...16 pixels...
Color 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0)
db %00001001,%10000000
(RLE block...2 pixles...
Color 3,3)
db %10010000,%10010001,%10000000
(Literal block...3
pixels... Color 1,2,3)
(next line starts here)
The Sprite Control Block
The sprite control block defines the
sprite onscreen, The first two bytes define the sprite type, how
it's drawn, and the other data in the block... note if RR<2
we don't need the Scale or Tilt words... so these can be
removed.
SPRCTL0 bits 7,6 defines the Colordepth 1/2/3/4 bits per pixel for
2/4/8/16 colors
SPRCTL1 bit 7 defines the sprite type 1=Literal... 0=RLE
Note... if you have the BPP wrong on a RLE sprite it will be a total
mess... a Literal sprite would just be stretched and a bit weird!
(colors would be sort of 'dithered')
The Xpos and Ypos are relative to the defined screen boundaries...
Wid and Hei are scales $100=100% $200=200%
The 'Palette' maps each 'color' in the sprite to a palette
setting... this is most useful for 1/2bpp sprites - where we will
need to select the colors each combination of bits will use. the
example shown is a 4bpp 16 color sprite
SCB: ;BBHV-TTT -
SPRCTL0... B=bits per pixel H=hflip V=vflip T=type (7=normal)
db %11000101
;LSRRPSUl - SPRCTL1... L=Literal S=Sizing choice (0 only!)
RR=Reloadable depth (1=Use Size 3=Use Size,ScaleTilt)
db %10010000
;P=Palette
reload (0=yes) s=skipsprite u=draw up l=draw left
db 0 ;- SPRCOL - 0= OFF
dw 0
;Next SCB (0=none)
dw LynxSprite
;Sprite
pointer
dw 10
;Xpos
dw 10
;Yos
dw $200 ;Wid ($100 = 100%)
dw $200 ;Hei ($100 = 100%) ; dw 0
;Scale - not needed if
B4,B5 of SPRCTL<3 ; dw 0
;Tilt - not needed if
B4,B5 of SPRCTL<2 ;Palette - maps nibbles to colors (useful for
<4 bpp)
db
$01,$23,$45,$67,$89,$AB,$CD,$EF
Sprite Drawing
Lets Draw a sprite... if our visible
screen is at &C000 - the following will work!
Note when setting 16-bit Suzy values, we must set the LSB before the
MSB... A write to the LSB will ZERO the
MSB automatically... so if we set FC09 first in this example it
WILL NOT WORK!
lda #$0
;MUST SET LSB FIRST!
;WRITE TO LSB will ZERO MSB
sta $FC08
;For sprites
lda #$C0
;Set screen ram pointer to $C000
sta $FC09
;For sprites
If we want our sprites to clip at the top left, we can set a
screen offset - for example to set an offset of 8:
LDA #8
STA $FC04
;SCR OFFSET
STA $FC06
;SCR OFFSET
Hardware Ports Memory Map
The Lynx memory map is pretty simple, the Zeropage is at $00xx , the Stack
is at $01xx....
addresses from $0200-$FC00 are free for ram... we need to allocate $2000 for
a screen buffer, which can be anywhere in ram we want (defined in address
$FD94-$FD95), but the rest is ours to use!
Note: Cartridge Rom is not memory mapped, it acts more like a 'disk drive'
which we have to access like an external system... which is annoying!
We need to use the hardware registers to control, and read from the devices
attached to the system, they are listed below:
From
To
Name
Description
Bits
Meaning
FC00
FC01
TMPADRL
Temporary
Address LH
FC02
FC03
TILTACUM
Accumulator
for tilt value LH
FC04
FC05
HOFF
Offert to H
edge of screen
FC06
FC07
VOFF
Offert to V
edge of screen
FC08
FC09
VIDBAS
Base address
of video build buffer
FC0A
FC0B
COLLBAS
Base address
of Coll build buffer
FC0C
FC0D
VIDADRL
Current Video
Build Addres
FC0E
FC0F
COLLARL
Current
Collision Build Address
FC10
FC11
SCBNEXT
Address of
next SCB
FC12
FC13
SPRDLINE
Start of
Sprite Data Line Address
FC14
FC15
HPOSSTRT
Starting Hpos
FC16
FC17
VPOSSTRT
Starting Vpos
FC18
FC19
SPRHSIZ
Hsize
FC1A
FC1B
SPRVSIZ
Vsize
FC1C
FC1D
STRETCH
H Size Adder
FC1E
FC1F
TILT
H Position
Adder
FC20
FC21
SPRDOFF
Offset to
next sprite data line
FC22
FC23
SPRVPOS
Current Vpos
FC24
FC25
COLLOFF
Offset to
collision depository
FC26
FC27
VSIZACUM
Vertical Size
Accumulator
FC28
FC29
HSIZOFF
Horizontal
size offset
FC2A
FC2B
VSIZOFF
Vertical Size
Offeet
FC2C
FC2D
SCBADR
Address of
current SCB
FC2E
FC2F
PROCADR
Current Spr
data Proc Address
FC80
FC80
SPRCTRL0
BBHV-TTT
B=bits per
pixel
H=hflip V=vflip
T=type (7=normal)
FC81
FC81
SPRCTRL1
LSRRPSUl
L=Literal
S=Sizing choice (0 only!)
RR=Reloadable depth
P=Palette reload (0=yes)
s=skipsprite u=draw up l=draw left