14

The C64 kernel (or KERNAL as it’s known in the C64 community) routines work via a jump vector table stored in page $03xx. A common programming paradigm to create something like a "device driver" would be to hook the kernal routine by replacing the appropriate jump vector table entry with a pointer to your own custom code. So kernal calls just jump to your code instead of the routine in ROM.

What is the best approach and memory location(s) for stashing your custom "device driver" code, so that compatibility is as good as possible? Assume you want the BASIC interpreter to continue to work, normal DOS functionality to not be impacted, and minimize the chance of some other software over-writing your driver. Also, what needs to change to have the same functionality in C128 mode?

4
  • 2
    I see that the term "kernal" in my original question has been recast as "kernel". Most C64 programmers and publications used the spelling "kernal". See en.wikipedia.org/wiki/KERNAL
    – Brian H
    May 27, 2017 at 19:55
  • If you want to get pedantic the C64's kernel name is KERNAL, based on a misspelling in the early 80s. The right name is "kernel" in terms of how it was referred to internally at Commodore.
    – Joe
    May 29, 2017 at 13:30
  • 7
    There is a kernel of pedantry implicit in either correct spelling.
    – Brian H
    May 29, 2017 at 15:24
  • Actually, "kernal" was used for the entire range of Commodore 8-bit machines. People referred to the PET's kernel as the Kernal".
    – JeremyP
    May 31, 2017 at 9:42

3 Answers 3

19

There was, in my opinion, not much standardisation for things like that for the c64.

  • The simplest approach would be to put your code in the range $C000-$CFFF, and then modify the jump table at $03C0. The area $C000-$CFFF is not used by the system.
  • Another approach would be copying the content of one or both of the rom's in underlaying RAM. Modify the copied code in the RAM. Switch the memory map so that RAM is used instead of ROM.
  • A third approach (if you need much space) would be to write your routines in underlaying RAM. Modify the jump table at $03C0 to jump to the $C000 area (like in method 1). There you bank ROM out and RAM in and jump to your routine. Don't forget to bank back after finishing your routine.
  • This methods can be mixed. Use method 2 on the kernal ROM area, replace the routines by a simple bank and jump routine (that banks the basic ROM and jumps to your routine in the RAM under the basic ROM). This is very usefull if you don't want to use the $C000 area.
  • Another possibility is to lower the area used by the system for BASIC. So you will create space for your routines.

I can't help you with C128.

3
  • If I want my device driver to survive program loads, then is the RAM under the kernal ROM best? My assumption is that most programs will load into the normal RAM for BASIC, then the $C000 RAM if they need more space. So programs that actually use the kernal are unlikely to touch the RAM under the kernal, perhaps?
    – Brian H
    May 27, 2017 at 19:45
  • It's really tricky to use the ram under the rom though in normal execution. You would have to not muck with the layout of the ROM or bank switch on the fly. There is no single "best@ way to avoid all conflicts; there are no fully safe places in the C64 memory map that everyone else avoids. c000 was safe from BASIC but lots of stuff went there. the tape buffer area was smaller but also well used. Then there's the 38k for BASIC programs and data.
    – Joe
    May 27, 2017 at 21:50
  • It is hard to say which place will survive loads out of the BASIC program area. If another programmer followed the same way of thinking he will use the same places as you...
    – EL Dendo
    May 28, 2017 at 12:00
4

The best approach is replacing the whole kernal. Discarding rarely used routines and putting new ones in their place.

Nowadays Easyflash and 1541 ultimate users have the chance to replace the kernal at will without opening the c64 case and doing any soldering.

So it's a viable method.

There are two main obstacles with a software only solution. 1. Preventing other software to not mess the hooks you setup is not trivial and it will only work for certain software. Compatibility would depend on ram needed by your custom software. 2. You need to load it on every boot before loading the actual software that will make use of the custom routines.

The other solution is to create a custom cartridge. Since this introduces extra memory (rom) to the computer it will be resident at least. You still need to cope with sharing ram with the actual software that will be running on the computer but this time it's not as hard as pure software solution since you will be hosting the code on the cartridge rom. You can use rarely used I/O areas or such to increase the compatibility.

4
  • Cartridge ROM is an interesting option. A standard 8K cartridge occupies $8000-9FFF. If the cartidge boot code relinquishes control back to BASIC, then is the cartridge ROM always banked in, and so it just steals the upper 8K of BASIC program space? Can it be "banked out" by ML programs that need that space?
    – Brian H
    May 30, 2017 at 23:19
  • Yes, it's possible with a few ttl chips on the cartridge.Basically one need to build an address decoder and need a latch to store such setting.
    – Nejat76
    May 31, 2017 at 7:12
  • 1
    You can simple use an 74138 3 to 8 decoder chip. G1 = PHI2, /G2A=/IO1, /G1A=RW Since you will control just a single memory address you can use any of the 8 output pins you want supplying hardcoded (to GND or VCC) address lines or simply take an address range with supplying 3 bits of an I/O address. The output of decoder is used on the latch which is connected to databus. Of course one need to take care of the initial condition where either EXROM or GAME signal to be pulled low. Wiring reset to the CLR line on the latch would do the job I guess.
    – Nejat76
    May 31, 2017 at 7:27
  • @BrianH: Using some interesting timing trickery, it's possible to have a cartridge which replaces any or all of the ROM on a C64 by pretending to be a "Max Machine" cartridge during part of each CPU cycle, but which otherwise allows the system's memory map to behave normally. I don't think anyone knew of the trick back in the day, and from what I understand it does not work and is not adaptable to the Commodore 128 which only checks for Max Machine cartridges on startup rather than continuously.
    – supercat
    Nov 30, 2017 at 19:30
1

Following a simple approach just place your code

  • on a C64 somewhere between $C000 and $CFFF, this RAM area is unused by BASIC and always visible to the CPU
  • on a C128 somewhere between $1300 and $17FF, this RAM area is unused by BASIC and visible to the CPU in Banks 0 and 15 (but not 1)

If you want to go under ROM, on both systems, C64 and C128 you would still need at least a stub that is visible when the jump vector is called. Since this is usually done by Kernal or BASIC and since the normal target of the jump vector goes into the Kernal you can assume that

  • on a C64, at least Kernal is banked in
  • on a C128, that you have Bank 15 enabled (RAM from Bank 0 until $3FFF, above that all ROM and I/O)

So, a natural place to put your stub would be

  • on a C64 somewhere between $C000 and $CFFF
  • on a C128 somewhere between $1300 and $17FF there are also some other usable holes in the memory map like the cassette buffer, but at least on the C64 this is also a frequent place for putting sprites or machine code. Moreover, data gets erased there after a datasette operation.

The safest part probably is towards the end of this memory area, but for $C000 and $CFFF some people place the screen memory to $CC00-$CFE8 when setting the VIC to use the highest 16K.

Moreover, if you place only a stub there and hide the larger parts of your code under ROM, the safest place is probably

  • on a C64 under the char ROM/IO-area $D000-$DFFF, which is one of the least used memory areas - for a reason: if your code needs to access VIC registers or the color ROM, being in this area is a big disadvantage since you need bank switching code from outside to access this area. In this case, the better place is under the Kernal ROM, but even this could be overwritten if somebody puts a bitmap there. The area under the BASIC ROM is more often used by programs, for example cc65 compiled programs use this area at default setting
  • on a C128 I would recommend to shorten the BASIC code area a bit via pointer $39/$3A and then put the code beneath the Kernal somewhere between $E000 and $FEFF.

For pure BASIC programs this approach would be safe, even for those programs placing sprites into the cassette buffer and putting some machine code to the beginning of $C000 (on a C128 that would be $1300 respectively)

On the other hand, there is always a risk of a conflict with another program. A bitmap or screen placed on the page with your code immediately kills it and of course, somebody could have a similar idea of using the end of a memory area to place their code. Some BASIC extensions offer two memory areas, asking if they want to be installed in memory area 1 or memory area 2 at set up. This would be a way to go: a normal BASIC user just uses the default setting and somebody using exotic memory areas would get the possibility to select the memory area for installation that does not interfere with their code and data.

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .