Copy Link
Add to Bookmark
Report

Analyzis of the 'Bored of the Rings' loader

by bokvamme

DrWatson's profile picture
Published in 
Commodore64
 · 16 May 2021
Bored of the Rings for the Commodore 64
Pin it
Bored of the Rings for the Commodore 64

(Thanks to tce and slc for help and guidance.)

Loading it into FinalTap v2.7b2 produced:

ROM HEADER 187 bytes
ROM DATA 2 bytes
Some unknown data
ROM HEADER 187 bytes
ROM DATA 2 bytes
More unknown data

This game is in two parts and each part seems to load independently.
FinalTap tells me the following CBM pulses are in use:

 $22 
$30
$42
$56


ROM DATA (2 bytes)

 52 03  ; Start address (see ROM HEADER) 

ROM HEADER (187 bytes)

This code loads 512 bytes from the first turbo block into memory at $CA30

Local variables:

 $02 How many bits to read 
$04 How many pages (256 bytes) to read
$05 Threshold Timer A
$06 Threshold Timer B


(disassembly of the ROM HEADER)

 033C  03 02 03 04 03 42 4F 54  .....BOT 
0344 52 20 50 41 52 54 20 31 R PART 1
034C 20 20 20 20 20 20

0352 78 SEI ; Interrupt disable
0353 A9 00 LDA #$00
0355 8D 11 D0 STA $D011 ; Blank screen
0358 A9 85 LDA #$85
035A 8D 18 D0 STA $D018 ; Set screen memory to $2000, Char memory $1000
035D A9 00 LDA #$00
035F 8D 20 D0 STA $D020 ; Border colour = 0
0362 A9 00 LDA #$00
0364 8D 27 D0 STA $D027 ; Sprite 0 colour = 0
0367 A2 02 LDX #$02
0369 86 04 STX $04 ; local var (How many bytes to read) = 2 pages
036B A9 06 LDA #$06
036D 85 05 STA $05 ; local var = $06 (00000110)
036F 85 01 STA $01 ; Bank in BASIC ROM ($A000-$BFFF) and IO ($D000-$DFFF)
0371 A9 0E LDA #$0E
0373 85 06 STA $06 ; local var = $0E (00001110)
0375 A9 00 LDA #$00
0377 8D 06 DD STA $DD06
037A 8D 04 DD STA $DD04

037D A5 05 LDA $05
037F 8D 05 DD STA $DD05 ; Timer A Threshold (first time = $0600 us) = $BD
0382 A5 06 LDA $06
0384 8D 07 DD STA $DD07 ; Timer B Threshold (First time = $0E00 us)

0387 A0 00 LDY #$00

-- --------------------------
0389 A2 05 LDX #$05

-- --------------------------
038B A9 19 LDA #$19
038D 8D 0E DD STA $DD0E ; Start timer A, one shot, force latch value being loaded
0390 8D 0F DD STA $DD0F ; ditto for timer B

0393 AD 0D DC LDA $DC0D ; Checks the interrupt register
0396 29 10 AND #$10 ; to see if the pulse (negative
0398 F0 F9 BEQ $0393 ; edge on a C64) came in or not

039A AD 0D DD LDA $DD0D ; Store CIA interrupt control for later test
039D 8C 0F DD STY $DD0F ; Stop timer B
03A0 8C 0E DD STY $DD0E ; Stop timer A
03A3 29 03 AND #$03 ; Mask out bit 0 (timer A), and bit 1 (timer B)
03A5 C9 01 CMP #$01 ; Timer A interrupt happened?
03A7 D0 E0 BNE $0389 ; no (less than threshold)

03A9 CA DEX ; Done it 5 times?
03AA D0 DF BNE $038B ; no -> repeat

03AC 46 05 LSR $05 ; Timer A Threshold / 2
03AE 46 06 LSR $06 ; Timer B Threshold / 2
03B0 90 CB BCC $037D ; Read a total of 10 long pulses (see below)

03B2 A9 02 LDA #$02
03B4 8D 05 DD STA $DD05 ; Timer A = $0200 Threshold ($3F TAP byte value)
; This is between the short and long pulses.
03B7 A2 00 LDX #$00
03B9 A0 19 LDY #$19
03BB 8C 0E DD STY $DD0E ; Start timer A, one shot, force latch value being loaded

-- Byte loop -------------------------
03BE A9 08 LDA #$08
03C0 85 02 STA $02 ; local var (How many bits to read) = $08

-- Read bit subroutine -------------------------
03C2 AD 0D DC LDA $DC0D ; Checks the interrupt register
03C5 29 10 AND #$10 ; to see if the pulse (negative
03C7 F0 F9 BEQ $03C2 ; edge on a C64) came in or not

03C9 8C 0E DD STY $DD0E ; Start timer A, one shot, force latch value being loaded
03CC AD 27 D0 LDA $D027 ;
03CF 49 02 EOR #$02
03D1 8D 27 D0 STA $D027 ; Flash sprite 0 colour
03D4 AD 18 D4 LDA $D418
03D7 49 00 EOR #$00 ; (?) no point in doing this
03D9 8D 18 D4 STA $D418 ; (Sound filter + volume)
03DC AD 0D DD LDA $DD0D ; Store CIA interrupt control for later test
03DF 4A LSR ; Rotate to carry flag
03E0 7E 30 CA ROR $CA30,X ; Move carry flag into bit 7 of memory address
03E3 C6 02 DEC $02 ; Read 8 bits?
03E5 D0 DB BNE $03C2 ; nope -> read some more bits
03E7 E8 INX ; Read 256 bytes?
03E8 D0 D4 BNE $03BE ; nope -> read some more bytes

03EA EE E2 03 INC $03E2 ; $CA -> $CB (Add $100 to the destination memory address)
03ED C6 04 DEC $04 ; Read all (2 * 256) bytes?
03EF D0 CD BNE $03BE ; nope -> read more bytes

03F1 8E 27 D0 STX $D027 ; Reset sprite 0 colour
03F4 4C 30 CA JMP $CA30 ; Jump to code just read

First 8 bytes from first turbo block decoded by hand.
This was done to check the validity of the data and the endianess.

 01111000 10101001 11000000 10001101 00000000 11011101 10101001 00101011 
$78 $A9 $C0 $8D $00 $DD $A9 $2B

 $22 - 0 
$56 - 1
LsbF


512 bytes in first turbo block (CA30 - CC2F)


This code loads 10K into memory at $4000, then moves 8KB of it to $E000, 1KB to $CC00 and 128bytes to $FF3F and 1KB to $D800. Some of this memory overlap.
It then reads 49920 bytes into memory at $0800 before starting the game.
It uses the same loader as the one in the CBM HEADER that was used to read this code.

 CA30  78		SEI		; Interrupt disable 
CA31 A9 C0 LDA #$C0
CA33 8D 00 DD STA $DD00 ; Serial bus data input, clock pulse
CA36 A9 2B LDA #$2B
CA38 8D 11 D0 STA $D011 ; Bitmap mode on, 25 rows, Y-scroll = 3
CA3B A9 3D LDA #$3D
CA3D 8D 18 D0 STA $D018 ; Char mem = $3000-37FFF, screen mem = $0C00
CA40 A9 d8 LDA #$D8
CA42 8D 16 D0 STA $D016 ; Multicolour, 40 cols
CA45 A9 1F LDA #$1F
CA47 8D 00 D0 STA $D000 ; Sprite 0 x-pos = 31 (= 31 + 255 = 286)
CA4A A9 37 LDA #$37
CA4C 8D 02 D0 STA $D002 ; Sprite 1 x-pos = 55 (= 55 + 255 = 310)
CA4F A9 03 LDA #$03
CA51 8D 10 D0 STA $D010 ; Sprite 0 + 1 msb x-pos
CA54 A9 DE LDA #$DE
CA56 8D 01 D0 STA $D001 ; Sprite 0 y-pos = 222
CA59 8D 03 D0 STA $D003 ; Sprite 1 y-pos = 222
CA5C A9 FF LDA #$FF
CA5E A0 04 LDY #$04
CA60 A2 00 LDX #$00
CA62 A9 06 LDA #$06
CA64 8D 20 D0 STA $D020 ; Border colour =
CA67 8E 21 D0 STX $D021 ; Background colour 0 =
CA6A A9 02 LDA #$02
CA6C 8D 27 D0 STA $D027 ; Sprite 0 colour =
CA6F 8D 28 D0 STA $D028 ; Sprite 1 colour =
CA72 A9 FF LDA #$FF
CA74 8D 15 D0 STA $D015 ; Enable all sprites

-- Clear 1024 bytes from $CC00 --------
CA77 A9 00 LDA #$00
CA79 9D 00 CC STA $CC00,X
CA7C E8 INX
CA7D D0 FA BNE $CA79
CA7F EE 7B CA INC $CA7B
CA82 88 DEY
CA83 D0 F4 BNE $CA79

-- Clear 1024 bytes from $D800 --------
CA85 A0 04 LDY #$04
CA87 A2 00 LDX #$00
CA89 A9 06 LDA #$06
CA8B 9D 00 D8 STA $D800,X
CA8E E8 INX
CA8F D0 FA BNE $CA8B
CA91 EE 8D CA INC $CA8D
CA94 88 DEY
CA95 D0 F4 BNE $CA8B

-- Set 8192 bytes to $FF from $E000 ---
CA97 A0 20 LDY #$20
CA99 A9 FF LDA #$FF
CA9B 9D 00 E0 STA $E000,X
CA9E E8 INX
CA9F D0 FA BNE $CA9B
CAA1 EE 9D CA INC $CA9D
CAA4 88 DEY
CAA5 D0 F4 BNE $CA9B

-- Prepare for loading ---------------
CAA7 A9 FD LDA #$FD
CAA9 8D F8 CF STA $CFF8
CAAC 8D F9 CF STA $CFF9
CAAF A9 3B LDA #$3B
CAB1 8D 11 D0 STA $D011 ; Bitmap mode on, 25 rows, Y-scroll = 3, no blank screen
CAB4 A9 00 LDA #$00
CAB6 8D CD CB STA $CBCD
CAB9 A9 40 LDA #$40
CABB 8D CE CB STA $CBCE ; Destination address for data from tape = $4000
CABE A9 28 LDA #$28
CAC0 85 04 STA $04 ; 40 pages (40 * 256 = 10KB) to read
CAC2 20 59 CB JSR $CB59 ; Read data from tape (next turbo block)

-- Copy 8192 bytes from $4000 to $E000
CAC5 A0 20 LDY #$20
CAC7 A2 00 LDX #$00
CAC9 BD 00 40 LDA $4000,X
CACC 9D 00 E0 STA $E000,X
CACF E8 INX
CAD0 D0 F7 BNE $CAC9
CAD2 EE CB CA INC $CACB
CAD5 EE CE CA INC $CACE
CAD8 88 DEY
CAD9 D0 EE BNE $CAC9

-- Copy 1024 bytes from $5F40 to $CC00
CADB A0 04 LDY #$04
CADD BD 40 5F LDA $5F40,X
CAE0 9D 00 CC STA $CC00,X
CAE3 E8 INX
CAE4 D0 F7 BNE $CADD
CAE6 EE DF CA INC $CADF
CAE9 EE E2 CA INC $CAE2
CAEC 88 DEY
CAED D0 EE BNE $CADD

-- Copy 128 bytes from $670F to $FF3F
CAEF A2 80 LDX #$80
CAF1 BD 0F 67 LDA $670F,X
CAF4 9D 3F FF STA $FF3F,X
CAF7 CA DEX
CAF8 D0 F7 BNE $CAF1

-- Copy 1024 bytes from $6328 to $D800
CAFA A0 04 LDY #$04
CAFC BD 28 63 LDA $6328,X
CAFF 9D 00 D8 STA $D800,X
CB02 E8 INX
CB03 D0 F7 BNE $CAFC
CB05 EE FE CA INC $CAFE
CB08 EE 01 CB INC $CB01
CB0B 88 DEY
CB0C D0 EE BNE $CAFC

CB0E A9 06 LDA #$06
CB10 8D 20 D0 STA $D020 ; Border colour =

-- Prepare for loading ---------------
CB13 A9 FD LDA #$FD
CB15 8D F8 CF STA $CFF8
CB18 A9 FE LDA #$FE
CB1A 8D F9 CF STA $CFF9
CB1D A9 00 LDA #$00
CB1F 8D CD CB STA $CBCD
CB22 A9 08 LDA #$08
CB24 8D CE CB STA $CBCE ; Destination address for data from tape = $0800
CB27 A9 C3 LDA #$C3
CB29 85 04 STA $04 ; 195 pages (195 * 256 = 49920 bytes) to read
CB2B 20 59 CB JSR $CB59 ; Read data from tape (next turbo block)

-- Prepare to start game -------------
CB2E A9 36 LDA #$36 ; Cassette motor off, cassette switch closed
CB30 85 01 STA $01 ; KERNAL ROM on, Chargen ROM off
CB32 A9 C8 LDA #$C8
CB34 8D 16 D0 STA $D016 ; 40 cols
CB37 A9 15 LDA #$15
CB39 8D 18 D0 STA $D018 ; Char memory $3800-$3FFF
CB3C A9 93 LDA #$93 ; Serial bus data input, Serial bus clock pulse (?)
CB3E 8D 00 DD STA $DD00 ; VIC chip system memory = $0000-$3FFF
CB41 A9 1B LDA #$1B
CB43 8D 11 D0 STA $D011 ; no blank screen, 25 rows, Y-scroll = 3
CB46 A9 83 LDA #$83
CB48 8D 02 03 STA $0302
CB4B A9 A4 LDA #$A4 ; Set basic warm start
CB4D 8D 03 03 STA $0303 ; address to $A483
CB50 58 CLI
CB51 A9 00 LDA #$00
CB53 8D 15 D0 STA $D015 : Disable all sprites
CB56 6C B6 AE JMP ($AEB6) ; Start game

-- Load routine ----------------------- ; VERY similar to the loader in the CBM Header
CB59 78 SEI
CB5A A9 06 LDA #$06
CB5C 85 05 STA $05
CB5E A9 55 LDA #$55 ; Cassette motor off, cassette switch closed (?)
CB60 85 01 STA $01 ; Basic in, char out
CB62 A9 0E LDA #$0E
CB64 85 06 STA $06
CB66 A9 00 LDA #$00
CB68 8D 06 DD STA $DD06
CB6B 8D 04 DD STA $DD04

CB6E A5 05 LDA $05
CB70 8D 05 DD STA $DD05
CB73 A5 06 LDA $06
CB75 8D 07 DD STA $DD07
CB78 A0 00 LDY #$00

CB7A A2 05 LDX #$05
CB7C A9 19 LDA #$19

CB7E 8D 0E DD STA $DD0E
CB81 8D 0F DD STA $DD0F
cB84 AD 0D DC LDA $DC0D
CB87 29 10 AND #$10
CB89 F0 F9 BEQ $CB84
CB8B AD 0D DD LDA $DD0D
CB8E 8C 0F DD STY $DD0F
CB91 8C 0E DD STY $DD0E
CB94 29 03 AND #$03
CB96 C9 01 CMP #$01
CB98 D0 E0 BNE $CB7A
CB9A CA DEX
CB9B D0 DF BNE $CB7E
CB9D 46 05 LSR $05
CB9F 46 06 LSR $06
CBA1 90 CB BCC $CB6E

CBA3 A9 02 LDA #$02
CBA5 8D 05 DD STA $DD05
CBA8 A2 00 LDX #$00
CBAA A0 19 LDY #$19
CBAC 8C 0E DD STY $DD0E

CBAF A9 08 LDA #$08
CBB1 85 02 STA $02
CBB3 AD 0D DC LDA $DC0D
CBB6 29 10 AND #$10
CBB8 F0 F9 BEQ $CBB3
CBBA 8C 0E DD STY $DD0E
CBBD AD 27 D0 LDA $D027
CBC0 49 04 EOR #$04
CBC2 8D 27 D0 STA $D027 ; Flash sprite 0 colour
CBC5 8D 28 D0 STA $D028 ; Flash sprite 1 colour
CBC8 AD 0D DD LDA $DD0D
CBCB 4A LSR
CBCC 7E 11 11 ROR $1111,X ; Destination address will be overwritten by caller
CBCF C6 02 DEC $02
CBD1 D0 E0 BNE $CBB3
CBD3 E8 INX
CBD4 D0 D9 BNE $CBAF
CBD6 EE CE CB INC $CBCE
CBD9 C6 04 DEC $04
CBDB D0 D2 BNE $CBAF
CBDD A9 02 LDA #$02
CBDF 8D 27 D0 STA $D027 ; Reset sprite 0 colour
CBE2 8D 28 D0 STA $D028 ; Reset sprite 1 colour
CBE5 60 RTS

CBE6 FF FF
CBE8 FF FF FF FF
CBEC FF FF FF FF
CBF0 00 00 00 00
CBF4 00 00 00 00
CBF8 00 00 00 00
CBFC 00 00 00 00
CC00 FF FF FF FF
CC04 FF FF FF FF
CC08 FF FF FF FF
CC0C FF FF FF FF
CC10 00 00 00 00
CC14 00 00 00 00
CC18 00 00 00 00
CC1C 00 00 00 00
CC20 FF FF FF FF
CC24 FF FF FF FF
CC28 FF FF FF FF
CC2C FF FF FF 7F


So we've got the following files on the tape:

ROM HEADER 187 bytes
ROM DATA 2 bytes
BOTR LOADER 512 bytes
BOTR LOADER 10240 bytes
BOTR LOADER 49920 bytes
ROM HEADER 187 bytes
ROM DATA 2 bytes
BOTR LOADER 512 bytes
And probably 2 more BOTR loader files. I've not checked this second part of the game.
It looks like the filesizes might be different.
BTW: There is a part 3 on the other side of the tape.

This loader miss headers and checksums and the start address and length of the turbo blocks are hardcoded in the loader.
For syncronization, the loader wants 5+5 concecutive pulses longer than $BD

From Tce:
The first 5 syncronization pulses don't fit in a single TAP byte so that they are encoded as "long pulses" (eg. 00 A8 08 00).

To FT users: FT assumes those long pulses are just pauses and will merge them if you attempt cleaning CRL tapes. DO NOT attempt cleaning ANY unrecognized loader (as the author suggests), otherwise syncronization won't be possible anymore for the loading routine of this turbo loader.
Future versions of FT may support this loader and check (and even repair) those 5 pulses that don't fit in a single TAP byte.

BOTR loader by ?


Threshold: $0200 clock cycles (TAP value: $3F)
Bit 0 pulse: $22
Bit 1 pulse: $56
Endianess: LSbF

Pilot byte: ?? (x???)
Sync sequence: see above

Hardcoded inside CBM Header block:
Load address ($CA30)
Number of pages to load (2)

No checksum and no trailer

← previous
next →
loading
sending ...
New to Neperos ? Sign Up for free
download Neperos App from Google Play
install Neperos as PWA

Let's discover also

Recent Articles

Recent Comments

Neperos cookies
This website uses cookies to store your preferences and improve the service. Cookies authorization will allow me and / or my partners to process personal data such as browsing behaviour.

By pressing OK you agree to the Terms of Service and acknowledge the Privacy Policy

By pressing REJECT you will be able to continue to use Neperos (like read articles or write comments) but some important cookies will not be set. This may affect certain features and functions of the platform.
OK
REJECT