Machine contract
This page gives the machine state that libtini needs at launch, the state that it changes and restores, and the rules for interrupts, memory mapping and shared devices. Read it before you add assembly, an interrupt handler or a different driver to a libtini program.
Launch
A libtini program is a ProDOS SYS file. ProDOS starts it at $2000 on an enhanced //e (65C02). At entry, these conditions must be true:
- Main memory is selected for reads and writes (RAMRD and RAMWRT off). ALTZP is off.
- The RamWorks selector is 0.
- Appletini fake SHR is off: $C029 holds $01.
- 80STORE, HIRES and PAGE2 do not send the instruction stream at $2000 to auxiliary memory.
A usual ProDOS launch from the text screen gives all of these conditions. Software cannot read $C073 and $C029. Thus their entry values are requirements, and startup does not check them.
What startup changes
Before main, startup saves these items:
- the cc65 zero page ($80-$99)
- the 64 bytes at $0140 that the transfer trampoline uses
- the entry processor status, 80STORE and six display switches that it can read.
Then it masks interrupts and sets 80STORE off. It copies the low segments of the full-game layout into position. It clears NEARBSS and BSS, sets up the C stack and installs the trampoline. If interrupts were enabled at entry, it enables them again. C runs in binary mode. Startup does not change the RESET vector, the ProDOS device list or the language card.
Exit
A return from main, or a call to exit, runs ti_shutdown. Then startup restores these items: main memory mapping, selector 0, fake SHR off, the saved display switches, 80STORE, the trampoline bytes, the zero page and the entry processor status. Then it calls ProDOS QUIT.
Startup restores the OS state that it saved. It does not restore the low memory of the program. A reset or a power loss cannot run this procedure.
If ProDOS does not remove the audio interrupt handler, ti_shutdown fails. A QUIT then makes ProDOS call into freed memory. Thus startup does not quit. It sets the text screen on and shows CLEANUP ERROR. KEY RETRIES; CODE RETAINED. It tries the shutdown again for each key.
The bank grant
ti_init(first_bank, bank_count) is a grant from the game. libtini cannot find which programs use a bank. Grant a continuous sequence of selectors 1-126 that no RAM disk or other program uses.
The probe at AUX $A000 finds missing banks and aliased banks, and it does not keep its writes. It does not test each byte, and it does not identify the firmware. Selector 127 is outside the first PSRAM chip of F1.3.2, so libtini rejects it.
Mapping and transfers
- Each public call runs in the foreground with main memory mapped, ALTZP and 80STORE off, and selector 0. Calls are not reentrant.
- A CPU transfer burst moves a maximum of 128 bytes. It saves the processor status, masks interrupts, copies its parameters to main zero page and selects the bank. It runs the copy loop from the trampoline at $0140. It restores main memory and selector 0, and then the status.
- If interrupts are enabled in the caller, they are enabled again between bursts. The limit of a burst is a byte and instruction count.
- RAMRD also changes the source of instruction fetches. Thus only the page-one trampoline runs while auxiliary reads are mapped.
- All screen writes are bank 0 transfers. A bank 0 transfer uses the selector value 0 that is already set, and it does not write $C073. Each access to $C070-$C07F also starts the paddle timers.
- C code never gets a pointer into a bank.
Interrupts and NMI
- An interrupt handler must not call libtini. It must not change memory mappings or the selector, use the libtini zero page or read granted banks. A C handler in switched memory is not safe.
- The CPU cannot mask an NMI. An NMI handler must run safely while auxiliary reads are mapped. It must keep each scratch location and obey the same rules.
- The libtini handler is the audio clock. It is assembly that libtini registers through ProDOS. It counts ticks and acknowledges the timer. It uses no C, no zero page, no mapping changes and no MLI calls.
- During a declared gap, the handler declines each interrupt before it accesses slot I/O.
- The hardware stack has 128 bytes, $0180-$01FF. These bytes must hold the deepest call chain of the program and the ProDOS interrupt dispatch. Measure the two stacks when you add depth or interrupt nesting.
The screen
ti_gfx_begin saves all of AUX bank 0 from $2000 to $9FFF. It sets fake SHR on: it writes $C1 to $C029. ti_gfx_end writes $01 and restores each byte.
Surfaces stay in banked memory. They go to the screen only through CPU writes. When a publication returns, its writes are complete. There is no page swap, no promise about the HDMI frame and no deadline. libtini checks the byte budget before it writes. It reports a deadline overrun after the writes.
ProDOS during a video session
The ProDOS /RAM disk can use the same AUX bank 0 bytes as the picture. A mode change alone does not make these bytes safe. Thus each file call during a session does these steps:
- It saves the picture to a temporary resource of 32,768 bytes.
- It sets SHR off and restores the bytes of the OS.
- It calls ProDOS.
- It saves the changes of the OS into the session backup.
- It restores the picture and sets SHR on.
Audio init and shutdown do the same steps around the ProDOS interrupt calls. If libtini cannot allocate the temporary resource, the call fails before ProDOS runs. libtini does not disconnect or format a /RAM device. Handlers that use auxiliary memory while SHR is on are not permitted.
Devices
| Device | Use |
|---|---|
| Slot 4 Mockingboard or Phasor | The audio driver owns it from ti_audio_init to ti_audio_shutdown: the two VIAs and timers, all AY chips and the two SSI-263 chips. The entry personality is Mockingboard. Shutdown restores it. |
| Slot 7 SmartPort | Storage, through ProDOS. The optional AMEM build also uses its expansion ROM space and FIFO for short times, with interrupts masked. |
| Keyboard, buttons, paddles | Standard //e locations: $C000, $C010, $C061-$C065, $C070. |
| Slot 2 | Not used. ti_mouse_read is reserved and reports that the mouse is not available. |
libtini does not change slot assignments or speed settings. It uses only firmware services that F1.3.2 has.