in-c

A program of Slots.

main.ngp

include "atari/130xe.ngp"
include "atari/extension.ngp"

modules { "print.asm" "run.ngc" "intro.ngc" "level.ngc" }

panes in ext { level }

resident { print, run }

phase intro { needs intro  then level  entry introStart }
phase level { needs level             entry levelStart }
entry intro

container xex
130xe.ngp project
; An Atari 130XE: five Banks as a unit set — the four of extended memory and,
; last, the machine's own memory under the Window — the Window PORTB brings
; one of them into, every Region of the address space, and the Modules a
; program on this machine needs. It declares no storage. A program that loads
; anything during a run includes `atari/extension.ngp` beside this, which makes
; the Banks its storage. What PORTB takes to show a state is the
; driver's and not here, and the OS is two Modules rather than a Region because
; it occupies memory for a while rather than being a truth about addresses: its
; variables for the whole of a program, its ROM in every Phase that does not
; say `without osrom` — see 0229. The same register switches the ROMs, so the
; driver is what declares them.
target {
  cpu "6502"

  ; DOS loads a `.xex`, and a Project naming any other Container is refused
  ; here rather than at the writer.
  containers xex

  ; No Window has a base, so the memory under this one is a Bank like the
  ; others and nothing of the program stands there but a Pane or storage — see
  ; docs/decisions/0229-no-window-has-a-base.md.
  units  extension 5
  window ext  $4000 .. $7FFF  views extension
}

target {
  region ram    $0000 .. $CFFF  ram
  region        $D800 .. $FFFF  ram
  region stack  $0100 .. $01FF  reserved
  region io     $D000 .. $D7FF  register
  ; The RAM the OS ROM covers, named so that a Section may say `in underOsLow`
  ; and stand there: only in a Phase without `osrom`, or hidden under it where
  ; nothing needs it — see docs/decisions/0243-a-section-may-name-the-region-it-stands-in.md.
  region underOsLow   $C000 .. $CFFF  ram
  region underOsHigh  $D800 .. $FFFF  ram

  ; This machine takes a `.xex` and nothing else, and a `.xex` is loaded by a
  ; DOS, which stays where a DOS of the 2.x family stays. A Project whose DOS
  ; takes more says so with a `reserved` Region of its own, which narrows this
  ; one further.
  region dos    $0700 .. $1FFF  reserved

  ; What the driver writes. A Project that wants more of the hardware by name
  ; declares it, as it declares the memory its DOS holds.
  register PORTB  $D301

  ; The hardware that holds a Section while a program has handed it one, by
  ; `.root NAME` before the taking of its address, until it hands another or
  ; `.release NAME`: what it holds stays where it is for that long. See
  ; docs/decisions/0247-the-hardware-holds-a-section-for-as-long-as-it-is-given-it.md.
  holder dlist                          ; ANTIC's display list: SDLSTL, or DLISTL
  holder dli                            ; the display list interrupt: VDSLST
  holder vbi                            ; the immediate vertical blank: VVBLKI
  holder vbd                            ; the deferred vertical blank: VVBLKD
  holder nmi                            ; $FFFA, where the OS ROM is switched out
  holder irq                            ; $FFFE, where the OS ROM is switched out
  holder chbase                         ; the character set: CHBAS, or CHBASE
  holder pmbase                         ; the player-missile memory: PMBASE
  holder dosini                         ; DOSINI: called once a boot returns, and on every reset
}

; The OS and the driver, listed as a file of the Project's own would be. The
; driver is here and not beside the storage because it does more than read it:
; it shows a Pane's Bank and switches the ROMs, with or without storage. BASIC
; is not listed: a program that wants it lists `atari/basic.asm` and names it
; in a Phase's `needs`, and the driver switches it out everywhere else.
modules {
  "atari/osram.asm"  "atari/osrom.asm"  "atari/mathpack.asm"
  "atari/charsets.asm"  "atari/portb.asm"
}
resident { osram, osrom, mathpack, charsets, portb }
extension.ngp project
; Storage in a 130XE's extended memory, for an `atari/130xe.ngp` program: the
; Banks of the unit set `extension`, read through the Window `ext`, and the
; decoders the tool ships. A Project includes this beside the machine. The
; driver is the machine's, since it shows a Pane's Bank whether or not there is
; storage.
storage { units extension }

; Every Transition calls the decoders, so they are resident; a decoder nothing
; uses is dropped.
modules { "stream/zx0.asm" }
resident { zx0 }
osram.asm osram
; The memory the Atari OS keeps its variables in: half the zero page and the
; pages from $0200 to $06FF. Sections that hold no bytes, pinned where the OS
; keeps them, so the solver sees memory that is taken rather than a Region it may
; never allocate from. See 0038 for why what occupies memory for a while is a
; Module, and 0229 for why the OS is two of them.
;
; **Resident wherever the OS is used at all**, and not only in the Phases whose
; ROM is in: a Phase without `atari/osrom.asm` runs with the ROM switched out and
; the OS asleep, and the variables have to be as the OS left them when a later
; Phase switches it back in.
;
; Every Section here is `root`: the OS and the hardware reach them with no
; Reference in any Chunk, so nothing else would keep them. None holds bytes, so
; none has a Payload and no Transition loads one. This is the OS as an XL or XE
; ships it; a machine whose OS lives elsewhere declares a Module of its own.

.export RTCLOK, SDMCTL, SDLSTL, SDLSTH, CH

; The OS's half of the zero page. The program's own variables live above it,
; in the half the variant leaves to the solver.
.section zeropage at $0000, root
osZero
        .res $12
RTCLOK  .res 3                          ; $0012: the frame counter, three bytes
        .res $6B
.ends

; The OS's variables and buffers. The shadow registers are among them and
; are Labels here rather than registers of the variant: they are the OS's
; memory, which the OS copies to the hardware on every vertical blank, and
; a `register` Region inside this Section would be an address the Section
; may not cover.
.section absolute at $0200, root
osRam
        .res $2F
SDMCTL  .res 1                          ; $022F: shadows DMACTL
SDLSTL  .res 1                          ; $0230: the display list address, low
SDLSTH  .res 1                          ; $0231: and high
        .res $CA
CH      .res 1                          ; $02FC: the last key pressed, $FF for none
        .res $403
.ends
osrom.asm osrom
; The Atari OS ROM, in the two ranges the hardware registers leave between them,
; and the entry points a program calls in the second. Sections that hold no
; bytes, pinned where the ROM is, so that a Phase that needs this Module has
; the ranges taken and one without it has the RAM underneath — see 0229.
;
; **A ROM the driver switches out.** A variant lists this Module in `resident`,
; so every Phase has the OS unless it says `without osrom`, which the driver's
; `.driver rom osrom` is what allows; a Phase without it has the RAM under the
; ROM, its own vectors at $FFFA to look after, and no call into the OS — a
; `jsr` to an entry point below is a Reference to a Section that Phase does not
; have, and refused as one.
;
; The high range begins at **$E000 and not at $D800**: $D800-$DFFF is the
; floating-point pack, which a device on the parallel bus maps its own ROM
; over once it is selected, so it is `atari/mathpack.asm` — a Module a variant
; may leave out, and one whose absence is a fact about the machine rather than
; an omission. A variant that lists this file lists that one too, unless it
; has something else to say about those addresses.
;
; Every Section is `root`, since the OS and the hardware reach them with no
; Reference in any Chunk. This is the ROM an XL or XE ships.

.export SETVBV, XITVBV

.section absolute at $C000, root
osRomLow
        .res $1000
.ends

.section absolute at $E000, root
osRomHigh
        .res $45C
SETVBV  .res 3                          ; $E45C: A = the stage, X/Y = the routine
        .res 3
XITVBV  .res 3                          ; $E462: the end of a deferred routine
        .res $1B9B
.ends
mathpack.asm mathpack
; The floating-point routines the OS keeps at $D800-$DFFF, as a Section that
; holds no bytes, so that the solver sees memory that is taken.
;
; **A Module of its own and not part of `atari/osrom.asm`**, because this is the
; one range of the OS that something else can take away. A device on the
; parallel bus maps its ROM over the whole of it once it is selected — the
; kernel's own PBI scan says so in as many words, and looks for a device's
; identifying bytes at $D803 and $D80B, which is why the math pack must not
; begin with them. A variant for a machine whose PBI device is selected for
; the life of the program therefore does not list this file, and says what
; stands there instead.
;
; **Switched out with the OS.** On an XL the one bit of PORTB that takes the OS
; ROM out takes this out too, so a driver that switches ROMs declares it as
; `.driver rom mathpack osrom`: a Phase that needs this needs the OS's ranges
; taken as well, and cannot do without `osrom` — see 0229.
;
; Nothing is exported. The pack's entry points are documented by Atari and a
; program that calls one declares it, as it declares any address of the OS
; `atari/osrom.asm` does not name; nothing the tool ships calls into it.
;
; `root`, because the OS reaches it with no Reference in any Chunk of the
; program, and holding no bytes it has no Payload and no Transition loads it.

.section absolute at $D800, root
mathPack
        .res $800
.ends
charsets.asm charsets
; The Atari's two codes for the same letters, as Charsets.
;
; `atascii` is what the character I/O takes: the machine's own code, with the
; graphics characters where an ASCII machine keeps its control codes, and the
; letters where ASCII has them — which is why an unprefixed literal of plain
; letters has always worked and why nothing else has. `\n` is `$9B`, the end of
; line, and not `$0A`.
;
; `screen` is what the display reads out of screen memory, which is the same
; letters at other numbers. A program that writes where the display looks
; rather than through the OS wants this one.
;
; The graphics characters are written here as the Unicode the box-drawing and
; block characters have, so a picture drawn in the source is the picture the
; machine draws. A letter in inverse video is one of those too — Unicode squares
; them off in a negative, and `atascii"PRESS 🆂"` is seven bytes with the last
; one inverse. A whole line of inverse text is better asked of a Charset
; derived from this one, `.charset bright : atascii ^ $80`, which is what a
; derivation is for. Two of them have no exact Unicode: `$02` and `$0D` are a
; quarter of a cell where the nearest character is an eighth, and are written
; as `▕` and `▔`. Everything else, the inverse entries included, was read off
; the machine's own font and matches it.
;
; This Module emits nothing: it is two names and two tables.

.export atascii, screen

; tag atascii
.charset atascii
  "♥├▕┘┤┐╱╲◢▗◣▝▘▔▂▖♣┌─┼●▄▎┬┴▌└"                                       = $00
  "↑↓←→"                                                              = $1C
  " !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_" = $20
  "♦"                                                                 = $60
  "abcdefghijklmnopqrstuvwxyz"                                        = $61
  "♠"                                                                 = $7B
  "│"                                                                 = $7C
  "\n"                                                                = $9B

  ; Inverse video is the same glyph with the bits the other way round, which
  ; for the blocks and the triangles is another character Unicode draws. Every
  ; one of these was read off the machine's own font and is exact; the rest of
  ; the inverse half has no glyph to be written as, and is reached by adding
  ; $80 or by a Charset derived from this one.
  "▊" = $82        ; ▕
  "◤" = $88        ; ◢
  "▛" = $89        ; ▗
  "◥" = $8A        ; ◣
  "▙" = $8B        ; ▝
  "▟" = $8C        ; ▘
  "▆" = $8D        ; ▔
  "▜" = $8F        ; ▖
  "▀" = $95        ; ▄
  "▐" = $99        ; ▌
  "█" = $A0        ; the space

  ; And the letters, which Unicode squares off in a negative.
  "🅰🅱🅲🅳🅴🅵🅶🅷🅸🅹🅺🅻🅼🅽🅾🅿🆀🆁🆂🆃🆄🆅🆆🆇🆈🆉" = $C1
.endch
; end atascii

.charset screen
  " !\"#$%&'()*+,-./0123456789:;<=>?"                                 = $00
  "@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_"                                 = $20
  "♥├▕┘┤┐╱╲◢▗◣▝▘▔▂▖♣┌─┼●▄▎┬┴▌└"                                       = $40
  "♦"                                                                 = $60
  "abcdefghijklmnopqrstuvwxyz"                                        = $61
  "♠"                                                                 = $7B
  "│"                                                                 = $7C

  ; The same inverses, at the display's numbers.
  "▊" = $C2
  "◤" = $C8
  "▛" = $C9
  "◥" = $CA
  "▙" = $CB
  "▟" = $CC
  "▆" = $CD
  "▜" = $CF
  "▀" = $D5
  "▐" = $D9
  "█" = $80
  "🅰🅱🅲🅳🅴🅵🅶🅷🅸🅹🅺🅻🅼🅽🅾🅿🆀🆁🆂🆃🆄🆅🆆🆇🆈🆉" = $A1
.endch
portb.asm portb
; The storage driver for extended memory behind PORTB: the units are the
; Banks of the variant's `extension`, which a value written to PORTB brings
; into the $4000-$7FFF window — `ext` in the variant — and the stream is a
; pointer into that window that walks on to the next unit at its end. Each
; role is a macro declared with `.driver`, which the routine, the loader's
; glue, the decoders and the tool's own Procs expand where they use it as
; `nga.open`, `nga.read`, `nga.show` and `nga.showAt`. What the hardware
; calls each state of a Window is this Module's alone: `portbValues` holds
; a PORTB value per state of `ext`, one per Bank, as many as the variant's
; `extension` counts — the last of the 130XE's five being the machine's own
; memory, which no Window keeps as a base — see 0229. `PORTB` is the variant's
; register.
;
; **The same register holds the OS ROM and BASIC**, bits 0 and 1, so this
; driver is also what switches them: `.driver rom` names the Modules that are
; those ROMs, and `select` is handed the set a Phase needs and writes the two
; bits, which every switch of a Bank keeps as they are — see 0229.
;
; Resident, and outside the window — the variant lists this Module in
; `resident`, and the tool holds it outside the window the stream reads
; through.

portbWindow    = $4000
portbWindowEnd = portbWindow + $4000    ; one past the window: the high byte of the first address outside it
portbUnitPages = $40                    ; a unit, in pages: what an offset carries by

.driver open    portbOpen
.driver read    portbRead
.driver unit    portbUnit
.driver offset  portbPtr
.driver stream  ext
.driver show    ext portbShow
.driver showAt  ext portbShowAt
.driver init    portbInit
.driver finalize portbFinalize
.driver rom     osrom                   ; bit 0 of the set
.driver rom     mathpack osrom          ; bit 1, which one bit of PORTB shows with the OS
.driver rom     basic                   ; bit 2
.driver select  portbRoms
.driver mask    portbMask
.driver unmask  portbUnmask

.transform copy portbCopy

; PORTB as the program found it, kept by `init` and put back by `finalize`: the
; bits of BASIC and of the self-test are the user's, whatever the program does
; with them meanwhile, and a DOS returned to expects its machine as it left it
; — see docs/decisions/0229-no-window-has-a-base.md.
.macro portbInit
        lda PORTB
        sta portbFound
.endm

.macro portbFinalize
        lda portbFound
        sta PORTB
.endm

.section
portbFound      .res 1
.ends

; What the load wraps a switch of every ROM out in, while the DOS's interrupts
; are still on: with the OS out, $FFFA is RAM the load is writing. IRQ masked,
; and NMI, which `sei` does not reach, off at ANTIC; then back as the OS keeps
; it, the vertical blank on — see
; docs/decisions/0241-bytes-kept-for-a-later-phase-may-lie-hidden-under-a-rom.md.
NMIEN = $D40E

.macro portbMask
        sei
        lda #0
        sta NMIEN
.endm

.macro portbUnmask
        lda #$40
        sta NMIEN
        cli
.endm

.macro portbOpen
        jsr portbOpenStream
.endm

.macro portbRead
        jsr portbReadByte
.endm

; The state to show, as the tool numbers ext's states: Bank n is n.
.macro portbShow state
        ldx #state
        jsr portbBank
.endm

; X = the state to show.
.macro portbShowAt
        jsr portbBank
.endm

; A = the set of ROMs the Phase needs.
.macro portbRoms
        jsr portbSelectRoms
.endm

; PORTB per state of ext, one value per Bank. The list runs to the 130XE's
; five — four of extended memory, and then $FF, the machine's own with the
; self-test out — and the variant's count trims it, so that a variant with
; fewer Banks takes a prefix; one with more lists its values here. Every value
; has bits 0 and 1 set, which is what lets portbBank keep the ROMs' bits as
; they are with one `and` and no scratch byte.
.macro portbBankValues n, values...
.if n > 0
.match values
.case first, rest...
        .byte first
        portbBankValues n - 1, rest...
.endmatch
.endif
.endm

; The table portbBank indexes, in a Section of its own: what has an
; address stands in one, and a Proc holds code alone.
.section
portbValues
        portbBankValues extension, $E3, $E7, $EB, $EF, $FF
.ends

; The stream: where the next byte is, and which unit is in. A plain zero-page
; Section rather than Temporaries, because the value has to survive between
; one call and the next while nothing here is running. They are also the
; cells the routine writes a position into before it opens the stream,
; `portbPtr` the offset and `portbUnit` the unit.
.section zeropage
portbPtr        .res 2
portbUnit       .res 1
.ends

; X = the state of ext to show: its PORTB value, with the bits of the OS ROM
; and of BASIC left as they are, since those are what `select` said. Writes no
; memory, so that a loader running it leaves nothing behind but the register.
.proc portbBank
        lda PORTB
        ora #$FC
        and portbValues,x
        sta PORTB
        rts
.endp

; A = the set of ROMs the Phase entered needs: bit 0 `osrom`, bit 1 `mathpack`,
; bit 2 `basic`. Bit 0 of PORTB is set for the OS ROM and the floating-point
; pack, which one bit shows together, and bit 1 is clear for BASIC; the bank
; bits are left as they are. Writes no memory, the set waiting on the stack
; while the register is worked out.
.proc portbSelectRoms
        pha
        lda PORTB
        ora #$03                        ; the OS in and BASIC out, to begin with
        tax
        pla
        pha
        and #$03                        ; neither osrom nor mathpack: the OS out
        bne @os
        txa
        and #$FE
        tax
@os
        pla
        and #$04                        ; basic: BASIC in
        beq @basic
        txa
        and #$FD
        tax
@basic
        stx PORTB
        rts
.endp

; The stream to the position in portbUnit and portbPtr. An offset past the
; unit's end carries into the units after, since the routine counts on in a
; Frame without knowing where a unit ends: a unit is $4000 bytes, so the
; offset's top two bits are units.
.proc portbOpenStream
        lda portbPtr+1
@carry
        cmp #portbUnitPages
        bcc @within
        sbc #portbUnitPages
        inc portbUnit
        jmp @carry
@within
        clc
        adc #>portbWindow
        sta portbPtr+1
        ldx portbUnit
        jsr portbBank
        rts
.endp

; A = the next byte of the stream. X and Y are not preserved.
.proc portbReadByte
        ldy #0
        lda (portbPtr),y
        inc portbPtr
        bne @done
        inc portbPtr+1
        ldy portbPtr+1
        cpy #>portbWindowEnd
        bne @done
        pha
        jsr portbNextUnit
        pla
@done
        rts
.endp

; The window's end: the next unit in, and the pointer back at its start.
.proc portbNextUnit
        inc portbUnit
        ldx portbUnit
        jsr portbBank
        lda #0
        sta portbPtr
        lda #>portbWindow
        sta portbPtr+1
        rts
.endp

;  The decoder of `copy`: X/Y = the destination, the stream at the stored
; size and then the bytes. Reads the window through the pointer rather than
; through portbReadByte, which is what a driver's own decoder is for: a run at a
; time, where a run ends at the source's page end or at the size, so that
; the inner loop is `(zp),y` down to zero — the window's end is a page end,
; so a unit is never crossed inside a run.
portbDst  .ztemp 2
portbSize .ztemp 2
portbRun  .ztemp 1                      ; bytes in the run, zero for 256

.proc portbCopy
        stx portbDst
        sty portbDst+1
        jsr portbReadByte
        sta portbSize
        jsr portbReadByte
        sta portbSize+1
@run
        lda portbSize
        ora portbSize+1
        beq @done
        lda portbPtr                    ; to the end of the source's page
        eor #$FF
        clc
        adc #1
        sta portbRun
        lda portbSize+1
        bne @copy                       ; at least a page left: the run stands
        lda portbRun
        beq @cap                        ; a whole page, and less than one left
        cmp portbSize
        bcc @copy
@cap
        lda portbSize
        sta portbRun
@copy
        ldy portbRun
@byte
        dey
        lda (portbPtr),y
        sta (portbDst),y
        tya
        bne @byte
        ldx portbRun                    ; the run, as 256 where it is zero
        bne @counted
        inc portbPtr+1
        inc portbDst+1
        dec portbSize+1
        jmp @crossed
@counted
        txa
        clc
        adc portbPtr
        sta portbPtr
        bcc @source
        inc portbPtr+1
@source
        txa
        clc
        adc portbDst
        sta portbDst
        bcc @destination
        inc portbDst+1
@destination
        sec
        lda portbSize
        stx portbRun
        sbc portbRun
        sta portbSize
        bcs @crossed
        dec portbSize+1
@crossed
        lda portbPtr+1
        cmp #>portbWindowEnd
        bne @run
        jsr portbNextUnit               ; the window's end: the next unit in
        jmp @run
@done
        rts
.endp
zx0.asm zx0
; The decoder of `zx0` over any driver's stream: Einar Saukas's ZX0, version
; 2's standard forward stream, decoded as the reference dzx0.c does, driven
; by the stream's own end marker. Written here from the format, not ported:
; nothing of the reference's text is in it, and it is licensed as everything
; under lib/ is, see lib/LICENSE. Entered with X/Y = the destination and the
; stream at the stored size, which it reads past. A match is copied a byte
; at a time, forward, from what was written, which is what makes an offset
; shorter than its length — the run — come out right. Resident, and outside
; the driver's window.
;
;   zx0Bits      the bit buffer, a sentinel one above the bits still unread
;   zx0Offset    the last offset
;   zx0Length    the length in hand, or an offset's MSB while one is read
;   zx0Invert    one while an offset's MSB is read, whose data bits the
;                stream carries complemented; zero otherwise

.transform zx0 zx0Decode

zx0Dst    .ztemp 2
zx0Src    .ztemp 2
zx0Bits   .ztemp 1
zx0Offset .ztemp 2
zx0Length .ztemp 2
zx0Invert .ztemp 1

; The next bit of the stream, in A as zero or one and in the Z flag. The
; buffer holds a sentinel above the unread bits, so shifting it to nothing is
; the signal to fetch the next byte and put the sentinel back below it.
.proc zx0Bit
        asl zx0Bits
        bne @have
        nga.read
        sec                     ; the sentinel the asl shifted out, which `read` may not keep
        rol
        sta zx0Bits
@have
        lda #0
        rol
        rts
.endp

; An interlaced Elias gamma value into zx0Length: a control bit says whether
; a data bit follows, and the value begins at one. Three Procs chained by
; `then` because the value after an offset is entered with its first control
; bit already read, at zx0EliasData: the first falls through into the
; second, and the second branches into the third, which the chain is what
; allows.
.proc zx0Elias
        lda #1
        sta zx0Length
        lda #0
        sta zx0Length+1
.endp then zx0EliasMore
.proc zx0EliasMore
        jsr zx0Bit
        beq zx0EliasData        ; zero: a data bit follows
        rts
.endp then zx0EliasData
.proc zx0EliasData
        jsr zx0Bit
        eor zx0Invert
        lsr
        rol zx0Length
        rol zx0Length+1
        jmp zx0EliasMore
.endp

; One byte written: the destination moves on and the length in hand comes
; down, leaving Z set when it reaches zero.
.proc zx0Step
        inc zx0Dst
        bne @moved
        inc zx0Dst+1
@moved
        lda zx0Length
        bne @low
        dec zx0Length+1
@low
        dec zx0Length
        lda zx0Length
        ora zx0Length+1
        rts
.endp

; The length in hand copied from zx0Dst less the last offset to zx0Dst.
.proc zx0Copy
        sec
        lda zx0Dst
        sbc zx0Offset
        sta zx0Src
        lda zx0Dst+1
        sbc zx0Offset+1
        sta zx0Src+1
@byte
        ldy #0
        lda (zx0Src),y
        sta (zx0Dst),y
        inc zx0Src
        bne @from
        inc zx0Src+1
@from
        jsr zx0Step
        bne @byte
        rts
.endp

.proc zx0Decode
        stx zx0Dst
        sty zx0Dst+1
        nga.read             ; the stored size, which the end marker makes unnecessary
        nga.read
        lda #$80
        sta zx0Bits             ; an empty buffer: the sentinel alone
        lda #1
        sta zx0Offset
        lda #0
        sta zx0Offset+1         ; the last offset begins at one
        sta zx0Invert
@literals
        jsr zx0Elias
@literal
        nga.read
        ldy #0
        sta (zx0Dst),y
        jsr zx0Step
        bne @literal
        jsr zx0Bit
        bne @offset
        jsr zx0Elias            ; a match at the last offset
        jsr zx0Copy
        jsr zx0Bit
        beq @literals
@offset
        lda #1
        sta zx0Invert
        jsr zx0Elias            ; the new offset's MSB, complemented in the stream
        lda #0
        sta zx0Invert
        lda zx0Length
        beq @done               ; 256 is the end marker, and the one value with a low byte of zero
        lsr                     ; offset = MSB * 128 - LSB / 2
        sta zx0Offset+1
        lda #0
        ror
        sta zx0Offset
        nga.read
        lsr                     ; the LSB's low bit is the length's first control bit
        php
        eor #$FF
        sec
        adc zx0Offset
        sta zx0Offset
        bcs @subtracted
        dec zx0Offset+1
@subtracted
        plp
        lda #1
        sta zx0Length
        lda #0
        sta zx0Length+1
        bcs @counted            ; a control bit of one: the value is one
        jsr zx0EliasData
@counted
        inc zx0Length           ; a match at a new offset is one longer than written
        bne @copy
        inc zx0Length+1
@copy
        jsr zx0Copy
        jsr zx0Bit
        bne @offset
        jmp @literals
@done
        rts
.endp

print.asm

; The one thing both Phases need: a line on the screen, through the character
; I/O the OS opens on channel 0 before a program starts. `PUTREC` writes a
; record, so it ends the line itself.

CIOV   = $E456
ICCOM  = $0342
ICBAL  = $0344
ICBLL  = $0348
PUTREC = 9

; Both Phases call it, so both have to see the name.
.export printLine

; Where the line stands, and how long it is. Declaring where the two arguments
; are is what lets a Module of C call this one as a function.
.proc printLine
        .declare arg xy
        .declare arg a

        stx ICBAL
        sty ICBAL+1
        sta ICBLL
        lda #0
        sta ICBLL+1
        lda #PUTREC
        sta ICCOM
        ldx #0
        jsr CIOV
        rts
.endp

run.ngc

[[slot]] extern u8* const title;
[[slot]] extern void draw();

void showScreen()
{
  u8 left = 0;
  while ( title[left] != 0 )
    left += 1;
  printLine( (u16)title, left );
  draw();
}
run, as the tool compiled it run
.source "run.ngc", 1
.export title
.slot title, pointer, zeropage

.source "run.ngc", 2
.export draw
.slot draw, vector

.source "run.ngc", 4
.export showScreen
.proc showScreen
        .source "run.ngc", 6
        lda #0
        sta __0left
@l1
        .source "run.ngc", 7
        ldy __0left
        lda (title),y
        jeq @l3
        .source "run.ngc", 8
        inc __0left
        .source "run.ngc", 7
        jmp @l1
@l3
        .source "run.ngc", 9
        ldx title
        ldy title+1
        lda __0left
        jsr printLine
        .source "run.ngc", 10
        jmp draw
__0left .ztemp 1
.endp

intro.ngc

[[implements(title)]] static const u8 introTitle[] = "INTRO";

static const u8 introText[] = "THE FIRST SCREEN";

[[implements(draw)]] void introDraw()
{
  printLine( (u16)introText, sizeof( introText ) - 1 );
}

void introStart()
{
  showScreen();
  [[transition(level)]] return;
}
intro, as the tool compiled it intro
.source "intro.ngc", 1
.section readonly
introTitle
        .byte 73, 78, 84, 82, 79, 0
.ends
.implements title, introTitle

.source "intro.ngc", 3
.section readonly
introText
        .byte 84, 72, 69, 32, 70, 73, 82, 83
        .byte 84, 32, 83, 67, 82, 69, 69, 78
        .byte 0
.ends

.source "intro.ngc", 5
.export introDraw
.proc introDraw
        .source "intro.ngc", 7
        ldx #<introText
        ldy #>introText
        lda #16
        jmp printLine
.endp
.implements draw, introDraw

.source "intro.ngc", 10
.export introStart
.proc introStart
        .source "intro.ngc", 12
        jsr showScreen
        .source "intro.ngc", 13
        .transition level
.endp

level.ngc

[[implements(title)]] static const u8 levelTitle[] = "LEVEL ONE";

[[in(level)]] static const u8 levelMap[] =
  "#####################################"
  "#...................................#"
  "#...................................#"
  "#......########.....................#"
  "#......#......#.....................#"
  "#......#......#.....................#"
  "#......########.....................#"
  "#...................................#"
  "#...................................#"
  "#####################################";

[[implements(draw)]] void levelDraw()
{
  [[with(level)]]
  {
    u16 at = (u16)levelMap;
    for ( u8 left = 10; left != 0; --left )
    {
      printLine( at, 37 );
      at += 37;
    }
  }
}

void levelStart()
{
  showScreen();
  for ( ;; )
  {
  }
}
level, as the tool compiled it level
.source "level.ngc", 1
.section readonly
levelTitle
        .byte 76, 69, 86, 69, 76, 32, 79, 78
        .byte 69, 0
.ends
.implements title, levelTitle

.source "level.ngc", 3
.section in level, readonly
levelMap
        .byte 35, 35, 35, 35, 35, 35, 35, 35
        .byte 35, 35, 35, 35, 35, 35, 35, 35
        .byte 35, 35, 35, 35, 35, 35, 35, 35
        .byte 35, 35, 35, 35, 35, 35, 35, 35
        .byte 35, 35, 35, 35, 35, 35, 46, 46
        .byte 46, 46, 46, 46, 46, 46, 46, 46
        .byte 46, 46, 46, 46, 46, 46, 46, 46
        .byte 46, 46, 46, 46, 46, 46, 46, 46
        .byte 46, 46, 46, 46, 46, 46, 46, 46
        .byte 46, 35, 35, 46, 46, 46, 46, 46
        .byte 46, 46, 46, 46, 46, 46, 46, 46
        .byte 46, 46, 46, 46, 46, 46, 46, 46
        .byte 46, 46, 46, 46, 46, 46, 46, 46
        .byte 46, 46, 46, 46, 46, 46, 35, 35
        .byte 46, 46, 46, 46, 46, 46, 35, 35
        .byte 35, 35, 35, 35, 35, 35, 46, 46
        .byte 46, 46, 46, 46, 46, 46, 46, 46
        .byte 46, 46, 46, 46, 46, 46, 46, 46
        .byte 46, 46, 46, 35, 35, 46, 46, 46
        .byte 46, 46, 46, 35, 46, 46, 46, 46
        .byte 46, 46, 35, 46, 46, 46, 46, 46
        .byte 46, 46, 46, 46, 46, 46, 46, 46
        .byte 46, 46, 46, 46, 46, 46, 46, 46
        .byte 35, 35, 46, 46, 46, 46, 46, 46
        .byte 35, 46, 46, 46, 46, 46, 46, 35
        .byte 46, 46, 46, 46, 46, 46, 46, 46
        .byte 46, 46, 46, 46, 46, 46, 46, 46
        .byte 46, 46, 46, 46, 46, 35, 35, 46
        .byte 46, 46, 46, 46, 46, 35, 35, 35
        .byte 35, 35, 35, 35, 35, 46, 46, 46
        .byte 46, 46, 46, 46, 46, 46, 46, 46
        .byte 46, 46, 46, 46, 46, 46, 46, 46
        .byte 46, 46, 35, 35, 46, 46, 46, 46
        .byte 46, 46, 46, 46, 46, 46, 46, 46
        .byte 46, 46, 46, 46, 46, 46, 46, 46
        .byte 46, 46, 46, 46, 46, 46, 46, 46
        .byte 46, 46, 46, 46, 46, 46, 46, 35
        .byte 35, 46, 46, 46, 46, 46, 46, 46
        .byte 46, 46, 46, 46, 46, 46, 46, 46
        .byte 46, 46, 46, 46, 46, 46, 46, 46
        .byte 46, 46, 46, 46, 46, 46, 46, 46
        .byte 46, 46, 46, 46, 35, 35, 35, 35
        .byte 35, 35, 35, 35, 35, 35, 35, 35
        .byte 35, 35, 35, 35, 35, 35, 35, 35
        .byte 35, 35, 35, 35, 35, 35, 35, 35
        .byte 35, 35, 35, 35, 35, 35, 35, 35
        .byte 35, 35, 0
.ends

.source "level.ngc", 15
.export levelDraw
.proc levelDraw
        .source "level.ngc", 17
        .with level
        __w0
        .source "level.ngc", 26
        rts
__0at .ztemp 2
__1left .ztemp 1
.endp
.implements draw, levelDraw

.macro __w0
        .source "level.ngc", 19
        lda #<levelMap
        sta levelDraw.__0at
        lda #>levelMap
        sta levelDraw.__0at+1
        .source "level.ngc", 20
        lda #10
        sta levelDraw.__1left
@l2
        lda levelDraw.__1left
        jeq @l5
        .source "level.ngc", 22
        ldx levelDraw.__0at
        ldy levelDraw.__0at+1
        lda #37
        jsr printLine
        .source "level.ngc", 23
        clc
        lda levelDraw.__0at
        adc #37
        sta levelDraw.__0at
        bcc @i0
        inc levelDraw.__0at+1
@i0
        .source "level.ngc", 20
        dec levelDraw.__1left
        jmp @l2
@l5
.endm

.source "level.ngc", 28
.export levelStart
.proc levelStart
        .source "level.ngc", 30
        jsr showScreen
@l1
        .source "level.ngc", 31
        jmp @l1
.endp
transition.asm nga.transition
ngaFramesHeld = 0
ngaUnitBytes = 1
ngaEntrySize = 4
ngaBlockSize = 6
ngaFrameHeader = 3 + ngaSelects
; The Transition routine. Called by `jsr` from a `.transition`, with the
; statement's list right behind the call: the Phase to enter, and per Phase
; the code may be in, the unit and offset where the edge's Frame waits.
; nga.open and nga.read are the driver's roles, macros of the driver's
; Module expanded here, and nga.unit and nga.offset the cells its stream
; stands in, which open reads the position from; and ngaCurrentPhase is the
; Cell the tool generates. Everything else the edge has to say waits in the
; Frame, in storage. See docs/spec/transition.md.
;
; Its zero page is Temporaries: nothing of it is needed once the routine has
; jumped to the entered Phase's entry, and a `.transition` is a jump into the
; routine, so whatever the statement's Section had on the zero page is dead by
; then too. Two Temporaries never live at once share an address — see
; docs/decisions/0034-trace.md.

.export ngaTransition

ngaPtr .ztemp 2
ngaEntry .ztemp 2
ngaDst .ztemp 2
ngaValue .ztemp 2
ngaOffset .ztemp 2
ngaFrameAt .ztemp 2 + ngaUnitBytes ; where the Frame's stream stands: the offset, then the unit
ngaWanted .ztemp 1
ngaCount .ztemp 1
ngaUnit .ztemp ngaUnitBytes

.proc ngaTransition
        pla
        sta ngaPtr
        pla
        sta ngaPtr+1            ; the return address: one below the statement's list
        ldy #1
        lda (ngaPtr),y
        sta ngaWanted           ; the Phase to enter
        iny
        lda (ngaPtr),y
        sta ngaCount            ; entries that follow
        lda ngaPtr
        clc
        adc #3
        sta ngaPtr
        bcc @scan
        inc ngaPtr+1
@scan
        lda ngaCount
        bne @check
        brk                     ; no entry for the current Phase: unreachable while the static rule holds
@check
        ldy #0
        lda (ngaPtr),y
        cmp ngaCurrentPhase
        beq @found
        lda ngaPtr              ; the next entry
        clc
        adc #ngaEntrySize
        sta ngaPtr
        bcc @skipped
        inc ngaPtr+1
@skipped
        dec ngaCount
        jmp @scan
@found
        iny
        lda (ngaPtr),y
        sta ngaFrameAt+2        ; the unit the Frame waits in
.if ngaUnitBytes > 1
        iny
        lda (ngaPtr),y
        sta ngaFrameAt+3
.endif
        iny
        lda (ngaPtr),y
        sta ngaFrameAt
        iny
        lda (ngaPtr),y
        sta ngaFrameAt+1        ; and where in it
.endp then ngaEnter

; The rest of it, and the way in for a Container that has no current Phase to
; look one up by: ngaWanted holds the Phase to enter and ngaFrameAt where
; its Frame waits. The cold start comes here — see
; docs/decisions/0216-a-car-names-its-format-and-the-cold-start-is-an-edge.md.
.export ngaEnter
.proc ngaEnter
        jsr ngaFrameOpen
.if ngaFramesHeld
        nga.read
        sta ngaPtr
        nga.read
        sta ngaPtr+1            ; where the descriptors are to be held, or zero
.endif
        nga.read
        sta ngaEntry
        nga.read
        sta ngaEntry+1
        nga.read
        sta ngaCount            ; blocks that follow
.if ngaSelects
        nga.read                ; the ROMs the entered Phase needs, switched
        nga.select              ; before a byte of it lands
.endif
        lda #ngaFrameHeader
        jsr ngaFrameSkip
.if ngaFramesHeld
        lda ngaPtr
        ora ngaPtr+1
        beq @block              ; nowhere to hold them: one open a block

; A block's bytes, read in one pass from where the stream already stands. The
; write pointer, the counter and the index are Temporaries of the routine's own
; that nothing is keeping here: ngaValue belongs to the Cell writes, which come
; after the blocks, and ngaUnit and ngaDst to a block being loaded, which has
; not begun. Only ngaPtr is really spent, being live across the whole of the
; block loop and so no longer able to share with what a driver declares — which
; is the two bytes of zero page this way of fetching costs.
        lda ngaPtr
        sta ngaValue
        lda ngaPtr+1
        sta ngaValue+1
        lda ngaCount
        sta ngaUnit             ; blocks still to hold
@hold
        lda ngaUnit
        beq @heldBlock
        lda #0
        sta ngaDst              ; the index, which a read may take out of Y
@six
        nga.read
        ldy ngaDst
        sta (ngaValue),y
        iny
        sty ngaDst
        cpy #ngaBlockSize
        bne @six
        lda #ngaBlockSize
        jsr ngaFrameSkip        ; so the Frame stands past them at the end
        lda ngaValue
        clc
        adc #ngaBlockSize
        sta ngaValue
        bcc @counted
        inc ngaValue+1
@counted
        dec ngaUnit
        jmp @hold

; Then a block at a time out of memory, and the Frame opened once more at the
; end rather than once for every one of them.
@heldBlock
        lda ngaCount
        beq @cells
        ldy #0
        lda (ngaPtr),y
        sta nga.unit            ; straight into the driver's cells: the
        iny                     ; descriptors are in memory, and nothing
.if ngaUnitBytes > 1            ; reads the stream before it is opened
        lda (ngaPtr),y
        sta nga.unit+1
        iny
.endif
        lda (ngaPtr),y
        sta nga.offset
        iny
        lda (ngaPtr),y
        sta nga.offset+1
        iny
        lda (ngaPtr),y
        sta ngaDst
        iny
        lda (ngaPtr),y
        sta ngaDst+1
        iny
        lda (ngaPtr),y
        pha                     ; the decoder's number
        lda ngaPtr
        clc
        adc #ngaBlockSize
        sta ngaPtr
        bcc @heldLoad
        inc ngaPtr+1
@heldLoad
        nga.open
        pla
        ldx ngaDst
        ldy ngaDst+1
        jsr ngaTransform
        dec ngaCount
        jmp @heldBlock
.endif

@block
        lda ngaCount
        beq @cells
        jsr ngaFrameOpen        ; back to the Frame: a block's stream replaced it
        nga.read
        sta ngaUnit
.if ngaUnitBytes > 1
        nga.read
        sta ngaUnit+1
.endif
        nga.read
        sta ngaOffset
        nga.read
        sta ngaOffset+1
        nga.read
        sta ngaDst
        nga.read
        sta ngaDst+1
        nga.read
        pha                     ; the decoder's number
        lda #ngaBlockSize
        jsr ngaFrameSkip
        lda ngaUnit             ; only now: the driver reads the Frame through
        sta nga.unit            ; these very cells, and writing them while it
.if ngaUnitBytes > 1            ; did would move the stream
        lda ngaUnit+1
        sta nga.unit+1
.endif
        lda ngaOffset
        sta nga.offset
        lda ngaOffset+1
        sta nga.offset+1
        nga.open                ; the block's stream: its stored size, then its bytes
        pla
        ldx ngaDst
        ldy ngaDst+1
        jsr ngaTransform
        dec ngaCount
        jmp @block
@cells
        jsr ngaFrameOpen
        nga.read
        sta ngaCount            ; Cell writes that follow, read in one stream
@cell
        lda ngaCount
        beq @enter
        nga.read
        sta ngaDst
        nga.read
        sta ngaDst+1
        nga.read
        sta ngaValue
        nga.read
        sta ngaValue+1
        ldy #0
        lda ngaValue
        sta (ngaDst),y          ; a Cell stands outside the driver's window
        iny
        lda ngaValue+1
        sta (ngaDst),y
        dec ngaCount
        jmp @cell
@enter
        lda ngaWanted
        sta ngaCurrentPhase     ; the Phase the program is in from here on
        jmp (ngaEntry)
.endp

; The Frame's stream, from where the routine last left it.
.proc ngaFrameOpen
        lda ngaFrameAt
        sta nga.offset
        lda ngaFrameAt+1
        sta nga.offset+1
        lda ngaFrameAt+2
        sta nga.unit
.if ngaUnitBytes > 1
        lda ngaFrameAt+3
        sta nga.unit+1
.endif
        nga.open
        rts
.endp

; A bytes on in the Frame, which is where its stream is opened next. The
; offset carries into the unit, so that a Frame running past the last offset
; a unit's number can reach goes on into the next.
.proc ngaFrameSkip
        clc
        adc ngaFrameAt
        sta ngaFrameAt
        bcc @done
        inc ngaFrameAt+1
        bne @done
        inc ngaFrameAt+2
.if ngaUnitBytes > 1
        bne @done
        inc ngaFrameAt+3
.endif
@done
        rts
.endp
cell nga.cell
ngaCurrentPhase
runtime.asm nga.runtime
__small = 0
; The C runtime: what `*`, `/` and `%` call where no constant makes them
; shifts, adds and masks. Each Proc is called as any Proc of the assembler is:
; its `.declare arg`s written, `jsr`, its `.declare ret` read. A product is the
; low half, one for either sign; a quotient and a remainder truncate towards
; zero, as C does. See docs/decisions/0095-literals-and-the-runtime.md. And
; what a loop that fills memory a byte at a time calls instead.

.export __mul8, __udiv8, __umod8, __sdiv8, __smod8
.export __mul16, __udiv16, __umod16, __sdiv16, __smod16
.export __mul8to16
.export __fill

; ---------------------------------------------------------------------------
; 8 bits

; left * right: the bits of `left` from the top, the product doubled before
; each and `right` added where the bit is set. The product is left in `A`,
; where it was made — see docs/decisions/0146-the-runtime-in-registers.md.
.proc __mul8, absolute within 256
        .declare arg u8
left    .ztemp 1
        .declare arg u8
right   .ztemp 1
        .declare ret a

        lda #0
        ldx #8
loop    asl                     ; product * 2
        asl left                ; the next bit of `left` into the carry
        bcc skip
        clc
        adc right
skip    dex
        bne loop
        rts
.endp

; left * right, both bytes, the whole product in two: the bits of `left` from
; the bottom, `right` doubled after each and added where the bit is set, the
; product kept in `Y` below and `X` above. Done once no bit of `left` is left,
; so a `left` below 16 takes at most four rounds where every one took eight:
; the compiler hands the operand the ranges hold smaller as `left`. `right` is
; doubled at most seven times, so its pair never carries out of the top — see
; docs/decisions/0144-a-multiply-stops-when-its-multiplier-does.md, and
; docs/decisions/0122-a-multiply-of-two-bytes.md for why there is a Proc of
; two bytes at all. `left` comes in `A`, which is where it is walked — see
; docs/decisions/0146-the-runtime-in-registers.md.
;
; Shaped as Oscar64's `mul16by8` is, which the benchmarks measured it against
; — see docs/decisions/0154-a-multiply-shaped-by-its-bits.md. `A` always holds
; a set bit at `next`, so a clear bit needs no test for zero; the first bit,
; where set, makes the product `right` rather than adding it to zero; and the
; last set bit adds straight into `product`.
.proc __mul8to16, absolute within 256
        .declare arg a
        .declare arg u8
right   .ztemp 1
        .declare ret u16
product .ztemp 2
high    .ztemp 1                ; `right`'s high byte as it is doubled
left    .ztemp 1                ; what is left of `left`, while `A` adds

        ldx #0
        lsr                     ; bit 0 of `left` into the carry
        beq last                ; no bit above it: the product is `right` or 0
        stx high
        ldy #0
        bcc next
        ldy right               ; bit 0 set: the product so far is `right`
        bcs next                ; always
add     sta left                ; what is left of it, while `A` adds
        clc
        tya
        adc right
        tay
        txa
        adc high
        tax
        lda left
next    asl right
        rol high
        lsr                     ; the next bit into the carry
        bcc next                ; clear: `A` still holds a set bit
        bne add                 ; set, and more above it
        clc                     ; set, and the last: added into the product
        tya
        adc right
        sta product
        txa
        adc high
        sta product+1
        rts
last    bcc zero                ; `A` and `X` are zero
        lda right
zero    sta product
        stx product+1
        rts
.endp

; dividend / divisor: the dividend shifted into the remainder in `A` a bit at a
; time, and the divisor subtracted where it fits, which sets the bit of the
; quotient the shift left clear in the dividend's byte. A carry out of the
; shift is a remainder of nine bits, which always fits. A divisor of zero
; always fits: the quotient is all ones.
;
; The quotient is built where the dividend was, a bit a turn, so that byte is
; both the argument and the result and its name says so — see
; docs/decisions/0119-one-temporary-carries-two-roles.md.
.proc __udiv8, absolute within 256
        .declare arg u8
        .declare ret u8
dividendAndQuotient .ztemp 1
        .declare arg u8
divisor .ztemp 1

        lda #0
        ldx #8
loop    asl dividendAndQuotient
        rol                     ; the remainder takes the dividend's top bit
        bcs subtract
        cmp divisor
        bcc skip
subtract
        sbc divisor             ; the carry is set on both ways here
        inc dividendAndQuotient ; the quotient's bit
skip    dex
        bne loop
        rts
.endp

; dividend % divisor: as `__udiv8`, the remainder kept. A divisor of zero leaves
; the dividend.
;
; The remainder is left in `A`, where it was made — see
; docs/decisions/0146-the-runtime-in-registers.md. The quotient is left in
; `dividend`, and a caller that wants both reads it there rather than calling
; `__udiv8` too: a contract, which whatever shape this Proc takes keeps — see
; docs/decisions/0301-a-quotient-and-a-remainder-are-one-call.md.
.proc __umod8, absolute within 256
        .declare arg u8
dividend .ztemp 1
        .declare arg u8
divisor .ztemp 1
        .declare ret a

        lda #0
        ldx #8
loop    asl dividend
        rol
        bcs subtract
        cmp divisor
        bcc skip
subtract
        sbc divisor
        inc dividend
skip    dex
        bne loop
        rts
.endp

; dividend / divisor, signed: the unsigned quotient of the magnitudes, negated
; where the signs differ.
;
; The dividend comes in `A` and the quotient goes back in it — see
; docs/decisions/0146-the-runtime-in-registers.md.
.proc __sdiv8, absolute within 256
        .declare ret a
        .declare arg a
        .declare arg i8
divisor .ztemp 1
sign    .ztemp 1

        tax                     ; the dividend, while the signs are compared
        eor divisor
        sta sign                ; bit 7: the signs differ

        txa
        bpl dividendKept
        eor #$FF
        clc
        adc #1
dividendKept
        sta __udiv8.dividendAndQuotient

        lda divisor
        bpl divisorKept
        eor #$FF
        clc
        adc #1
divisorKept
        sta __udiv8.divisor

        jsr __udiv8
        lda __udiv8.dividendAndQuotient
        bit sign
        bpl done
        eor #$FF
        clc
        adc #1
done    rts
.endp

; dividend % divisor, signed: the unsigned remainder of the magnitudes, negated
; where the dividend is negative.
;
; The dividend comes in `A` and the remainder goes back in it, as it comes
; back from `__umod8` — see docs/decisions/0146-the-runtime-in-registers.md.
.proc __smod8, absolute within 256
        .declare ret a
        .declare arg a
        .declare arg i8
divisor .ztemp 1
sign    .ztemp 1

        sta sign                ; bit 7: the dividend is negative
        tax                     ; `N` from the dividend: the caller promises none
        bpl dividendKept
        eor #$FF
        clc
        adc #1
dividendKept
        sta __umod8.dividend

        lda divisor
        bpl divisorKept
        eor #$FF
        clc
        adc #1
divisorKept
        sta __umod8.divisor

        jsr __umod8             ; the remainder in `A`
        bit sign
        bpl done
        eor #$FF
        clc
        adc #1
done    rts
.endp

; ---------------------------------------------------------------------------
; 16 bits

; left * right, the low half: the bits of `left` from the bottom, `right`
; doubled after each and added where the bit is set, the product kept in `Y`
; below and `high` above. Done once no bit of `left` is left, which is
; docs/decisions/0144-a-multiply-stops-when-its-multiplier-does.md carried from
; `__mul8to16` to the wider Proc: a multiplier whose high byte is zero takes at
; most eight rounds where every one took sixteen, and one of four bits takes
; four. The round is cheaper as well, `right` being doubled where
; the product was.
;
; `A` walks what is left of the multiplier. Over the low byte a set bit is
; rolled in above it first, so that `A` running out says the byte is done and
; not that the bits above are clear; over the high byte, which is walked at
; `narrow` and is where a multiplier of one byte starts, `A` running out ends
; the Proc. See docs/decisions/0170-a-wide-multiply-stops-when-its-multiplier-does.md.
.proc __mul16, absolute within 256
        .declare arg u16
left    .ztemp 2
        .declare arg u16
right   .ztemp 2
        .declare ret u16
product .ztemp 2
high    .ztemp 1                ; the product's high byte, while `Y` holds its low

.if __small
        ; Sixteen rounds, always, the multiplier walked from the bottom out of
        ; its own bytes and the product added up in memory: half the bytes of
        ; the shape above and none of what makes that one quick.
        lda #0
        sta product
        sta product+1
        ldx #16
@loop
        lsr left+1
        ror left
        bcc @+skip
        clc
        lda product
        adc right
        sta product
        lda product+1
        adc right+1
        sta product+1
@skip
        asl right
        rol right+1
        dex
        bne @-loop
        rts
.else
        ldy #0
        sty high
        lda left
        ldx left+1
        beq @+narrow            ; one byte of multiplier: `A` holds all of it
        sec
        ror                     ; bit 0 out, and a set bit in above the rest
        bcc @+lowNext
@lowAdd
        tax                     ; what is left of the byte, while `A` adds
        clc
        tya
        adc right
        tay
        lda high
        adc right+1
        sta high
        txa
@lowNext
        asl right
        rol right+1
        lsr                     ; the next bit into the carry
        bcc @-lowNext           ; clear: `A` still holds a set bit
        bne @-lowAdd            ; set, and more above it
        lda left+1              ; set, and the mark: the low byte is done
@narrow
        lsr
        bcc @+next
@add
        tax
        clc
        tya
        adc right
        tay
        lda high
        adc right+1
        sta high
        txa
@next
        asl right
        rol right+1
        lsr
        bcs @-add
        bne @-next
        sty product
        lda high
        sta product+1
        rts
.endif
.endp

; dividend / divisor, as `__udiv8` with the remainder in `rest`: the quotient
; is built where the dividend was, so that byte is both the argument and the
; result — see docs/decisions/0119-one-temporary-carries-two-roles.md.
;
; A divisor of one byte below $80 takes the loop at `narrow`, where the
; remainder lives in `A` alone: it stays below the divisor, so doubling it and
; taking a bit of the dividend keeps it inside a byte, and the compare is one
; instruction rather than four. The dividend's own pair carries the quotient
; in at the bottom as it carries the next bit out at the top, so the two
; rotations serve both and nothing counts the bit separately. Half the cycles
; of the wide loop, and it is the loop every call of `prime` takes — see
; docs/decisions/0171-a-divisor-of-one-byte.md. A divisor of zero
; goes wide, which is where the quotient of all ones and the remainder of the
; dividend that docs/spec/c-subset.md states come from.
.proc __udiv16, absolute within 256
        .declare arg u16
        .declare ret u16
dividendAndQuotient .ztemp 2
        .declare arg u16
divisor .ztemp 2
rest    .ztemp 2

.if !__small
        lda divisor+1
        bne wide
        lda divisor
        beq wide
        bmi wide                ; $80 and above: a remainder of nine bits
        ldx #16
        asl dividendAndQuotient
        rol dividendAndQuotient+1
        lda #0
@narrow
        rol                     ; the remainder takes the dividend's top bit
        cmp divisor
        bcc @+narrowSkip
        sbc divisor             ; the carry is set on both ways here
@narrowSkip
        rol dividendAndQuotient ; the quotient's bit in, the next dividend bit out
        rol dividendAndQuotient+1
        dex
        bne @-narrow
        rts
.endif

wide    lda #0
        sta rest
        sta rest+1
        ldx #16
loop    asl dividendAndQuotient
        rol dividendAndQuotient+1
        rol rest                ; the remainder takes the dividend's top bit
        rol rest+1
        bcs subtract
        lda rest                ; rest >= divisor?
        cmp divisor
        lda rest+1
        sbc divisor+1
        bcc skip
subtract
        lda rest
        sec
        sbc divisor
        sta rest
        lda rest+1
        sbc divisor+1
        sta rest+1
        inc dividendAndQuotient ; the quotient's bit
skip    dex
        bne loop
        rts
.endp

; dividend % divisor, as `__udiv16`, the remainder kept: it is built in the
; result's own bytes, and the dividend's hold the quotient when it returns. A
; caller that wants both reads the quotient there rather than calling
; `__udiv16` too: a contract, which the narrow loop, the wide one and the
; small shape all keep — see
; docs/decisions/0301-a-quotient-and-a-remainder-are-one-call.md. The narrow
; loop is `__udiv16`'s, and leaves in `A` the remainder that Proc throws away
; — see docs/decisions/0171-a-divisor-of-one-byte.md.
.proc __umod16, absolute within 256
        .declare arg u16
dividend .ztemp 2
        .declare arg u16
divisor .ztemp 2
        .declare ret u16
remainder .ztemp 2

.if !__small
        lda divisor+1
        bne wide
        lda divisor
        beq wide
        bmi wide                ; $80 and above: a remainder of nine bits
        lda #0
        sta remainder+1         ; a remainder below the divisor is one byte
        ldx #16
        asl dividend
        rol dividend+1
@narrow
        rol
        cmp divisor
        bcc @+narrowSkip
        sbc divisor
@narrowSkip
        rol dividend
        rol dividend+1
        dex
        bne @-narrow
        sta remainder
        rts
.endif

wide    lda #0
        sta remainder
        sta remainder+1
        ldx #16
loop    asl dividend
        rol dividend+1
        rol remainder
        rol remainder+1
        bcs subtract
        lda remainder
        cmp divisor
        lda remainder+1
        sbc divisor+1
        bcc skip
subtract
        lda remainder
        sec
        sbc divisor
        sta remainder
        lda remainder+1
        sbc divisor+1
        sta remainder+1
        inc dividend
skip    dex
        bne loop
        rts
.endp

; dividend / divisor, signed, as `__sdiv8`.
.proc __sdiv16, absolute within 256
        .declare arg i16
dividend .ztemp 2
        .declare arg i16
divisor .ztemp 2
        .declare ret i16
quotient .ztemp 2
sign    .ztemp 1

        lda dividend+1
        eor divisor+1
        sta sign                ; bit 7: the signs differ

        lda dividend+1
        bmi dividendNegative
        lda dividend
        sta __udiv16.dividendAndQuotient
        lda dividend+1
        sta __udiv16.dividendAndQuotient+1
        jmp dividendDone
dividendNegative
        lda dividend
        eor #$FF
        clc
        adc #1
        sta __udiv16.dividendAndQuotient
        lda dividend+1
        eor #$FF
        adc #0
        sta __udiv16.dividendAndQuotient+1
dividendDone

        lda divisor+1
        bmi divisorNegative
        lda divisor
        sta __udiv16.divisor
        lda divisor+1
        sta __udiv16.divisor+1
        jmp divisorDone
divisorNegative
        lda divisor
        eor #$FF
        clc
        adc #1
        sta __udiv16.divisor
        lda divisor+1
        eor #$FF
        adc #0
        sta __udiv16.divisor+1
divisorDone

        jsr __udiv16
        bit sign
        bmi negative
        lda __udiv16.dividendAndQuotient
        sta quotient
        lda __udiv16.dividendAndQuotient+1
        sta quotient+1
        rts
negative
        lda __udiv16.dividendAndQuotient
        eor #$FF
        clc
        adc #1
        sta quotient
        lda __udiv16.dividendAndQuotient+1
        eor #$FF
        adc #0
        sta quotient+1
        rts
.endp

; dividend % divisor, signed, as `__smod8`.
.proc __smod16, absolute within 256
        .declare arg i16
dividend .ztemp 2
        .declare arg i16
divisor .ztemp 2
        .declare ret i16
remainder .ztemp 2
sign    .ztemp 1

        lda dividend+1
        sta sign                ; bit 7: the dividend is negative

        lda dividend+1
        bmi dividendNegative
        lda dividend
        sta __umod16.dividend
        lda dividend+1
        sta __umod16.dividend+1
        jmp dividendDone
dividendNegative
        lda dividend
        eor #$FF
        clc
        adc #1
        sta __umod16.dividend
        lda dividend+1
        eor #$FF
        adc #0
        sta __umod16.dividend+1
dividendDone

        lda divisor+1
        bmi divisorNegative
        lda divisor
        sta __umod16.divisor
        lda divisor+1
        sta __umod16.divisor+1
        jmp divisorDone
divisorNegative
        lda divisor
        eor #$FF
        clc
        adc #1
        sta __umod16.divisor
        lda divisor+1
        eor #$FF
        adc #0
        sta __umod16.divisor+1
divisorDone

        jsr __umod16
        bit sign
        bmi negative
        lda __umod16.remainder
        sta remainder
        lda __umod16.remainder+1
        sta remainder+1
        rts
negative
        lda __umod16.remainder
        eor #$FF
        clc
        adc #1
        sta remainder
        lda __umod16.remainder+1
        eor #$FF
        adc #0
        sta remainder+1
        rts
.endp

; ---------------------------------------------------------------------------
; Memory

; `count` bytes from `to` on, each set to the byte that comes in `A`: the
; whole pages first, `Y` running through each and `X` counting them, then what
; is left of the last. Eleven cycles a byte over a page, where the loop it
; stands for took over thirty. A count of zero writes nothing, though the
; compiler calls it with one at least: it is what a loop
; `for (i = from; i < bound; i = i + 1) a[i] = v;` over a 16-bit counter
; becomes — see docs/decisions/0265-a-loop-that-fills-calls-the-runtime.md.
.proc __fill, absolute within 256
        .declare arg u16
to      .ztemp 2
        .declare arg u16
count   .ztemp 2
        .declare arg a

        ldy #0
        ldx count+1
        beq rest
page    sta (to),y
        iny
        bne page
        inc to+1
        dex
        bne page
rest    ldx count
        beq done
last    sta (to),y
        iny
        dex
        bne last
done    rts
.endp
transforms.asm nga.transforms
; The dispatcher over the decoders the program declared, in the order the
; tool numbered them. See docs/spec/transition.md.
.export ngaSelects
ngaSelects = 1
.export ngaLoadUnit, ngaLoadMap
.section root
ngaLoadUnit
        .res 1
.ends
.proc ngaLoadMap, root
        ldx ngaLoadUnit
        nga.showAt ext
        rts
.endp
.export ngaTransform
.proc ngaTransform
        cmp #0
        bne @not0
        jmp portbCopy
@not0
        brk
.endp
init.asm nga.init
; The driver's `init`, wrapped so that a Container has an address to call.
; See docs/spec/transition.md and docs/spec/xex.md.
.export ngaInit
.proc ngaInit, root
        nga.init
        rts
.endp
finish.asm nga.finish
; Where a program that returns ends: the driver's `finalize`, then back to
; what started it. See docs/decisions/0229-no-window-has-a-base.md.
.export ngaFinish
.proc ngaFinish, root
        nga.finalize
        rts
.endp
run.asm nga.run
; How the program is started: the ROMs its entry Phase needs selected, and
; where it ends left on the stack, so that its return lands on ngaFinish
; from whichever Phase it returns. See
; docs/decisions/0229-no-window-has-a-base.md.
.export ngaRun
.proc ngaRun, root
        lda #3
        nga.select
        .own                    ; followed by the rts the program returns with
        lda #>( ngaFinish - 1 )
        pha
        .own
        lda #<( ngaFinish - 1 )
        pha
        jmp nga.entry
.endp

Memory map

  1. intro145 zero page, 16241 bytes
  2. level145 zero page, 16246 bytes
AddressBytesSectionModuleKindPhasesWaits
$0000–$007F128osZeroosramsection0..1
$0080–$00823portbPtrportbsection0..1
$0083–$00842ngaDstnga.transitiontemporary0..1
$0083–$00842ngaPtrnga.transitiontemporary0..1
$0083–$00842portbDstportbtemporary0..1
$0083–$00831showScreen.__0leftruntemporary0..1
$0085–$00862ngaOffsetnga.transitiontemporary0..1
$0085–$00862ngaValuenga.transitiontemporary0..1
$0085–$00862portbSizeportbtemporary0..1
$0087–$00871ngaUnitnga.transitiontemporary0..1
$0087–$00871portbRunportbtemporary0..1
$0088–$00892ngaEntrynga.transitiontemporary0..1
$008A–$008C3ngaFrameAtnga.transitiontemporary0..1
$008D–$008D1ngaWantednga.transitiontemporary0..1
$008E–$008E1ngaCountnga.transitiontemporary0..1
$008F–$00902titlenga.slotssection0..1
$0200–$06FF1280osRamosramsection0..1
$2000–$20001portbFoundportbsection0..1
$2001–$20055portbValuesportbsection0..1
$2006–$201112portbBankportbproc0..1
$2012–$202F30portbSelectRomsportbproc0..1
$2030–$204724portbOpenStreamportbproc0..1
$2048–$205D22portbReadByteportbproc0..1
$205E–$206D16portbNextUnitportbproc0..1
$206E–$20DF114portbCopyportbproc0..1
$20E0–$20F825printLineprintproc0..1
$20F9–$211327showScreenrunproc0..1
$2114–$21196introTitleintrosection0
$211A–$212A17introTextintrosection0
$212B–$21339introDrawintroproc0
$2134–$213F12introStartintroproc0
$2140–$214D14ngaRunnga.runproc0
$2153–$219B73ngaTransitionnga.transitionproc0..1
$219C–$2235154ngaEnternga.transitionproc0..1
$2236–$224516ngaFrameOpennga.transitionproc0..1
$2246–$225314ngaFrameSkipnga.transitionproc0..1
$2254–$22541ngaCurrentPhasenga.cellsection0..1
$2255–$22573drawnga.slotssection0..1
$2258–$22581ngaLoadUnitnga.transformssection0..1
$2259–$225F7ngaLoadMapnga.transformsproc0..1
$2260–$22678ngaTransformnga.transformsproc0..1
$2268–$226E7ngaInitnga.initproc0..1
$226F–$22757ngaFinishnga.finishproc0..1
$4000–$4172371levelMaplevelsection0..1
$C000–$CFFF4096osRomLowosromsection0..1
$D800–$DFFF2048mathPackmathpacksection0..1
$E000–$FFFF8192osRomHighosromsection0..1
$0083–$00842levelDraw.__0atleveltemporary1
$0085–$00851levelDraw.__1leftleveltemporary1
$2114–$211D10levelTitlelevelsection1bank 0 +$001F (copy, 12 B)
$211E–$214C47levelDrawlevelproc1bank 0 +$002B (copy, 49 B)
$214D–$21526levelStartlevelproc1bank 0 +$005C (copy, 8 B)

The xex

1164 bytes, 14 segments, 0 unaccounted.

02container header
$2001–$213F 319 bytes at 2
$20015portb.portbValuesportb.asm
$20063portb.portbBankportb.asm
$20092portb.portbBankportb.asm
$200B3portb.portbBankportb.asm
$200E3portb.portbBankportb.asm
$20111portb.portbBankportb.asm
$20121portb.portbSelectRomsportb.asm
$20133portb.portbSelectRomsportb.asm
$20162portb.portbSelectRomsportb.asm
$20181portb.portbSelectRomsportb.asm
$20191portb.portbSelectRomsportb.asm
$201A1portb.portbSelectRomsportb.asm
$201B2portb.portbSelectRomsportb.asm
$201D2portb.portbSelectRomsportb.asm
$201F1portb.portbSelectRomsportb.asm
$20202portb.portbSelectRomsportb.asm
$20221portb.portbSelectRomsportb.asm
$20231portb.portbSelectRomsportb.asm
$20242portb.portbSelectRomsportb.asm
$20262portb.portbSelectRomsportb.asm
$20281portb.portbSelectRomsportb.asm
$20292portb.portbSelectRomsportb.asm
$202B1portb.portbSelectRomsportb.asm
$202C3portb.portbSelectRomsportb.asm
$202F1portb.portbSelectRomsportb.asm
$20302portb.portbOpenStreamportb.asm
$20322portb.portbOpenStreamportb.asm
$20342portb.portbOpenStreamportb.asm
$20362portb.portbOpenStreamportb.asm
$20382portb.portbOpenStreamportb.asm
$203A3portb.portbOpenStreamportb.asm
$203D1portb.portbOpenStreamportb.asm
$203E2portb.portbOpenStreamportb.asm
$20402portb.portbOpenStreamportb.asm
$20422portb.portbOpenStreamportb.asm
$20443portb.portbOpenStreamportb.asm
$20471portb.portbOpenStreamportb.asm
$20482portb.portbReadByteportb.asm
$204A2portb.portbReadByteportb.asm
$204C2portb.portbReadByteportb.asm
$204E2portb.portbReadByteportb.asm
$20502portb.portbReadByteportb.asm
$20522portb.portbReadByteportb.asm
$20542portb.portbReadByteportb.asm
$20562portb.portbReadByteportb.asm
$20581portb.portbReadByteportb.asm
$20593portb.portbReadByteportb.asm
$205C1portb.portbReadByteportb.asm
$205D1portb.portbReadByteportb.asm
$205E2portb.portbNextUnitportb.asm
$20602portb.portbNextUnitportb.asm
$20623portb.portbNextUnitportb.asm
$20652portb.portbNextUnitportb.asm
$20672portb.portbNextUnitportb.asm
$20692portb.portbNextUnitportb.asm
$206B2portb.portbNextUnitportb.asm
$206D1portb.portbNextUnitportb.asm
$206E2portb.portbCopyportb.asm
$20702portb.portbCopyportb.asm
$20723portb.portbCopyportb.asm
$20752portb.portbCopyportb.asm
$20773portb.portbCopyportb.asm
$207A2portb.portbCopyportb.asm
$207C2portb.portbCopyportb.asm
$207E2portb.portbCopyportb.asm
$20802portb.portbCopyportb.asm
$20822portb.portbCopyportb.asm
$20842portb.portbCopyportb.asm
$20861portb.portbCopyportb.asm
$20872portb.portbCopyportb.asm
$20892portb.portbCopyportb.asm
$208B2portb.portbCopyportb.asm
$208D2portb.portbCopyportb.asm
$208F2portb.portbCopyportb.asm
$20912portb.portbCopyportb.asm
$20932portb.portbCopyportb.asm
$20952portb.portbCopyportb.asm
$20972portb.portbCopyportb.asm
$20992portb.portbCopyportb.asm
$209B2portb.portbCopyportb.asm
$209D1portb.portbCopyportb.asm
$209E2portb.portbCopyportb.asm
$20A02portb.portbCopyportb.asm
$20A21portb.portbCopyportb.asm
$20A32portb.portbCopyportb.asm
$20A52portb.portbCopyportb.asm
$20A72portb.portbCopyportb.asm
$20A92portb.portbCopyportb.asm
$20AB2portb.portbCopyportb.asm
$20AD2portb.portbCopyportb.asm
$20AF3portb.portbCopyportb.asm
$20B21portb.portbCopyportb.asm
$20B31portb.portbCopyportb.asm
$20B42portb.portbCopyportb.asm
$20B62portb.portbCopyportb.asm
$20B82portb.portbCopyportb.asm
$20BA2portb.portbCopyportb.asm
$20BC1portb.portbCopyportb.asm
$20BD1portb.portbCopyportb.asm
$20BE2portb.portbCopyportb.asm
$20C02portb.portbCopyportb.asm
$20C22portb.portbCopyportb.asm
$20C42portb.portbCopyportb.asm
$20C61portb.portbCopyportb.asm
$20C72portb.portbCopyportb.asm
$20C92portb.portbCopyportb.asm
$20CB2portb.portbCopyportb.asm
$20CD2portb.portbCopyportb.asm
$20CF2portb.portbCopyportb.asm
$20D12portb.portbCopyportb.asm
$20D32portb.portbCopyportb.asm
$20D52portb.portbCopyportb.asm
$20D72portb.portbCopyportb.asm
$20D93portb.portbCopyportb.asm
$20DC3portb.portbCopyportb.asm
$20DF1portb.portbCopyportb.asm
$20E03print.printLineprint.asm
$20E33print.printLineprint.asm
$20E63print.printLineprint.asm
$20E92print.printLineprint.asm
$20EB3print.printLineprint.asm
$20EE2print.printLineprint.asm
$20F03print.printLineprint.asm
$20F32print.printLineprint.asm
$20F53print.printLineprint.asm
$20F81print.printLineprint.asm
$20F92run.showScreenrun.asm
$20FB2run.showScreenrun.asm
$20FD2run.showScreenrun.asm
$20FF2run.showScreenrun.asm
$21012run.showScreenrun.asm
$21032run.showScreenrun.asm
$21053run.showScreenrun.asm
$21082run.showScreenrun.asm
$210A2run.showScreenrun.asm
$210C2run.showScreenrun.asm
$210E3run.showScreenrun.asm
$21113run.showScreenrun.asm
$21146intro.introTitleintro.asm
$211A8intro.introTextintro.asm
$21228intro.introTextintro.asm
$212A1intro.introTextintro.asm
$212B2intro.introDrawintro.asm
$212D2intro.introDrawintro.asm
$212F2intro.introDrawintro.asm
$21313intro.introDrawintro.asm
$21343intro.introStartintro.asm
$21379intro.introStartintro.asm
$2153–$2254 258 bytes at 325
$21531nga.transition.ngaTransitiontransition.asm
$21542nga.transition.ngaTransitiontransition.asm
$21561nga.transition.ngaTransitiontransition.asm
$21572nga.transition.ngaTransitiontransition.asm
$21592nga.transition.ngaTransitiontransition.asm
$215B2nga.transition.ngaTransitiontransition.asm
$215D2nga.transition.ngaTransitiontransition.asm
$215F1nga.transition.ngaTransitiontransition.asm
$21602nga.transition.ngaTransitiontransition.asm
$21622nga.transition.ngaTransitiontransition.asm
$21642nga.transition.ngaTransitiontransition.asm
$21661nga.transition.ngaTransitiontransition.asm
$21672nga.transition.ngaTransitiontransition.asm
$21692nga.transition.ngaTransitiontransition.asm
$216B2nga.transition.ngaTransitiontransition.asm
$216D2nga.transition.ngaTransitiontransition.asm
$216F2nga.transition.ngaTransitiontransition.asm
$21712nga.transition.ngaTransitiontransition.asm
$21731nga.transition.ngaTransitiontransition.asm
$21742nga.transition.ngaTransitiontransition.asm
$21762nga.transition.ngaTransitiontransition.asm
$21783nga.transition.ngaTransitiontransition.asm
$217B2nga.transition.ngaTransitiontransition.asm
$217D2nga.transition.ngaTransitiontransition.asm
$217F1nga.transition.ngaTransitiontransition.asm
$21802nga.transition.ngaTransitiontransition.asm
$21822nga.transition.ngaTransitiontransition.asm
$21842nga.transition.ngaTransitiontransition.asm
$21862nga.transition.ngaTransitiontransition.asm
$21882nga.transition.ngaTransitiontransition.asm
$218A3nga.transition.ngaTransitiontransition.asm
$218D1nga.transition.ngaTransitiontransition.asm
$218E2nga.transition.ngaTransitiontransition.asm
$21902nga.transition.ngaTransitiontransition.asm
$21921nga.transition.ngaTransitiontransition.asm
$21932nga.transition.ngaTransitiontransition.asm
$21952nga.transition.ngaTransitiontransition.asm
$21971nga.transition.ngaTransitiontransition.asm
$21982nga.transition.ngaTransitiontransition.asm
$219A2nga.transition.ngaTransitiontransition.asm
$219C3nga.transition.ngaEntertransition.asm
$219F3nga.transition.ngaEntertransition.asm
$21A22nga.transition.ngaEntertransition.asm
$21A43nga.transition.ngaEntertransition.asm
$21A72nga.transition.ngaEntertransition.asm
$21A93nga.transition.ngaEntertransition.asm
$21AC2nga.transition.ngaEntertransition.asm
$21AE3nga.transition.ngaEntertransition.asm
$21B13nga.transition.ngaEntertransition.asm
$21B42nga.transition.ngaEntertransition.asm
$21B63nga.transition.ngaEntertransition.asm
$21B92nga.transition.ngaEntertransition.asm
$21BB2nga.transition.ngaEntertransition.asm
$21BD3nga.transition.ngaEntertransition.asm
$21C03nga.transition.ngaEntertransition.asm
$21C32nga.transition.ngaEntertransition.asm
$21C53nga.transition.ngaEntertransition.asm
$21C82nga.transition.ngaEntertransition.asm
$21CA3nga.transition.ngaEntertransition.asm
$21CD2nga.transition.ngaEntertransition.asm
$21CF3nga.transition.ngaEntertransition.asm
$21D22nga.transition.ngaEntertransition.asm
$21D43nga.transition.ngaEntertransition.asm
$21D72nga.transition.ngaEntertransition.asm
$21D93nga.transition.ngaEntertransition.asm
$21DC1nga.transition.ngaEntertransition.asm
$21DD2nga.transition.ngaEntertransition.asm
$21DF3nga.transition.ngaEntertransition.asm
$21E22nga.transition.ngaEntertransition.asm
$21E42nga.transition.ngaEntertransition.asm
$21E62nga.transition.ngaEntertransition.asm
$21E82nga.transition.ngaEntertransition.asm
$21EA2nga.transition.ngaEntertransition.asm
$21EC2nga.transition.ngaEntertransition.asm
$21EE3nga.transition.ngaEntertransition.asm
$21F11nga.transition.ngaEntertransition.asm
$21F22nga.transition.ngaEntertransition.asm
$21F42nga.transition.ngaEntertransition.asm
$21F63nga.transition.ngaEntertransition.asm
$21F92nga.transition.ngaEntertransition.asm
$21FB3nga.transition.ngaEntertransition.asm
$21FE3nga.transition.ngaEntertransition.asm
$22013nga.transition.ngaEntertransition.asm
$22042nga.transition.ngaEntertransition.asm
$22062nga.transition.ngaEntertransition.asm
$22082nga.transition.ngaEntertransition.asm
$220A3nga.transition.ngaEntertransition.asm
$220D2nga.transition.ngaEntertransition.asm
$220F3nga.transition.ngaEntertransition.asm
$22122nga.transition.ngaEntertransition.asm
$22143nga.transition.ngaEntertransition.asm
$22172nga.transition.ngaEntertransition.asm
$22193nga.transition.ngaEntertransition.asm
$221C2nga.transition.ngaEntertransition.asm
$221E2nga.transition.ngaEntertransition.asm
$22202nga.transition.ngaEntertransition.asm
$22222nga.transition.ngaEntertransition.asm
$22241nga.transition.ngaEntertransition.asm
$22252nga.transition.ngaEntertransition.asm
$22272nga.transition.ngaEntertransition.asm
$22292nga.transition.ngaEntertransition.asm
$222B3nga.transition.ngaEntertransition.asm
$222E2nga.transition.ngaEntertransition.asm
$22303nga.transition.ngaEntertransition.asm
$22333nga.transition.ngaEntertransition.asm
$22362nga.transition.ngaFrameOpentransition.asm
$22382nga.transition.ngaFrameOpentransition.asm
$223A2nga.transition.ngaFrameOpentransition.asm
$223C2nga.transition.ngaFrameOpentransition.asm
$223E2nga.transition.ngaFrameOpentransition.asm
$22402nga.transition.ngaFrameOpentransition.asm
$22423nga.transition.ngaFrameOpentransition.asm
$22451nga.transition.ngaFrameOpentransition.asm
$22461nga.transition.ngaFrameSkiptransition.asm
$22472nga.transition.ngaFrameSkiptransition.asm
$22492nga.transition.ngaFrameSkiptransition.asm
$224B2nga.transition.ngaFrameSkiptransition.asm
$224D2nga.transition.ngaFrameSkiptransition.asm
$224F2nga.transition.ngaFrameSkiptransition.asm
$22512nga.transition.ngaFrameSkiptransition.asm
$22531nga.transition.ngaFrameSkiptransition.asm
$22541nga.cell.ngaCurrentPhase
$008F–$0090 2 bytes at 587
$008F2nga.slots.titlerun.asm
$2255–$2257 3 bytes at 593
$22553nga.slots.drawrun.asm
$2259–$2275 29 bytes at 600
$22593nga.transforms.ngaLoadMaptransforms.asm
$225C3nga.transforms.ngaLoadMaptransforms.asm
$225F1nga.transforms.ngaLoadMaptransforms.asm
$22602nga.transforms.ngaTransformtransforms.asm
$22622nga.transforms.ngaTransformtransforms.asm
$22643nga.transforms.ngaTransformtransforms.asm
$22671nga.transforms.ngaTransformtransforms.asm
$22686nga.init.ngaInitinit.asm
$226E1nga.init.ngaInitinit.asm
$226F6nga.finish.ngaFinishfinish.asm
$22751nga.finish.ngaFinishfinish.asm
$2140–$214D 14 bytes at 633
$21402nga.run.ngaRunrun.asm
$21423nga.run.ngaRunrun.asm
$21452nga.run.ngaRunrun.asm
$21471nga.run.ngaRunrun.asm
$21482nga.run.ngaRunrun.asm
$214A1nga.run.ngaRunrun.asm
$214B3nga.run.ngaRunrun.asm
$02E2–$02E3 2 bytes at 651
$02E22initad
$2258–$2258 1 bytes at 657
$22581nga.transforms.ngaLoadUnittransforms.asm
$02E2–$02E3 2 bytes at 662
$02E22initad
$4000–$4063 100 bytes at 668
$4000100storage image
$2258–$2258 1 bytes at 772
$22581nga.transforms.ngaLoadUnittransforms.asm
$02E2–$02E3 2 bytes at 777
$02E22initad
$4000–$4172 371 bytes at 783
$40008level.levelMaplevel.asm
$40088level.levelMaplevel.asm
$40108level.levelMaplevel.asm
$40188level.levelMaplevel.asm
$40208level.levelMaplevel.asm
$40288level.levelMaplevel.asm
$40308level.levelMaplevel.asm
$40388level.levelMaplevel.asm
$40408level.levelMaplevel.asm
$40488level.levelMaplevel.asm
$40508level.levelMaplevel.asm
$40588level.levelMaplevel.asm
$40608level.levelMaplevel.asm
$40688level.levelMaplevel.asm
$40708level.levelMaplevel.asm
$40788level.levelMaplevel.asm
$40808level.levelMaplevel.asm
$40888level.levelMaplevel.asm
$40908level.levelMaplevel.asm
$40988level.levelMaplevel.asm
$40A08level.levelMaplevel.asm
$40A88level.levelMaplevel.asm
$40B08level.levelMaplevel.asm
$40B88level.levelMaplevel.asm
$40C08level.levelMaplevel.asm
$40C88level.levelMaplevel.asm
$40D08level.levelMaplevel.asm
$40D88level.levelMaplevel.asm
$40E08level.levelMaplevel.asm
$40E88level.levelMaplevel.asm
$40F08level.levelMaplevel.asm
$40F88level.levelMaplevel.asm
$41008level.levelMaplevel.asm
$41088level.levelMaplevel.asm
$41108level.levelMaplevel.asm
$41188level.levelMaplevel.asm
$41208level.levelMaplevel.asm
$41288level.levelMaplevel.asm
$41308level.levelMaplevel.asm
$41388level.levelMaplevel.asm
$41408level.levelMaplevel.asm
$41488level.levelMaplevel.asm
$41508level.levelMaplevel.asm
$41588level.levelMaplevel.asm
$41608level.levelMaplevel.asm
$41688level.levelMaplevel.asm
$41703level.levelMaplevel.asm
$02E0–$02E1 2 bytes at 1158
$02E02runad