This page describes in some detail my effort for the Retro Challence 2018/09 event.
TL;DR = make an informational demo/intro/animation for the HomeComputerMuseum in Helmond (.nl). running on a Commodore 64 ("C64"). The demo-program will be hosted on some sort of EPROM/EEPROM/flash cartridge.
The HomeComputerMuseum is an interactive computer-museum in the center of Helmond, in the south-east of the Netherlands, opened in 2017.
The museum is located on a street with fairly much pedestrian-traffic, but it's possible to walk by without even knowing what is there, or knowing what the museum is all about.
Since a number of spare C64s + monitors are available, it is perhaps a good idea to place one behind the window facing the passers-by, running some sort of attract-screen in the form of a flashy demo/intro. (Colours, logo, movement, pictures, text.) The idea is to capture the attention of pedestrians, and give them a quick idea of what they can find inside.
The C64 can run ROM-cartridges plugged into its cartridge-port. The idea is to put the attract-screen code on some sort of mini-cartridge, so the C64 will display it automatically at power-on. The cartridge-hardware can be something existing or homebrew (EEPROM/flash + PCB).
The attract-screen code will be made in 6510/6502 assembly, and will be at most 16 kb in size (the largest program-size a simple cartridge can host). It will contain flashy graphics to catch peoples attention, but won't contain sound.
Right now, there exists nothing, except some level of 6502-clue in my head. I don't have any cartridge-hardware yet.
So, my plan for RC2018/09 is to...
Right now this is all pretty vague, so expect the above list to change along the way.
There is nothing yet! I'll be busy with work for the coming 2 weeks, so expect me to not start before half of September. Don't cry yet.
TL;DR = decided to use script-generated PPM-pictures to prototype simple visual effects, and to use a stripped-down version of the Bait-a-Cart C64 cartridge-adapter to make the final demo-code available to the C64 as a plug-in cartridge.
I briefly looked around to see whether a C64 EEPROM/flash cart able to host custom code was available, but the existing solutions were either too much overkill or too expensive to just stick into someone elses C64 and leave it there forever. ;-)
For the previous RetroChallenge, I already made firmware for a C64 Bait-a-Cart C64 cartridge-adapter.
Although the final result had some serious hardware-flaws, it can mimic a normal ROM-cartridge quite well - this basic behaviour is not affected by any of the flaws listed. Yay.
Since the relatively big microcontroller on the Bait-a-Cart (AVR, ATmega640) has lots of flash-memory on board, the simplest way to go, I think, is to...
(This approach is a bit cumbersome, but avoids serial communication to the host-PC, which is... a bit broken and would require soldering/rework. I expect to have to do this only a few times, since the C64-demo can be fully tested in an emulator instead of on real hardware.)
The C64-demo will mainly consist of graphical effects.
Today's availability of cross-development tools have made C64-programming much easier than it once was, at least for me. However, having to code a visual effect in 6510-assembly just to see if it looks nice or not, can be a timesink.
Therefore, I would like to use generated
PPM bitmaps/frames in combination
with ImageMagick's convert
to make animated GIFs out of
separate frames.
The advantage - for me - of using PPM, is that an picture can be hand-crafted or generated from text-utilities / shellcode extremely easy - a picture is readable plaintext.
For the C64's 16-colour palette, I found these RGB-values somewhere on the web a while ago (sorry, forgot where):
R G B
0 / black : 0 0 0
1 / white : 255 255 255
2 / red : 136 0 0
3 / cyan : 171 255 239
4 / purple : 204 68 204
5 / green : 0 203 85
6 / blue : 0 0 170
7 / yellow : 238 238 118
8 / orange : 222 136 85
9 / brown : 102 67 0
10 / pink : 255 119 119
11 / darkgrey : 51 51 51
12 / midgrey : 119 119 119
13 / lightgreen : 170 255 102
14 / lightblue : 0 136 255
15 / lightgrey : 187 187 187
They look like this:
If interested, this is the same palette for use in the GNU Gimp program:
GIMP Palette
Name: C64_bright
Columns: 0
#
0 0 0 Untitled
255 255 255 Untitled
136 0 0 Untitled
171 255 239 Untitled
204 68 204 Untitled
0 203 85 Untitled
0 0 170 Untitled
238 238 118 Untitled
222 136 85 Untitled
102 67 0 Untitled
255 119 119 Untitled
51 51 51 Untitled
119 119 119 Untitled
170 255 102 Untitled
0 136 255 Untitled
187 187 187 Untitled
(I guess you can drop this into ~/.gimp-*/palettes/)
To generate the picture below containing the full C64 colour-palette...
...the following shellcode can be used:
#!/usr/bin/env bash
. ../common.inc
BARHEIGHT=10
writeHeader $[ ( $BARHEIGHT + 1 ) * 16 ]
for colour in $( seq 0 15 ); do
writeLines $colour $BARHEIGHT
writeLine $WHITE
done
(A "line" in this context means "C64 rasterline", i.e. a 1-pixel high horizontal line spanning the whole screen.)
This script makes use of "common.inc" given verbatim at the end of this page, containing some utility-functions and palette-definitions.
Using a script "animate_bar" repeatedly calling script "paint_bar", PPM-pictures of a rotating horizontal bar are generated, and combined into an animated GIF-picture:
(Both scripts are given verbatim at the end of this page.)
TL;DR = visual composition will consist of a logo- and text-area, separated by a spacer (the rotating bar from a previous post).
Since the animation would normally be viewed from a few meters away by passers-by, it probably makes sense to show relatively large effects instead of itty gritty details.
So, what I would like to do, is to divide the screen into logo- and text-area, separated by a horizontal spacer (e.g. the rotating bar from a previous post). Impression:
The numbers on the right indicate height of that area, in characters. The screen-boundary is indicated by a grey rectangle - beyond that is the border. I am not clued enough, and also too lazy, to place anything other than raster-graphics there. (The grey rectangle as well as the orange ones will of course not be shown in the actual demo.)
The logo would swing left to right and up and down inside the orange rectangle around the logo, and the spacer would rotate and swing up/down, inside the lines above and below the spacer.
To make it a bit less boring, top- and bottom-halves (logo and text) would occasionally exchange place due to some transition-effect.
The logo will consist of a monocolour-rendering of the museum's logo seen at the top of this page:
Size is 200x48 pixels, or 25x6 characters. (A character on the C64 is 8x8 pixels in size.) Rounded border and letters of the logo, shown here in pink, could be displayed in different colours, or could go through some fade-in/-out effect.
The coloured bars will probably be done using 5 double-width sprites (48 pixels each), so they can wobble around a bit inside the logo.
Well... an unspectacular charset for displaying informative text:
At least it's readable. (Background-colours just to show char-boundaries - will be gone in the final result.) This pic is made in GNU Gimp. I'll use a Ruby-script to convert it to charset-data.
TL;DR = pixeled some small retro-related bitmaps, and made a priority-list in case I run out of time to finish everything before the end of September...
To make the text-area of the demo a bit less boring, I made the following drawings. They are approximately 40 characters (320 bytes) each.
Something resembling a C64 breadbox:
Classic Mac:
A nice compact cassette tape (if you don't remember these, you missed out on so much):
So... we're almost halfway in, and I'm nowhere near halfway done.
PANIC!!! :-)
To increase the likelyhood I will actually finish something around the end of September, let's order all intended demo-effects according to necessity. From "vital" to "meh":
Something like that. Probably makes little sense when reading it in a boring list like this - we need pictures!
Hardware-part of the demo (i.e. getting it into a physical cartridge) needs to be done no matter what. I still have 1 PCB left over from RC2018/04, and just ordered components, so nothing can possibly go wrong there (...) - PCB-assembly will probably happen somewhere this weekend.
TL;DR = populated leftover Bait-a-Cart PCB and tested it in an actual C64 with minimal cartridge-code
Placed only necessary components onto a Bait-a-Cart PCB, and omitted some components (purple areas) that weren't necessary to host a single cartridge-image:
Omitted:
Shown is the underside of the PCB.
This is serious overkill for what could have been an EEPROM-/flash-board BTW.
To test this thing in an actual C64, I used a simple loop toggling the background colour, namely the "minicart" code ripped out of the original Bait-a-Cart source:
* = $8000
.word Start ; cold-start vector
.word Start ; warm-start vector
.byte $c3, $c2, $cd, $38, $30 ; "CBM80"
Start:
lda #4
sta $d020
lda #0
sta $d020
jmp Start
* = $9fff ; fill
.byte $0
Or if you prefer hex/pain:
0x09, 0x80, 0x09, 0x80, 0xc3, 0xc2, 0xcd, 0x38, 0x30, 0xa9, 0x04,
0x8d, 0x20, 0xd0, 0xa9, 0x00, 0x8d, 0x20, 0xd0, 0x4c, 0x09, 0x80,
0x00, ...
:-)
Since I gave away all of my C64-stuff a while ago, I had to actually go to the HomeComputerMuseum to test this. It worked straight away, so I was happy. Forgot to take a picture, but here's a VICE screenshot:
Oh BTW, on the Bait-a-Cart page, I was wondering why VICE had apparently already sort of enabled the video before cartridge-code was started, shown by a black inner screen like this:
Turns out that using x64sc
(accurate C64 emulator) instead of x64
(fast C64 emulator) fixed this.
(The black inner screen is not present when running this code on real hardware.)
If you found this text, I guess you know this page (now) gives progress in chronological order, because it probably makes more sense - at least in my head.
TL;DR = no actual work done except writing some minimal code, deciding on memory-layout and re-reading how VIC II banks worked again
Ok... brain violently protested against actual RC-work being done, so here we are - some more fluffy background-/meta-info.
Behold, a floppy. Not quite that retro, but it will do, I guess.
(Pictures like this are meant to occur within the info-text half of the screen, and make it look a bit less boring.)
So far, we have (width and height given in 8x8-pixel characters)...
The maximum number of characters per picture is thus 40.
Overall, the demo should show a logo and a big 2x2 font adorned with small pictures like the floppy above. (Pictures of these are shown in a previous post.)
I would like textmode and custom character-set(s) instead of bitmap, mainly to save on size. Doing the math to see how many characters would be used in total:
...we can conveniently fit everything into 1 charset (256 chars):
logo 58
big font 154
whitespace 1
picture-slot 40
----- +
253
Lo and behold, we even have 3 characters free. What a waste.
So, by copying 40-char-max pictures into a reserved slot within a single charset - when requested during runtime - we can stuff everything into 1 charset. Is this really an advantage? Hardly, but that's just what I wanted to do now. (Can save a tiny bit of work during debugging, bitmap-conversion and programming.)
Furthermore, we could get rid of one 40-character picture. This would leave 3x40 = 120 chars, or 960 bytes for picture-data. Since this is conveniently almost 1 kb, we could layout charset (2 kb), screenchar-memory (1000 bytes) and picture-data as follows:
$4000: screenchar-memory (1000 bytes)
$4400: picture-data (960 bytes)
$4800: charset (2 kb)
($5000: writable data)
(Screenchar-memory needs to be aligned on 1 kb boundary, and charset needs to be aligned at 2 kb boundary, so it all fits nicely.)
To be honest, I forgot how the C64 / VIC II handles memory, so I had to re-read this from existing code. was helpful to show which of the 4 selectable VIC-banks corresponded to which memory-regions, with which quirks (picture from their site):
As can be seen, VIC bank 1 ($4000-$7fff) is nice, since it doesn't have a copy of the C64's ROM charset and sits conveniently outside of the ROM-cartridge space ($8000 onwards). So let's use bank 1, at $4000, not just for graphics but all writable data too.
This is not rocket-science. In my previous Bait-a-Cart C64 software, I specified logical (runtime-) address for data to be in the $4000 area, while the ROM-image including code was sitting at $8000. This allowed the code to copy parts of its own image to RAM upon start:
I use 64tass as assembler, for which I wrote a
quick reference earlier. </plug>
. Using the .logical
....here
directive, you can
use different runtime- and assembly-time locations for emitted code and data:
VIC_BANK_BASE = $4000
...
* = $8000
.dsection code ; All code-sections are emitted from $8000 onwards.
DATA_ROM_START = * ; Remember start of data-in-ROM-image region to be able to copy this to VIC_BANK_BASE ($4000) at program start.
.logical VIC_BANK_BASE ; Runtime-address for data-in-ROM is at VIC_BANK_BASE ($4000).
.dsection data
.here
DATA_ROM_END = * ; Remember end of data-in-ROM-image region to be able to copy it at program start (see above).
Right, this is all I did for now - simple code to verify the correct VIC-banks are selected.
Looks like garbage? Yes, that's right. To be continued, hopefully.
There's no way I'm gonna finish this project in time for RC2018/09. What I want to do is continue with it, and perhaps add some snapshots of progress below this line - the Line of Shame.
Or else, make a YT-video of the result. Or else, do nothing.
(-:
palette=(
" 0 0 0"
"255 255 255"
"136 0 0"
"171 255 239"
"204 68 204"
" 0 203 85"
" 0 0 170"
"238 238 118"
"222 136 85"
"102 67 0"
"255 119 119"
" 51 51 51"
"119 119 119"
"170 255 102"
" 0 136 255"
"187 187 187"
)
BLACK=0
WHITE=1
RED=2
CYAN=3
PURPLE=4
GREEN=5
BLUE=6
YELLOW=7
ORANGE=8
BROWN=9
PINK=10
DGREY=11
MGREY=12
LGREEN=13
LBLUE=14
LGREY=15
IMGWIDTH=320
function writeLine () {
colour=$1
for i in $( seq $IMGWIDTH ); do echo -n "${palette[$colour]} "; done
echo
}
function writeLines () {
colour=$1
N=$2
if [ $N -gt 0 ]; then
for i in $( seq $N ); do writeLine $colour; done
fi
}
function writeHeader () {
imgheight=$1
echo "P3"
echo "$IMGWIDTH $imgheight"
echo "255"
}
#!/usr/bin/env bash
# WHAT IS THIS?
#
# This script takes as arguments an angle in degrees [0..90) and 2 colours, and
# generates a picture showing a horizontal 4-sided bar, rotated around its axis
# by the given angle.
#
# The 2 colours given are applied to the "front-/top-facing" and "bottom-/front-facing"
# sides of the bar, to allow for some sort of shading-effect.
. ../common.inc
if [ $# -lt 3 ]; then echo "expect 3 args"; exit; fi
ANGLE=$1
TOPFACECOLOUR=$2
BOTFACECOLOUR=$3
RAD=$( echo "$ANGLE * 3.14159 / 180" | calc -p | tr -d \~ )
IMGHEIGHT=200
BARSIDELENGTH=15
SPACERHEIGHT=2
SPACERCOLOUR=$BLACK
ABOVEBARCOLOUR=$BROWN
BELOWBARCOLOUR=$LGREY
TOPFACEHEIGHT=$( echo "round( $BARSIDELENGTH * cos( $RAD ) )" | calc -p | tr -d \~ )
BOTFACEHEIGHT=$( echo "round( $BARSIDELENGTH * sin( $RAD ) )" | calc -p | tr -d \~ )
ABOVEBARHEIGHT=$( echo "( $IMGHEIGHT / 2 ) - round( ( $TOPFACEHEIGHT + $BOTFACEHEIGHT ) / 2 ) - $SPACERHEIGHT" | calc -p )
BELOWBARHEIGHT=$( echo "$IMGHEIGHT - ( 2 * $SPACERHEIGHT ) - $TOPFACEHEIGHT - $BOTFACEHEIGHT" | calc -p | tr -d \~ )
function writeSpacer () { writeLines $SPACERCOLOUR $SPACERHEIGHT; }
writeHeader $IMGHEIGHT
writeLines $ABOVEBARCOLOUR $ABOVEBARHEIGHT
writeSpacer
writeLines $TOPFACECOLOUR $TOPFACEHEIGHT
writeLines $BOTFACECOLOUR $BOTFACEHEIGHT
writeSpacer
writeLines $BELOWBARCOLOUR $BELOWBARHEIGHT
#!/usr/bin/env bash
. ../common.inc
rm *.ppm *.png *.gif
echo "generating frames..."
./paint_bar 0 $CYAN $PINK > 00.ppm
./paint_bar 15 $CYAN $RED > 01.ppm
./paint_bar 30 $WHITE $RED > 02.ppm
./paint_bar 45 $WHITE $RED > 03.ppm
./paint_bar 60 $WHITE $PINK > 04.ppm
./paint_bar 75 $CYAN $PINK > 05.ppm
./paint_bar 0 $YELLOW $LBLUE > 10.ppm
./paint_bar 15 $YELLOW $BLUE > 11.ppm
./paint_bar 30 $WHITE $BLUE > 12.ppm
./paint_bar 45 $WHITE $BLUE > 13.ppm
./paint_bar 60 $WHITE $LBLUE > 14.ppm
./paint_bar 75 $YELLOW $LBLUE > 15.ppm
echo "animating..."
convert -delay 6 -loop 0 $( ls *.ppm | sort -n ) out.gif