Viewer source
The Viewer source export is the C64 program that shows your picture, as one assembly source file: its code with labels and notes, the picture's data as labeled blocks, and a header that maps its memory. Assembled unchanged, it builds the same program as the C64 program export, byte for byte. It's the starting point for a demo part, an intro or a title screen of your own: you get a working viewer, and it tells you where there's room for more.
Every format with a C64 program has a viewer source.
Exporting it
- File ▸ Export… (Ctrl+E).
- Under Assembly source, choose Viewer source (.asm).
- Choose the Assembler: KickAssembler or ACME. (64tass / ca65 is disabled here: the viewer source is checked byte for byte with KickAssembler and ACME only. It stays available for the data's own source.)
- Export…. The file is named
NAME.viewer.asm.
The source names its own file in its build command, so keep the name or change the command to match:
| Assembler | Build command (in the header) |
|---|---|
| KickAssembler | java -jar KickAss.jar NAME.viewer.asm -o NAME.viewer.prg |
| ACME | acme -f cbm -o NAME.viewer.prg --strict-segments NAME.viewer.asm |
The source is plain ASCII, as the assemblers read it.
The header
The header is a comment at the top. For a Koala picture, for KickAssembler, it reads:
// Multicolor bitmap (Koala), 160 x 200: Mosaicus viewer source for KickAssembler
// The viewer of Mosaicus' "C64 program" export as source: assembled unchanged, it builds the same .prg, byte for
// byte.
//
// Machine: Shows the picture. LOAD and RUN it on any C64, PAL or NTSC (or an emulator).
//
// Build: java -jar KickAss.jar koala.viewer.asm -o koala.viewer.prg
//
// Per picture: the screen, bitmap and colors blocks and the background and border constants: the code is the same
// for any picture.
//
// Memory map, loaded (each part at its fixed address; zeros between):
// $0801-$080C basic the BASIC line: 10 SYS 2061
// $080D-$0C86 code the viewer, then the color RAM's bytes
// $4000-$43E7 screen the screen, a byte a cell, row by row: two of its colors (high, low nybble)
// $6000-$7F3F bitmap the bitmap, cell by cell (8 bytes a cell, 40 cells a row)
// Written at run time:
// $FFFA-$FFFF the NMI and IRQ vectors
// Free RAM (with $01 = $35: RAM under BASIC and the KERNAL; I/O at $D000-$DFFF; zero page below): $0200-$0800,
// $0C87-$3FFF, $43E8-$5FFF, $7F40-$CFFF, $E000-$FFF9.
//
// Entry: start ($080D), from the BASIC line: LOAD and RUN the .prg.
//
// What it uses and assumes:
// - $01 = $35: BASIC and the KERNAL switched out (RAM under them), I/O in.
// - The IRQ and NMI vectors ($FFFE, $FFFA: RAM under the KERNAL) point at `rti`; no CIA or VIC interrupts, no
// sprites.
// - VIC bank 1 ($DD00 bits 0-1 %10): the screen at $4000, the bitmap at $6000 ($D018 = $08).
// - No zero page but $01. Nothing is timed: any C64, PAL or NTSC.
// - It never returns: it ends in `main`, a jmp to itself.
//
// Nothing is timed. The .errorif lines fail the build when a part grows into the next. Operand sizes are forced
// (.abs, .zp) where the assembler could choose another.
What each part tells you:
- Machine: what the program needs to run, the same text as the C64 program's entry in the Export dialog.
- Build: the command that builds it.
- Per picture: which blocks change from one picture to another. Swap those, and the same code shows another picture of the same format (and, for the raced Ultimate formats, of the same size).
- Memory map, loaded: each part of the program and the address it sits at. The bytes between the parts are zeros.
- Written at run time: memory the program writes while it runs, outside its own parts (zero page aside).
- Free RAM: every gap of 16 bytes or more that the program neither loads nor writes, from $0200 up. It counts RAM under BASIC and the KERNAL as free, because the viewer switches them out ($01 = $35), and leaves out the I/O area at $D000-$DFFF.
- Entry: the label the BASIC line's SYS starts.
- What it uses and assumes: the zero page it uses, the interrupts, the VIC bank, the timing, any turbo registers. Keep to it, and your additions won't break the picture.
The code
After the header, each part of the program follows at its fixed address (*= in KickAssembler, * = in ACME):
- The code has labels and a comment for each step. A part whose name is a data block's (the picture's
bitmap, say) is labeled the same in the code, so the code readslda colors,xrather than an address. - The picture's data is in labeled blocks of byte lines; a long run of zeros is one
.fill(or!fill). - Operand sizes are forced where the assembler could choose another (
.absand.zpin KickAssembler,+2and+1in ACME), so the code assembles to the same bytes whatever you add before it. - Mosaicus' viewers use the stable undocumented 6502 opcodes where they help, written with each assembler's own names. The SuperCPU format's viewer uses documented opcodes only (
.cpu _6502NoIllegalsor!cpu 6502), since a 65816 runs the others as other instructions.
The build checks
The source checks what the program depends on, and the build fails with a message when an edit breaks it. These are the .errorif lines in KickAssembler, and !if … { !error … } in ACME:
- each part must end before the next one starts (
code runs into screen at $4000); - a delay loop or other timed code must not cross a page, where a branch takes a cycle more (
a delay loop: a branch across a page takes a cycle more); - a table the code reads with an index must start at a page's start;
- in ACME, the BASIC line's SYS must still be the entry label's address.
Code marked cycle-exact in its comments counts cycles, and code marked timed must land on its raster lines: don't edit either, and don't move it across a page. The header says whether the viewer has any. A plain Koala or hires viewer has none: nothing in it is timed.
Mosaicus' own tests assemble every kind of viewer's source with the real KickAssembler and ACME and compare the result with the C64 program, byte for byte.
Where your own code can go
Most viewers' code has a comment where your own code can run, and the header's What it uses and assumes says what it must leave alone and, for the timed viewers, which lines are free.
- Koala and hires: after the picture is shown, the viewer ends in
main, ajmpto itself, marked the picture shows: code of your own can run here instead. Nothing is timed and no interrupts run, so your code has the whole machine, as long as it leaves the VIC's settings, the screen, the bitmap and the color RAM alone. - Multicolor + sprites and Hires + sprites: raster interrupts place the sprites, and the main loop is free: code of your own can run here instead. The interrupts start within 13 cycles of their line from that loop; longer instructions, or
sei, delay them. - Sprite sheet: the interrupt does the rest; the main loop is free in the same way.
- Hires, Multicolor and ECM characters: as Koala and hires, the picture shows and the main loop is free.
- Charset logo: the logo shows, and the main loop is free.
- Hires FLI (AFLI), Multicolor FLI and the FLI + sprites formats: the kernels hold the CPU from line 35 to the picture's end; the rest of the frame is the wait in
idle, where your code can run, as long as the frame's code at line 251 stays on time. There is no marker comment here: the uses notes say it. - SuperCPU format: the kernels take every line of the picture area (51-250), so no time there is free; outside it the viewer only waits for the next frame.
- The C64 Ultimate formats: these viewers race the beam with interrupts off, so the free time is between frames. The header's last uses note says where it is, how long it lasts and what you may use.
For a 320 × 200 Hires, 16 colors per pixel picture, that note reads:
// - Code of your own: at mx_end, reached as the picture's last line ends (line 251): replace the free-time
// count (from mx_end to its jmp mx_go) with your code and a jmp mx_frame, which waits for line 44 (the next
// frame's sync): your code must end before line 44, or a frame is skipped (all black); budget about 105 lines
// PAL (416,745 cycles), 56 NTSC (229,320 cycles). Or keep the count after your code: it first waits for line
// 256, so your code must then end before the frame's last line (311 PAL, 262 NTSC); budget about 60 lines PAL
// (238,140 cycles), 11 NTSC (45,045 cycles). It may use A, X, Y, the stack, and zero page but $02-$07 and
// $B0-$D7; keep the interrupts off (sei), the display blanked and the sprites off.
The lines and budgets depend on the picture's size and the clock (48 MHz viewers put your code before their jmp mx_frame), so read them in your own picture's source. The cycles are turbo cycles.
TipThe status bar's raster-time item shows the viewer's share of each frame and what's left for other code, for the formats whose viewers are timed: see Running on a C64.
Things to know
- Mosaicus doesn't open a viewer source as a picture. To edit the picture again, open its project, its PNG or its data.
- The viewer source is the program for the picture as it was when you exported it. After more editing, export it again, or swap only the blocks the Per picture line names.
- For your own code with your own viewer, the raw data or the assembly data may suit you better.
Related
- Exporting: the C64 program and the data exports.
- Running on a C64: try the program in VICE or on a C64 Ultimate.
- C64 Ultimate formats: the raced formats and what they need.