I wanted to write this post to clarify the competitions technical rules as there seems to be a little confusion over them. Also to show how you can test your entries to see if they conform to the rules. And finally give some pointers to help you make the most of the space you have available.
First up the technical rules.
At first glance the rules may seem strict & confusing. But while they are somewhat strict they should hopefully become less confusing with some explanation.
The most important thing to note is that you can enter this competition at various skill levels. Games in BASIC are perfectly valid as long as they conform to the memory limitations. Simple assembly programs with no memory trickery are of course allowed too. But if you want to take up the maximum challenge, see how close you can get to 4kb and how many tricks you can use to pull it off!
So here are some clarifications:
2. Final code must be in .PRG format that loads into memory below $1000 hex, giving 4,096 bytes of address space.
For all games we expect them to be in PRG format and be able to load and run from VICE. The PRG can load at any address below, and must not run past $1000. The idea here is to create limitations which force the creation of Cassette 50 style games, but to do something other than just saying "You have 4kb" in order to create some intrigue for the more technical coders. Some valid examples:
- Assembly program saved as PRG that loads at $0801 (As most using some form of Kick Assemblers BasicUpstart do) and contains $7ff bytes. This program therefore loads into $0801 - $0fff.
- 20 line BASIC program saved as PRG. This will load in at $0801 and will be short enough to not run past $1000
- Assembly program saved as PRG that loads at $0120 and uses $edf bytes. This program loads from $0120-$0fff
All of these examples are possible to have run in VICE, although the third example requires some trickery which I will explain in a later part of this post.
3. At no point can the code write to any location at or above $1000, however using IO normally at $D000-$DFFF is allowed, as is calling BASIC or KERNAL routines. BASIC programs are welcome too, just be sure to start your program with POKE 56,16 so it conforms to the memory limits.
This is the rule that seems to have caused the most confusion, but its actually quite simple. It simply means that you must keep all your memory writes below $1000, therefore:
- All your variables must be below $1000 and are probably best stored in zero page
- Your screen ram cannot be moved beyond $1000 so is probably best left at $0400
- All your assets, should you choose to use custom font or sprites must also live below $1000
- You cannot unpack or unroll anything into space at or above $1000
- You cannot use $fffe/$ffff to set interrupts as this is writing above $1000. Use $0314/$0315 instead
The ONLY exception to this rule is accessing IO area, that is memory between $d000-$dfff when and ONLY when it is banked in as IO area ram (which it is by default). You CANNOT bank out IO area RAM and use the space underneath!! This means it is OK to:
- Write to any VIC registers such as border color $d020, sprite registers $d000, $d027 and so on
- Write to any SID registers e.g. $d400+
- Write to Color RAM at $d800+
This rule also does NOT prevent you from:
- Reading memory at any addresses at or above $1000, if there's a set of values you can use that exist in the BASIC or Kernal, go for it! Just don't write there!!
- Calling methods in BASIC or kernal. More on this later on!
If you are writing a BASIC program makes sure the very first line is POKE 56,16. This ensures that your program and variables can never go past the $1000 limit.
Testing your game against these rules should you wish to, is actually quite straightforward. I will be testing all games this way to ensure they conform. (Additionally I will be checking for any sneaky IO banking using $01!!!). I will be using VICE 3.4 for all testing and recommend that everyone should do the same.
Start VICE and open up the monitor (Alt-H in the latest v3.4) and type the following lines pressing ENTER after each:
w exec f5da g
This sets a breakpoint at the point when the machine is ready to load (via virtual fs or copy to d64). Now drop your PRG onto the VICE window to load it and you should see the monitor reappear due to the breakpoint we set. Now we can apply the memory rules. Type the following lines into the monitor pressing ENTER after each:
w store 1000 cfff w store e000 ffff g
Your game should now load as normal, but if at any point you write to memory above $1000 it will bring up the monitor again and this will indicate the game breaks the rules.
#1 - BASIC programs are more than enough and definitely in the spirit of Cassette 50
If you take a look at some of the 10 liner C64 BASIC competitions, there are some great games there in only 10 lines!! The C64 can only fit 80 chars on a line, so these programs take less than 1Kb to store in memory. Variables do take a little bit of memory at runtime but as long as you use the POKE 56,16 to contain everything below $1000 you should be able to create some awesome results, just take a look at some of the 10 line competition winners over the years:
For BASIC programs to conform you must limit the memory available to BASIC by doing POKE 56,16 before running the program or before a CLR. You can include it as the first line like so:
1 POKE 56,16: CLR 10 PRINT "MY AMAZING PROGRAM HERE"
#2 - Using routines from Kernal and BASIC
Some things you might want to do already exist in BASIC and Kernal, though you usually have to sacrifice some of zero page and some of the area at$200-$2ff for them. Check out the ROM disassemblies at https://www.pagetable.com/c64ref/c64disasm/ for more details and to work out what locations you need to sacrifice. But here are some examples:
Clearing the screen and fill color ram with RED, obviously you can preload 0286 and 0288 if you want:
lda #$02 sta $0286 lda #$04 sta $0288 jsr $e544
Note: It has been pointed out that on older kernal the color is taken from $d021, so you would want to load the color value there instead. (thanks to kakefoni for this info!)
Shift screen contents up 1 row, again preload 0288 if necessary:
lda #$04 sta $0288 ldx #$19 jsr $e967
Load the accumulator with a random number 0-255:
lda #$01 jsr $e09a lda $63
There are many more useful routines I suggest browsing through the disassemblies of the ROMs linked above to see whats there and what you need to sacrifice to use them.
#3 - Using screen ram for code and other data:
One sneaky way to get more space for code is to reduce the size of your screen area. You could sacrifice a few lines at the top and bottom of the screen to put code into instead, filling color ram at that location to hide the resulting garbage. Remove 4 lines from top and bottom and you can save an extra 320 bytes.
If you additionally squash horizontally by reducing the screen width by 8 on each side you have room for some predefined tables or even some custom fonts, a screen that's 18 high and 24 wide will give you enough room on the sides to hide an enough custom font for alphabet and numbers 0-9, while still providing 280 bytes of free space at the top and bottom too!
#4 - Using very low memory locations
The following kick assembler snippet shows how you can utilise memory way below the usual $0801 area:
//Autostarting PRG code under $1000 // 3767 bytes free for code // 254 zero page bytes free for variables // 18 bytes at $1ed for variables * = $120 Entry: ldx #$1f txs //Push stack down to $100-$11f //Start your code HERE (205 bytes up to $1ec) inc $d020 jmp *-3 * = $1ed //This area can be used for runtime vars //Once prg is loaded (1ed-1ff = 18 bytes) //But is bashed by the loading process * = $1f8 //Override return from load vector on stack .byte <[Entry-1], >[Entry-1] * = $200 //143 free bytes here .fill $8f, 0 *=$028f .byte $48,$eb //Prevent keyboard jam //131 free bytes here .fill $83, 0 * = $314 //IRQ, BRK and NMI Vectors .byte $31, $ea, $66, $fe, $47,$fe //Keep the following vectors also .byte $4a,$f3,$91,$f2,$0e,$f2 .byte $50,$f2,$33,$f3,$57,$f1,$ca,$f1 .byte $ed,$f6 //STOP vector - Essential to avoid JAM //3286 free bytes here for your pleasure! .fill $cd6, 0
This works by overriding the stack return pointer for the load routine, which can be found at $01f8 and pointing it instead to a new Entry point found in the lower end of the stack. This entry point then moves the stack pointer to $011f reducing the stack to 32 bytes (can of course be adjusted as need be). At this point you now have 3767 free bytes between $123 and $fff where you can place anything (currently just filled with zeros). Note I used VICE 3.4 for this and recommend that everyone should do the same (VICE 3.1 has been reported to crash with this code, thanks Docster for the info)
Hopefully that's helped to alleviate some of the fears some people had about the requirements and given a few useful pointers to those that wish to enter.