The C128 Cartridge functionality is different than in the C64. Maximum size has been doubled to 32K and an EPROM socket (U36) has been provided inside the machine in addition to the cartridge port.
An external cartridge is referred as “External(Function ROM)” and the internal EPROM socket is referred as “Internal(Function ROM)”. This will focus on how to create an external cartridge.
A 8-bit machine can only “see” 64K at a time, and the same goes for the C128.
There are two areas in the C128 memory where cartridges can be located; the MID ($8000-$BFFF) and HIGH ($C000-FFFF). A cartridge can be started up in either these 16K areas or single 32K cartridge can be used. It is possible to have both an internal “cartridge” and an external cartridge installed, but the external has preference over the internal.
C128 memory is managed by a chip called the Memory Management Unit(MMU) and does not use the PLA(GAME/EXROM lines) like the C64.
MMU is controlled by its primary registers at $D500- and its secondary registers at $FF00-. The secondary registers are available to make it possible to switch out the I/O area at $D000-DFFF and still be able to access the MMU. This creates a memory “hole” at $FF00-$FF04 which can not be used for code.
Note that both cartridge areas (MID and HIGH) are always switched in together. If you set up a 16K cartridge in MID area and want to access Kernal routines, you will need to bank in Kernal at the HIGH space.
Cartridge Autostart
When a C128 is booted, a cartridge presence is checked. First it checks if GAME or EXROM lines are low. If this is the case, a C64 mode cartridge is connected and the machine starts in C64 mode. Otherwise the C128 memory is checked for an identifier(“CBM”) string at $8007 and $c007, this is done both for the external and internal slots. The banks are checked in the following order: External Mid, External High, Internal Mid and Internal High.
Code at $E27A- is used to verify if a cartridge is inserted:
1 2 3 4 5 6 7 8 9 10 11 |
$E27A LDY #$09 .. $E28B LDA #$9E $E28D JSR $F7D0 ; swap in bank $E290 CMP $E2BD,Y ; search for ID string "CBM" $E293 BNE $E2B6 ; ID byte,y not found $E295 DEY $E296 CPY #$07 $E298 BCS $E289 |
16K Cartridge code example at $8000(MID) with Kernal ROM banked in.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 |
* = $8000 JMP Coldstart ; coldstart vector JMP Warmstart ; warmstart vector .byte 1 ; 1 = Autostart .byte $43,$42,$4D ; "CBM" string Coldstart Warmstart sei ldx #$ff txs cld lda #$e3 sta $01 lda #$37 sta $00 lda #%00001010 ; Bank in Kernal ROM ; BIT 0 : $D000-$DFFF (0 = I/O Block) ; BIT 1 : $4000-$7FFF (1 = RAM) ; BIT 2/3 : $8000-$BFFF (10 = External ROM) ; BIT 4/5 : $C000-$CFFF/$E000-$FFFF (00 = Kernal ROM) ; BIT 6/7 : RAM used. (00 = RAM 0) sta $ff00 ; MMU Configuration Register jsr $ff8a ; Restore Vectors jsr $ff84 ; Init I/O Devices, Ports & Timers jsr $ff81 ; Init Editor & Video Chips ldx #0 txtoutr lda textout,x jsr $ffd2 ; Output Vector, CHROUT beq txtdone inx bne txtoutr txtdone jmp * textout .text $0d,"cartridge booted...",0 * = $bfff .byte 0 |
Test the code above with WinVICE:
1 2 3 4 5 |
64tass -a c128_cart.tas -o c128_cart.prg (now strip the 2 first bytes, loadadress, from the .prg and save it as .bin) WinVice\x128.exe -extfunc -extfrom c128_cart.bin |
C128 16K Cartridge $8000-$BFFF (MID)
You can add another 27128 and connect RH to /OE on that chip to get 32K.
Comments