Skip to content

What's fitted

Your ACE has everything. That is what the machine is: a complete computer on one board, video, sound, storage, serial, clock, sticks and banked RAM all present.

Your program will also run on a COB with three cards in it, or on an emulator someone started headless, or on a machine whose owner has pulled the sound card to fix something. One byte tells you which of those you are on, and checking it costs four instructions.

The byte

At power-on the Kernal walks the eight hardware slots, works out what answers, and writes the result to HW_PRESENT at $030D. One bit per slot, set means fitted.

BitMaskSlotWhat's there
0HW_RAM_L1AS6C4008 banked SRAM (low)
1HW_RAM_H2AS6C4008 banked SRAM (high)
2HW_RTC3DS1511Y
3HW_CF4CompactFlash (8-bit True IDE)
4HW_SC5R65C51 / W65C51 ACIA
5HW_GPIO6W65C22 VIA
6HW_SID7MOS 6581 SID / ARMSID
7HW_VID8TMS9918A / Pico9918
asm
  lda HW_PRESENT
  and #HW_SID
  beq NoSound                   ; play it silent, then

The same byte is what MEM prints as HW=$xx from BASIC, and what PEEK(781) reads.

The program that lists them

asm
; What's fitted — the machine builds a list of its own cards at power-on and
; leaves it in one byte. This reads that byte and names every card it found.
;
; Guarding your own code the same way the Kernal guards its own is the whole
; point: a program that checks before it draws keeps running on a machine with
; no video card instead of hanging.

.setcpu "65C02"

.include "6502.inc"

.segment "CODE"

BasicStartup:
  .byte $0A, $08, $0A, $00, $A5, $32, $30, $36, $30, $00, $00, $00

Start:
  lda #<Header
  ldy #>Header
  jsr PrintStr

  ldx #0
NextCard:
  lda CardName_lo,x             ; the name is padded, so the column lines up
  ldy CardName_hi,x
  jsr PrintStr                  ; PrintStr keeps X for us

  lda Mask,x
  and HW_PRESENT                ; the byte the power-on probe wrote
  beq NotFitted

  lda #<Fitted
  ldy #>Fitted
  bra Report

NotFitted:
  lda #<Missing
  ldy #>Missing

Report:
  jsr PrintStr
  jsr PrintCRLF
  inx
  cpx #8
  bne NextCard
  rts

Mask:
  .byte HW_RAM_L, HW_RAM_H, HW_RTC, HW_CF
  .byte HW_SC, HW_GPIO, HW_SID, HW_VID

CardName_lo:
  .lobytes Ram1, Ram2, Clock, Card, Serial, Input, Sound, Video
CardName_hi:
  .hibytes Ram1, Ram2, Clock, Card, Serial, Input, Sound, Video

Header:  .byte "WHAT THIS MACHINE HAS", CHAR_CR, CHAR_LF, $00
Fitted:  .byte "YES", $00
Missing: .byte "NO", $00

Ram1:    .byte "BANKED RAM, LOW     ", $00
Ram2:    .byte "BANKED RAM, HIGH    ", $00
Clock:   .byte "CLOCK               ", $00
Card:    .byte "MEMORY CARD         ", $00
Serial:  .byte "SERIAL PORT         ", $00
Input:   .byte "KEYBOARD AND STICKS ", $00
Sound:   .byte "SOUND               ", $00
Video:   .byte "VIDEO               ", $00

On a complete ACE:

WHAT THIS MACHINE HAS
BANKED RAM, LOW     YES
BANKED RAM, HIGH    YES
CLOCK               YES
MEMORY CARD         YES
SERIAL PORT         YES
KEYBOARD AND STICKS YES
SOUND               YES
VIDEO               YES

A mask table and a name table indexed by the same counter is all it takes, and the padding in the names is what makes the column line up without any formatting code.

Degrade like the Kernal does

The ROM's own habit is worth copying, because it is what makes the same ROM work across the whole family:

  • Sound — no card, no noise, no error. A game that beeps on a hit keeps playing.
  • Video — no card, and the console goes to the serial port instead. The drawing routines return without doing anything.
  • Storage — no card, and the load and save calls come back with the carry set. Nothing hangs.
  • Console — no video and no serial is the one case the machine cannot survive, because there is nothing to talk to. It stops at power-on.

The pattern in your own code: check once at the start for what you must have, and check per-call for what you can do without.

asm
Start:
  lda HW_PRESENT
  and #HW_VID
  bne HaveScreen
  lda #<NeedsAScreen            ; say so, on whatever console exists
  ldy #>NeedsAScreen
  jsr PrintStr
  rts
HaveScreen:

Which ROM am I on?

KernalVersion gives the major version in A and the minor in X. If your program uses something a particular release added, ask:

asm
  jsr KernalVersion             ; A = major, X = minor
  cmp #1
  bcc TooOld
  bne NewEnough
  cpx #5
  bcc TooOld
NewEnough:

There are three version numbers in the one ROM, and they move independently: the BIOS as a whole, BASIC, and the Monitor. KernalVersion reports the first.

The DIP switches turn cards off

The ACE has an eight-way switch bank that enables and disables each I/O section, meant for fault-finding. A switched-off card is invisible to the power-on probe, so it reads exactly like a card that is not there. That makes your detection code testable on real hardware without unplugging anything — see Your ACE.

Next: writing a cartridge.

Written for BIOS v1.5. Released under the MIT License.