Petscii Forums "PETSCII.COM"
« C64DTV ROM-filesystem Information »

Welcome Guest. Please Login or Register.
Jul 30, 2010, 10:57pm




Petscii Forums "PETSCII.COM" :: General :: DTVHacking :: C64DTV ROM-filesystem Information
   [Search This Thread][Send Topic To Friend] [Print]
 AuthorTopic: C64DTV ROM-filesystem Information (Read 506 times)
Jim Brain
Wizard's Apprentice
*****
member is offline





Joined: Mar 2004
Gender: Male
Posts: 532
 C64DTV ROM-filesystem Information
« Thread Started on Dec 21, 2004, 10:06pm »

C64DTV Kernal LOAD modifications to support ROM-based filesystem
Version 1.0 2004-12-21
Jim Brain
brain@jbrain.com

Introduction:

In it's current implementation, the Commodore 64 Direct To TV
(C64DTV) unit requires the ability to load one of 30 game
applications using solid state memory. Given time and cost
constraints, a ROM based filesystem was created for the unit. In
order to preserve compatibility with the use of the C64DTV as a
complete Commodore 64 platform and to minimize the amount of
KERNAL rework needed to support the ROM FS, the DTV development
team decided to replace the C64 cassette tape load routines with
routines implementing the new filesystem. Considering the target
usage, only LOAD support is implemented. Though the ROM
filesystem replaces device #1, the device implements random access
device semantics, like the Commodore 15XX disk drive series.
Obviously, all multi-load games were modified to load from device
#1 to utilize the ROM-fs. To conserve space in the 2MB ROM, the
directory is stored verbatim, but the files are RLL encoded.
Efficient data transfer is accomplished by using the C64DTV
extended functions, notably the DMA Engine.

In short, the routine acquires some work space, scans the
directory for a match, runs the RLL expansion algorithm to load
the file, and then returns control to the caller.

Obviously, there's a few places where the code gets a bit jumpy or
a bit redundant, but I'll record here (since this may enjoy a wide
distribution) that the developers were under severe time pressure
to complete this. It works, and it's good enough. If folks want
to pass constructive recommendations to me, I'll pass them onto
the developers in case they want to make changes for the PAL
unit. I'm not a schooled disassembler, but I think I caught the
essence of the code.

As I don't know Adrian Gonzales' (dW/Style) and Robin Harbron's
(Macbeth/PSW) coding styles, I can't comment on the exact
authorship. Given circumstance, I suspect it was a joint effort.
Adrian did not have an actual unit to test with, so the routines
were written to utilize an REU. Robin had an actual prototype
unit, so he no doubt modified Adrian's code to work with the
different register layout of the DTV DMA Engine.

Comments and bugfixes are appreciated. The KERNAL ROM was dumped
from a 041104 build date unit and disassembled via Marko Makela's
d65 disassembler.

« Last Edit: Dec 21, 2004, 10:06pm by Jim Brain »Link to Post - Back to Top  IP: Logged

--
Jim Brain
www.jbrain.com/vicug/gallery/
tcpser/tcpser4j author, CBM hobbyist at large
Jim Brain
Wizard's Apprentice
*****
member is offline





Joined: Mar 2004
Gender: Male
Posts: 532
 Re: C64DTV ROM-filesystem Information
« Reply #1 on Dec 21, 2004, 10:08pm »

Detailed commentary:

The ROM fs directory is stored as 32 byte stuctures starting at
002000 in ROM. Each entry is organized as follows:

$0-$17 filename, or 0 for end of dir
$18 low byte of ROM start
$19 mid byte of ROM start
$1a hi byte of ROM start
$1b low byte of load address
$1c hi byte of load address
$1d unknown
$1e unknown
$1f unknown

The entries are loaded to $100, and checked against the current
load name. If a match is found, the corresponding vectors are
loading into temp registers and the load starts

The data is stored in ROM in a RLL scheme. It is of the form:

length : data

If length:7 is 0, the code copies length bytes from source to
destination, updating the temporary vectors. If length:7 = 1,
then length is normalized to 0-127, and the next byte in source is
copied length times to the destination. If length =0, the load is
complete.

The normal load indirect vector at $fd4c is rerouted from $f4a5 to
$f730. Code there, in turn, jumps to $f736:

F730 4C 36 F7 JMP LF736 ; patched jump

The code then saves off the load/verify flag, loads the device,
checks against tape, and goes to the old routine if not device 1

F736 85 93 STA L93 ; store verify flag
F738 A5 BA LDA LBA ; load current device
F73A C9 01 CMP #$1 ; is it tape?
F73C F0 03 BEQ LF741 ; yes
F73E 4C A7 F4 JMP LF4A7 ; no, go to old routine.

If it is tape, flags are set for load, the DMA engine is enabled
via the MAGIC VIC bit and modulo hi bytes are set to 0.

F741 A9 00 LDA #$0 ; set for load
F743 85 93 STA L93 ; store
F745 A2 01 LDX #$1 ; enable DMA engine
F747 8E 3F D0 STX LD03F
F74A 8D 07 D3 STA LD307 ; source stepping HI to 0
F74D 8D 09 D3 STA LD309 ; destination stepping HI to 0
F750 20 2D F8 JSR LF82D ; save off some temp scratch space

The goal here is to free up some RAM for the LOAD routine without
affecting anything. As one can't know what RAM is in use, this
routine swaps out 55 bytes of the current RAM footprint. To
minimize interference, it uses the top end of zp ($f9-) and the
low 48 bytes of stack.

F82D A9 00 LDA #$0
F82F A0 01 LDY #$1
F831 8C 06 D3 STY LD306 ; source stepping to 1
F834 8C 08 D3 STY LD308 ; dest stepping to 1
F837 A0 40 LDY #$40
F839 8C 05 D3 STY LD305 ; destination is RAM at 00XXYY
F83C C8 INY
F83D 8C 02 D3 STY LD302 ; read from RAM at 01XXYY
F840 8D 0B D3 STA LD30B ; read 00XX bytes
F843 8D 04 D3 STA LD304 ; dest start is 0000XX
F846 A9 80 LDA #$80
F848 8D 01 D3 STA LD301 ; source is 0180XX
F84B A9 F9 LDA #$F9
F84D 8D 00 D3 STA LD300 ; source is 0180F9
F850 8D 03 D3 STA LD303 ; dest is 0000f9
F853 A9 37 LDA #$37
F855 8D 0A D3 STA LD30A ; read 0037 bytes
F858 A9 0F LDA #$F ; possibly could have been $d -- jlb
F85A 8D 1F D3 STA LD31F ; swap 55 bytes from 00f9 to 0180f9
F85D 4C 69 F8 JMP LF869

F869 AD 1F D3 LDA LD31F ; load flags
F86C 4A LSR A
F86D B0 FA BCS LF869 ; is DMA done?
F86F 60 RTS ; yes.

Control returns to F753, which initializes some vectors in the
temp ram, prints searching, and gets an entry to check. It
continues checking until the end is reached or entry is found.

I think the PHP and the SEI should have been called earlier, as
RAM is now corrupt, but I suspect this just mirrors the old IEC or
tape routines.

F753 08 PHP ; push status bits
F754 78 SEI ; no irqs.
F755 A9 00 LDA #$0
F757 85 FA STA LFA ; $(fc,fb,fa) is source
F759 A2 20 LDX #$20 ; set source to ROM at 002000
F75B 86 FB STX LFB
F75D 85 FC STA LFC
F75F A9 00 LDA #$0
F761 85 FD STA LFD ; $(0:fe:fd) is destination
F763 A9 01 LDA #$1 ; set destination to RAM at 0001XX
F765 85 FE STA LFE
F767 20 AF F5 JSR LF5AF ; print "SEARCHING FOR [NAME]"
F76A 20 9E F8 JSR LF89E ; load source vector to registers:

F89E A5 FA LDA LFA ; put temp Source into source vector.
F8A0 8D 00 D3 STA LD300
F8A3 A5 FB LDA LFB
F8A5 8D 01 D3 STA LD301
F8A8 A5 FC LDA LFC
F8AA 8D 02 D3 STA LD302
F8AD 60 RTS

F76D 20 AE F8 JSR LF8AE ; load dest vector to registers

F8AE A5 FD LDA LFD ; put temp dest vector into dest
registers
F8B0 8D 03 D3 STA LD303
F8B3 A5 FE LDA LFE
F8B5 8D 04 D3 STA LD304
F8B8 A9 00 LDA #$0
F8BA 8D 05 D3 STA LD305
F8BD 60 RTS

F770 A9 20 LDA #$20 ;
F772 20 61 F8 JSR LF861 ; set length of DMA to 32 bytes

F861 20 79 F8 JSR LF879 ; set length to 00.A

F879 8D 0A D3 STA LD30A
F87C A9 00 LDA #$0
F87E 8D 0B D3 STA LD30B
F881 60 RTS

F864 A9 0D LDA #$D ; transfer bytes.
F866 8D 1F D3 STA LD31F
F869 AD 1F D3 LDA LD31F
F86C 4A LSR A
F86D B0 FA BCS LF869 ; is DMA done?
F86F 60 RTS

At this point, we have the first directory entry at 0100 to 011f.
If the first byte is 0, we've reached the end of the dir.
Otherwise, check the name.

F775 AD 00 01 LDA L100 ; Did we check all the entries?
F778 D0 07 BNE LF781 ; no
F77A 20 2D F8 JSR LF82D ; swap the data back
F77D 28 PLP
F77E 4C 04 F7 JMP LF704 ; "FILE NOT FOUND"

F781 A5 B7 LDA LB7 ; load name length
F783 D0 07 BNE LF78C ; name is not null
F785 20 2D F8 JSR LF82D ; swap temp ram back
F788 28 PLP
F789 4C 10 F7 JMP LF710 ; "MISSING FILE NAME"
F78C C9 18 CMP #$18 ; check length against 24 bytes (24?
jlb)
F78E 90 02 BCC LF792 ; not more, check name.
F790 B0 E8 BCS LF77A ; swap data back and FILE NOT FOUND
ERROR
F792 A0 00 LDY #$0
F794 B1 BB LDA (LBB),Y ; load letter
F796 C9 2A CMP #$2A ; Wilcard (*) match?
F798 F0 0F BEQ LF7A9 ; match
F79A D9 00 01 CMP L100,Y ; does letter match?
F79D D0 35 BNE LF7D4 ; no, add 32 to source, and try again
F79F C8 INY
F7A0 C4 B7 CPY LB7 ; did we scan entire name?
F7A2 D0 F0 BNE LF794 ; no, continue scanning
F7A4 B9 00 01 LDA L100,Y ; load next byte
F7A7 D0 2B BNE LF7D4 ; if not name end, add $20 to source,
loop
F7A9 20 D2 F5 JSR LF5D2 ; print "LOADING..."
F7AC A2 04 LDX #$4
F7AE BD 18 01 LDA L118,X ; load $fa,fb,fc,fd,$fe from $118-.
F7B1 95 FA STA LFA,X
F7B3 CA DEX
F7B4 10 F8 BPL LF7AE
F7B6 A5 B9 LDA LB9 ; load secondary address
F7B8 D0 08 BNE LF7C2 ; we are an absolute load
F7BA A5 C3 LDA LC3 ; put 0801 in $fe:$fd
F7BC 85 FD STA LFD
F7BE A5 C4 LDA LC4
F7C0 85 FE STA LFE
F7C2 20 DC F7 JSR LF7DC

At this point, we are loading the file. the sequence is to get a
byte from ROM into $ff. If 0, we are done, if 1-127, copy that
many bytes from source to dest. If >128, copy the next byte in
source ($ff)-128 times.

F7DC A9 FF LDA #$FF ;
F7DE 20 82 F8 JSR LF882 ; put 0000FF into dest, step 1
F7E1 20 9E F8 JSR LF89E ; set dest to $(fc,fb,fa)
F7E4 A9 01 LDA #$1
F7E6 20 70 F8 JSR LF870 ; transfer 1 byte

F870 48 PHA ; save off .A
F871 20 61 F8 JSR LF861 ; another jump

F861 20 79 F8 JSR LF879 ; a JSR :-) (transfer 256 bytes)

F879 8D 0A D3 STA LD30A ; set bytes to transfer as 1
F87C A9 00 LDA #$0
F87E 8D 0B D3 STA LD30B
F881 60 RTS

F864 A9 0D LDA #$D ; transfer data
F866 8D 1F D3 STA LD31F
F869 AD 1F D3 LDA LD31F ; load status
F86C 4A LSR A
F86D B0 FA BCS LF869 ; is DMA done?
F86F 60 RTS

F874 68 PLA ; restore .A
F875 20 BE F8 JSR LF8BE ; add .A to source address ?

F878 60 RTS

F7E9 A5 FF LDA LFF ; load first byte of data
F7EB F0 37 BEQ LF824 ; if 0, then ($fd)->$ae, ($fe)->$af,
rts

F7ED 30 12 BMI LF801 ; if > 127, F801
F7EF AA TAX ; save .A
F7F0 20 AE F8 JSR LF8AE ; $(0,$fe,$fd) -> dest address
F7F3 20 9E F8 JSR LF89E ; $(fc,fb,fa) -> source
F7F6 8A TXA ; restore .A
F7F7 20 70 F8 JSR LF870 ; transfer .A bytes, add .A to source.
F7FA 8A TXA
F7FB 20 CC F8 JSR LF8CC ; add .A to dest.
F7FE 4C DC F7 JMP LF7DC ; loop

if ($ff) > 127,

F801 49 80 EOR #$80 ; mask high bit
F803 AA TAX
F804 20 AE F8 JSR LF8AE ; set dest as above
F807 20 9E F8 JSR LF89E ; set source as above
F80A A9 00 LDA #$0
F80C 8D 06 D3 STA LD306 ; no stepping for source
F80F A9 01 LDA #$1
F811 8D 08 D3 STA LD308 ; step by 1 for dest.
F814 8A TXA ; restore .A to number of loops
F815 20 61 F8 JSR LF861 ; transfer 1 byte to (.A) locations.
F818 A9 01 LDA #$1
F81A 20 BE F8 JSR LF8BE ; add 1 to source
F81D 8A TXA
F81E 20 CC F8 JSR LF8CC ; add .A to dest.
F821 4C DC F7 JMP LF7DC ; loop.

F7C5 20 2D F8 JSR LF82D ; swap RAM back in
F7C8 A9 00 LDA #$0
F7CA 8D 3F D0 STA LD03F ; turn off DMA Engine
F7CD 28 PLP ; restore status
F7CE A9 00 LDA #$0 ; clear $90
F7D0 85 90 STA L90
F7D2 18 CLC ; clear carry
F7D3 60 RTS ; return from LOAD.
Link to Post - Back to Top  IP: Logged

--
Jim Brain
www.jbrain.com/vicug/gallery/
tcpser/tcpser4j author, CBM hobbyist at large
Jim Brain
Wizard's Apprentice
*****
member is offline





Joined: Mar 2004
Gender: Male
Posts: 532
 Re: C64DTV ROM-filesystem Information
« Reply #2 on Dec 21, 2004, 10:09pm »

Notes:

1) $f858 sets DMA for swap, though I'm not sure we care about the
data scoming in from high memory
2) The RLL decoder can handle chunks of data up to 127 bytes, but
only 48 bytes of RAM is available (55 saved bytes - 7 vector/flag
bytes). I assume Adrian's perl scripts used to encode the RLL
data took this into account, but if someone should replace the
ROM, be aware that decoding > 48 bytes will trash portions of the
stack page.
3) My earlier caveat aside, there's plenty of room for
optimization. The source and destination vectors are handled very
conservativel, for instance. As well, no more than 48 bytes are
ever transferred at any time, so the initial clearing of the
stepping hi bytes should be all that is needed throughout the code.
4) $f78e looks like it can be safely removed.
5) Someone's coder skills seem to be coming out in $f801 :-) . AND
#$7f would accomplish the same thing, I believe.
6) As noted, the SEI at $f754 looks like it should come earlier,
to prevent issues if the load happens while SP is < 48 and some
IRQ happens.
7) The code at $f869 is curious. I can't find any mention of the
DMA engine relinquishing control back to the CPU before the DMA is
completed or an error occurs.
8) I'm assuming many of these ideas came from RAMDOS or variations
of it. Howver, if not, I think a RAMDOS that uses these
techniques would be a very non-invasive utility.

Jim Brain
Link to Post - Back to Top  IP: Logged

--
Jim Brain
www.jbrain.com/vicug/gallery/
tcpser/tcpser4j author, CBM hobbyist at large
   [Search This Thread][Send Topic To Friend] [Print]

Google
Webjledger.proboards.com
Click Here To Make This Board Ad-Free


This Board Hosted For FREE By ProBoards
Get Your Own Free Message Boards & Free Forums!
Terms of Service | Privacy Policy | Report Abuse | Mobile