If you thought my last post about a 32 byte (plus 2 byte load address) Commodore 64 demo was esoteric, wait until you burrow into this one.
Back in March at Lovebyte I released a C64 demo that is a total of 6 bytes. I contrived this one so that the 4b of code ends up overwriting part of a zero-page routine that runs every time RETURN is pressed. The effect is a pulsing pattern on the border. (You can just as easily make the screen pulse, which I personally find less aesthetically pleasing because the pulsing in that case happens over any text that is on the screen. It’s also a bit more eye watering and more likely to trigger seizures.) While it’s a very simple effect, I don’t know of any demo at all for this platform that has this file size or any smaller one. Some extensive trickery was involved in injecting my code into existing memory contents to produce this effect.
You can download the demo, NONMONOCHROME.
For my several readers who enjoy reading 6502 assembly, here’s what I did:
; NONMONOCHROME
; 6b C64 demo (2b header + 4b)
; by nom de nom/TROPE
; RETURN to begin.
;
; This demo loops about every 1.45 seconds (on an NTSC
; machine).
;
; Holding down a key while this runs will produce an extra
; effect.
;
; RUN STOP - RESTORE will stop the program, but as soon as
; RETURN is pressed the demo will just start again. To
; restore normal function, it's necessary to power-cycle
; the C64.
.outfile "nonmonochrome"
.word $007e ; Wedge the code into CHRGET on the zero page.
.org $007e ; Note that CHRGET itself begins at $0073.
sta $d020 ; Set the border color with whatever's in the
; accumulator. The value varies as the code from $73
; through $7e runs. It oscillates between black and white
; for one phase and colorful for another phase. A detailed
; explanation of why is provided below.
.byte $50 ; This makes the next instruction bvc, clobbering
; $20. The following value in memory, $f0, had been used as
; beq. $50 $f0 = bvc $73, which (infinitely) takes us back to
; the beginning of CHRGET at $0073.
; A disassembly of CHRGET, left, and how it's been modified,
; right:
;
; .org $0073 .org $0073
;
; chrget inc txtptr nonmono inc txtptr
; bne chrgot bne chrgot
; inc txtptr+1 inc txtptr+1
; chrgot lda chrgot lda
; txtptr .word $0207 txtptr .word $0207
; pointb cmp #$3a cmp #$3a
; bcs exit sta $d020
; cmp #$20 bvc nonmono
; beq chrget
; sec
; sbc #$30
; sec
; sbc #$d0
; exit rts
; CHRGET keeps increasing TXTPTR until it reaches the end of
; whatever BASIC input it's reading -- whether in immediate
; mode or when reading a BASIC program.
;
; NONMONO, however, loops forever and keeps increasing TXTPTR
; until it overflows past $ffff back to $0000.
;
; Because of the LDA TXTPTR, the accumulator is being
; consecutively loaded with each of the bytes in the C64's
; 64kb of accessible RAM and ROM. Then, after the CMP
; (which does nothing but slow the demo down by two cycles
; per loop), the contents of the accumulator are stored
; so as to set the border color.
;
; A large region of the C64's memory has $0 or $1 as the
; low nibble -- at least at power on, with no BASIC program
; loaded. Thus, black or white. Then, another large region
; (including ROM and the zeropage) has more "interesting"
; contents which manifest themselves as colors.
My demo was also taken up as an object of discussion in the first 7 minutes and 30 seconds of this YouTube video, where some of this explanation is provided as the demo runs on a C64c. This a later model of the Commodore 64, not the system I used in testing it, so the pulsing effect is slightly different. Among other things, my name for the demo, NONMONOCHROME, makes less sense when it’s running on this computer!
It could be an interesting challenge for others to try writing a Commodore 64 demo of only 6 bytes total, or of fewer bytes. For reasons I’d be glad to discuss, it seems to me that injecting the code into some productive context (an existing routine such as CHRGET or elsewhere in memory) is the only workable approach.
I was fascinated to find recently that way back in 2002, White Flame released a demo called 4byte that uses the same trick of modifying CHRGET to create the same sort of loop. And the demo is the same size: 4 bytes + 2 bytes load address.
So I was almost 20 years too late with this particular trick! I did improve the effect so that there is a more complex type of pulsing. But I must admit that some plagiarism by anticipation took place.