Targets
A raw memory image is not a file a machine will load. NGA writes the machine's
own format itself, with no packer to run afterwards, and the project asks for
it in a word: container xex is the Atari binary DOS reads.
modules { "rainbow.asm" }
container xex
COLBK = $D01A
COLPF2 = $D018
WSYNC = $D40A
VCOUNT = $D40B
.proc entry
@top lda VCOUNT
jne @top
ldx #0
@line sta WSYNC
stx COLBK
stx COLPF2
inx
cpx #240
jcc @line
jmp @top
.endp
This is the straightforward implementation of the classic Atari rainbow. The implementation is fine, but NGA applied on it default placement rules which restrict memory usage to $2000-$9fff.
NGA can be told about specific needs about the placement rules for a project using a target block. Within we can among others tell the allocator about the regions we require.
Regions
You tell it in a target block, one line per range.
target {
region $0080 .. $00FF ram
region $2000 .. $BFFF ram
}
ram is where the solver may put things and it's the only thing what is really needed to instruct the allocator.
reserved is where it may not, though you may still pin a section there with
at. Then the tool warns rather than refuses, because you may know something
the machine's description does not — and the finding names both lines.
target {
region $0080 .. $00FF ram
region dos $0700 .. $1FFF reserved
region $2000 .. $BFFF ram
}
.section absolute at $0800
belowDos
.res 1
.ends
warning[NGA5218]: `belowDos` is pinned at $800, in the reserved region `dos`
--> pinned.asm:2:1
|
2 | .section absolute at $0800
| ^^^^^^^^
note: the region `dos` is declared here
--> main.ngp:4:3
|
4 | region dos $0700 .. $1FFF reserved
| ^^^^^^
register is where it cannot. Pinning anything within a region register generates an error.
A region's name is a symbol every module sees, whose value is the address it
starts at, which is how a hardware register is declared. There is a shorter
form for that, and a two-byte register is written by adding , 2 after the
address.
region COLBK $D01A .. $D01A register
register COLPF2 $D018
register WSYNC $D40A
register VCOUNT $D40B
With that in the project's target block, the module stops declaring those four names. Redeclaration or shadowing registers is an error.
A name from a target block is reached the same way from C, with nothing
declared at all — no extern, and no volatile either. The Target's
registers are volatile whatever C says about them, so every read and every
write happens exactly where the source puts it. The type comes from the width:
one byte is a u8, two a u16, more a u8[N] at its address.
void entry()
{
for ( ;; )
{
while ( VCOUNT != 0 )
{
}
for ( u8 i = 0; i < 240; ++i )
{
WSYNC = 0;
COLBK = i;
COLPF2 = i;
}
}
}