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

the whole of rainbow

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

the whole of rainbow

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
}

the whole of machine

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
}

the whole of pinned

.section absolute at $0800
belowDos
        .res 1
.ends

the whole of pinned

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
  |   ^^^^^^

the whole of pinned

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

the whole of registers

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;
    }
  }
}

the whole of rainbow-in-c