Buckley

Found on:
"Chuckie Egg 2"
"Colony"
"Dark Empire"
"Legions Of Death"
"Milk Race"
"Molecule Man"
"Pro Mountain Bike Simulato"
"Psycho Hopper"
"Xenon"

Loader files:
ROM Header 1
Contains only filename
ROM Data 1
Autostart and uses standard Kernal routine to load next file which it then starts

ROM Header 2
Contains only filename
ROM Data 2
Contains the turbo loader

Encoding:
Endianess:MSbF
Threshold:0x190
Lead-in:100 x %011111111 (<-9 bits)
Sync:0x00

Structure:
Header:9 bytes
Checksum:Yes
Header:
00-01   ?? (Unused) One of them should be the checksum cancellation byte.
02      Data checksum
03-04   Changes a jump vector used by the loader. Triggering new code to be run after each file.
        Basically displays the intro picture and after last file clears it and starts the game.
05-06   Bytes to load, High/low order.
07-08   Load start address, High/Low order

Checksum:
Header checksum. Sum of all header bytes should be zero.
Data checksum. Sum of all bytes including header bytes.
The data checksum byte is in the header. Loader doesn't check it or even calculates it.

Loader uses nine bits per byte.
The first bit whch would have been the most significant one is just rotated out of the byte
and ignored. This bit always seems to be a '0' bit.
Loader also inverts loaded bits, so passig threshold means a '0' bit.

Checksum:
Chuckie Egg 2 is slightly different from the rest.
It calculates the number of bytes to load differently and therefore loads 1 byte more than what the header says.

Three releases of Chuckie Egg known so far, Original release by A'n'F, Now Games 2 compilation and the Icon Design re-release.
All three load one addional byte.
The original release by A'n'F and the Now Games 2 release uses a different threshold, 0x01f4.

.pc = $1153

.label bitCount = $cb
.label readByte = $ce
.label checksum = $cf
.label SCROLY = $d011
.label RASTER = $d012
.label SCROLX = $d016
.label VMCSB = $d018
.label IRQMSK = $d01a
.label EXTCOL = $d020
.label BGCOL0 = $d021
.label CIAPRA = $dc00
.label CIDDRA = $dc02
.label TIMALO = $dc04
.label TIMAHI = $dc05
.label TIMBLO = $dc06
.label TIMBHI = $dc07
.label CIAICR = $dc0d
.label CIACRA = $dc0e
.label CIACRB = $dc0f
.label CI2PRA = $dd00
.label C2DDRA = $dd02
.label TI2ALO = $dd04
.label TI2AHI = $dd05
.label TI2BLO = $dd06
.label TI2BHI = $dd07
.label CI2ICR = $dd0d
.label CI2CRA = $dd0e
.label CI2CRB = $dd0f

// Autostart code jumps here
START:jsrinitLoad// Sets up environment, timers, IRQ etc.
 lda#$ff
 staCIDDRA
C_115b:lda#0
 staCIAPRA
 ldaD_13ff// value is 0 from start
 cmp#$03
 bneC_116f
 jsr$e000
 lda#$02
 staD_13ff
C_116f:ldaD_13ff
 beqC_1177
 jsrC_13e0
C_1177:jsrC_117f
 bneC_115b
 jmpC_1189

C_117f:lda$cd// Starts as $ff from initialization code @1198
 cmp#$04
 bneC_1186
 rts

C_1186:cmp#$03
 rts

C_1189:lda$01
 ora#$20
 sta$01
 lda#$7f
 staCIAICR
 nop
C_1195:jmp($00c4)// $c4/$c5 is part of file header area, so this vector is changed for every file loaded.
// These are the values that will be loaded
// $13B7 $1352 $1153 $1153 $139E

//
// INIT environment
//
initLoad:lda#$05
 sta$01// Disconnect Kernal
 lda#0
 staEXTCOL
 lda#$56
 staC_124d+1// Modifying JMP instr
 lda#$12
 staC_124d+2// Modifying JMP instr
 lda#$7f
 staCIAICR
 staCI2ICR
 lda#0
 staCIACRA
 lda#$48
 staCIACRB
 lda#0// Timer A = $0008, Timer B = $ffff
 staTIMAHI
 lda#$ff
 staTIMBHI
 lda#$08
 staTIMALO
 lda#$ff
 staTIMBLO
 lda#$ff
 sta$cd
 lda#0
 stareadByte
 lda#$1e
 sta$ca
 lda#0
 staIRQMSK
 sei
 lda#$e7// New NMI vector $12e7
 sta$fffa
 lda#$12
 sta$fffb
 lda#$08// New IRQ vector $1208
 sta$fffe
 lda#$12
 sta$ffff
 lda#$90
 staCIAICR// Enable FLAG IRQ
 cli
 lda#$59
 staCIACRB// Timer B: Load latched value, one-shot mode, count times Timer A counts down, start
 lda#$11
 staCIACRA// Timer A: Load latched value, continous mode, start
 rts

// IRQ Vector
IRQ:pha
 txa
 pha
 tya
 pha
 ldaCIAICR
 ldaEXTCOL
 eor#$01
 staEXTCOL
 ldyTIMBHI
 ldaTIMBLO
// Restart timers
 ldx#$59
 stxCIACRB
 ldx#$11
 stxCIACRA
// Invert Timer B low byte
// Timer B counts how many times Timer A has counted down from 8
 ldx#0
 eor#$ff
 clc
 adc#$01
 cmp#$0f
 bccbadPulse// < 120? 0.000122 us
 cmp#$78
 bcsbadPulse// >= 960? 0.000974 us
 decbitCount
 bmibyteDone
 cmp#$32// 400 0.000406 us
 ldareadByte
 rol// rol = MSbF
 eor#$01// Invert read bit
 stareadByte
 jmpC_12da

byteDone:lda#$08
 stabitCount
 cpy#$ff
C_124d:jmpIRQ//SMC, Jump to IRQ will never occur. Will be changed before first jump.
// Possible jumps $1256, $12da, $12ac, $128e (Read header)

badPulse:lda#$08
 stabitCount
 bneC_124d
C_1256:bneC_1285
 lda$cd
 beqC_1270
 ldareadByte
 stxreadByte
 cmp#$ff
 bneC_1285
 dec$ca
 beqC_126b
 jmpC_12da

C_126b:stx$cd
 jmpC_12da

C_1270:ldareadByte
 stxreadByte
 cmp#0
 bneC_127b
 jmpC_1308

C_127b:cmp#$ff
 beqC_1282
 jmpC_131f

C_1282:jmpC_12da

C_1285:lda#$1e
 sta$ca
 incbitCount
 jmpC_12da

C_128e:beqC_1293
 jmpC_131f

C_1293:ldareadByte
 stxreadByte
 ldy$ca
 sta$00bf,y// Store header, 9 bytes
 clc
 adcchecksum// Only used when reading header. So no data checksum
 stachecksum
 dec$ca
 bneC_12da
 ldachecksum
 bneC_131f
 jmpC_12ee

C_12ac:bneC_131f
 incEXTCOL
 incEXTCOL
 ldareadByte
 stxreadByte
 ldy#0
 sta($c0),y// Store read byte
 inc$c0
 bneC_12c2
 inc$c1
C_12c2:dec$c2
 lda$c2
 cmp#$ff
 bneC_12cc
 dec$c3
C_12cc:ora$c3
 beqC_12d3
 jmpC_12da

C_12d3:inc$cd
 lda#$da
 staC_124d+1// Modifies JMP instr
C_12da:ldaCIAICR// Checks if serial shift reg finished a byte!?
 and#$08
 bneC_131f
 pla
 tay
 pla
 tax
 pla
 rti

// NMI vector
NMI:pha
 lda#$04
 sta$cd
 pla
 rti

C_12ee:inc$cd
 lda#$ac
 staC_124d+1// Modifies JMP instr
// Create a copy of load start address and load size
 lda$c2
 sta$d2
 lda$c3
 sta$d3
 lda$c0
 sta$d0
 lda$c1
 sta$d1
 jmpC_12da

C_1308:lda#$04
 staEXTCOL
 inc$cd
 lda#$8e// Sets jump to routine for fetching header
 staC_124d+1// Modifies JMP instr
 lda#$09
 sta$ca
 lda#0
 stachecksum
 jmpC_12da

// This seems to create some sort of 16bit checksum, it's never called though-
C_131f:lda#$da
 staC_124d+1// Modifies JMP instr
 lda#$04
 sta$cd
 bneC_12da
 ldy#0
 sty$d4
 sty$d5
C_1330:clc
 lda($d0),y
 adc$d4
 sta$d4
 lda$d5
 adc#0
 sta$d5
 inc$d0
 bneC_1343
 inc$d1
C_1343:dec$d2
 lda$d2
 cmp#$ff
 bneC_134d
 dec$d3
C_134d:ora$d3
 bneC_1330
 rts

// This addres is triggered by data in the header of the first file
// there is a JMP ($00c4) at $1195. $c4/$c5 vector is part of header
// This is triggered after second file
// Displays the intro picture
C_1352:ldaC2DDRA
 ora#$03
 staC2DDRA
 ldaCI2PRA
 and#$fc
 ora#$02
 staCI2PRA
 lda#$90
 staVMCSB
 ldaSCROLX
 ora#$18
 staSCROLX
 lda#$07
 staEXTCOL
 lda#$07
 staBGCOL0
 ldy#$03
C_137d:ldx#0
C_137f:lda$6000,x// SMC
C_1382:sta$d800,x// SMC
 dex
 bneC_137f
 incC_137f+2// Modifies LDA instr
 incC_1382+2// Modifies STA instr
 dey
 bplC_137d
 lda#$38
 staSCROLY
 lda#$03
 staD_13ff
 jmpSTART

// This addres is triggered by data in the header of the first file
// there is a JMP ($00c4) at $1195. $c4/$c5 vector is part of header
// This is triggered after fifth and last file file
C_139e:ldy#$07
C_13a0:ldx#0
C_13a2:lda$1800,x// SMC
C_13a5:sta$6000,x// SMC
 dex
 bneC_13a2
 incC_13a2+2// Modifies LDA instr
 incC_13a5+2// Modifies STA instr
 dey
 bplC_13a0
 jmp$8001

// This addres is triggered by data in the header of the first file
// there is a JMP ($00c4) at $1195. $c4/$c5 vector is part of header
// This is triggered after first file
C_13b7:lda#$7f
 staCI2ICR
 lda#0
 staTI2AHI
 staTI2BHI
 lda#$18
 staTI2ALO
 lda#$0c
 staTI2BLO
 lda#$11
 staCI2CRA
 lda#$51
 staCI2CRB
 lda#$82
 staCI2ICR
 jmpSTART

C_13e0:ldaRASTER
 cmp#200// Check if we're before raster line 200
 bccC_13f4
 ldaD_1400
 bneC_13e0
 lda#$01
 staD_1400
 jmpC_13fc

C_13f4:lda#0
 staD_1400
 jmpC_13e0

C_13fc:jmp$e003

D_13ff:.byte0
D_1400:.byte0,0,0