Jump to content
IGNORED

cc65 loader


Shawn Jefferson

Recommended Posts

Hi,

 

While playing with getting Lode Runner to work as a cartridge image, I was using cc65 and was wondering about something to do with the new loader (since Wookie's great work cracking the encryption key, the loader is much smaller-49 bytes, and directly loads into RAM the first directory entry.) I used cc65 to generate the loader and initial cartridge image, manually edited the directory entry, and spliced the two together to form a LYX file.

 

What should the directory entry look like? For Lode Runner, the destination address is $0200 and the size is $BA00. However, I couldn't figure out how to create a directory entry that would work. The loader code checks whether the length of the file is negative or not, and if it isn't XORs it with $FF. $BA however, is negative. If you xor the length with $FFFF beforehand it's not negative ($45FF), so the bootloader XORs it! The end result is that the cc65 loader (with my directory entry) does not load the whole file. I'm wondering if this is a bug in the loader or just me not understanding what the directory entry should look like! In the end, I had to patch the loader to get things to work properly.

 

directory entry I used:

00 E7 00 88 00 02 00 BA

 

code snippet from the cc65 bootloader:

; 4. Read in the main exe to RAM
lda _FileDestAddr
ldx _FileDestAddr+1
sta	 _FileDestPtr
stx	 _FileDestPtr+1
lda	 _FileFileLen
ldx	 _FileFileLen+1
phx   ; The BLL kit uses negative counts
plx   ; while the basic Lynx uses positive
bmi @3  ; make all counts negative  (here's what I had to patch for LR... set it to bra $4 instead)
eor #$FF
pha
txa
eor #$FF
bra @4
@3: pha
txa
@4: tay
plx
jsr	 seclynxread0

Edited by Shawn Jefferson
Link to comment
Share on other sites

Good find. I have to change this in the cc65 loader code. Perhaps the best solution would be to always used non-xored values for values in the directory. They are human readable. And it is easier to deal with non-xored stuff as the upper commands like open, read etc. will use actual sizes anyway.

 

The way it is now makes it impossible to load in larger code snippets than $7fff which seems to be a bad design decision.

 

I will fix this asap. Then you can load in the entire RAM if you want to.

 

--

Karri

Edited by karri
Link to comment
Share on other sites

Done. The daily snapshot will now contain a bootloader that is 20 bytes smaller than the previous one and the size limitations are gone.

 

I also did the same changes to open() and read() commands. All take now positive arguments from the directory structure.

 

So if you create your own directories with cc65 do not XOR the length and the offset values with $FF anymore like you do in the BLL-style directories.

 

--

Karri

Link to comment
Share on other sites

Here is a small description about the new fileformat/bootloader of a Lynx cart. This is in tomorrows snapshot at cc65.org:

 

32 bytes Lynx header with info about the author etc. The same format as usually.

4c 59 4e 58 00 04 00 04 01 00 43 61 72 74 20 6e
61 6d 65 20 20 20 20 20 20 20 20 20 20 20 20 20
20 20 20 20 20 20 20 20 20 00 4d 61 6e 75 66 61
63 74 75 72 65 72 20 20 20 00 00 00 00 00 00 00

52 bytes encrypted miniloader that will load the secondary loader to $F000

ff 30 73 35 4a a8 54 ef 54 20 f5 38 f4 35 7e 31
7a c3 f6 eb ee 30 e3 e5 81 91 85 bf 4b d9 cf 80
5f 54 36 b5 8a b0 50 d6 38 22 3e c1 01 a6 dd f5
4b 5e 6b 21

151 bytes secondary loader that will be read starting at $F000

a2 00 a0 08 ad b2 fc 95 26 e8 88 d0
f7 a5 26 85 2e 20 62 f0 a5 28 49 ff a8 a5 27 49
ff aa 20 39 f0 a5 2a a6 2b 85 31 86 32 a5 2d 49
ff a8 a5 2c 49 ff aa 20 44 f0 6c 2a 00 e8 d0 03
c8 f0 57 20 57 f0 80 f5 e8 d0 03 c8 f0 4c 20 57
f0 92 31 e6 31 d0 f1 e6 32 80 ed ad b2 fc e6 2f
d0 38 e6 30 d0 34 48 da 5a a5 1a 29 fc a8 09 02
aa a5 2e e6 2e 38 80 0b 90 04 8e 8b fd 18 e8 8e
87 fd ca 8e 87 fd 2a 8c 8b fd d0 ec a5 1a 8d 8b
fd 64 2f a9 fc 85 30 7a fa 68 60

8 bytes first directory entry. It will be loaded into ZP RAM

00 d3 00 88 00 02 f2 0f

At this point the memory is free from 0200 to EFFF. So you can now load quite a big chunk in RAM.

 

--

Karri

Link to comment
Share on other sites

This is great information to go into the platform specific doc of cc65! :) I see those are horribly outdated... they even have my name and very old email address attached.

 

Oh, and nice work, Karri! You've been doing an excellent job with the cc65 Lynx target, thank you!

Edited by Shawn Jefferson
Link to comment
Share on other sites

There is no reason. I move it to $FB68 and try if it works from there.

 

In the early days there was those 1k competitions. The absolute minimum would be:

64 bytes lnx header

52 bytes encrypted loader

908 bytes for the application

 

I will make an encrypted loader that loads in 908 bytes starting at $0400 and jumps there after the load is complete.

 

Are there any hard core coders up there for the competition?

 

There would be just one category. It can be a game, a demo, an utility, anything.

--

Karri

Edited by karri
Link to comment
Share on other sites

The cc65 now has the secondary loader at FB68.

A file game.lnx now looks like this.

 

The lnx header. Needed for Handy only:

4c 59 4e 58 00 04 00 04 01 00 43 61 72 74 20 6e
61 6d 65 20 20 20 20 20 20 20 20 20 20 20 20 20
20 20 20 20 20 20 20 20 20 00 4d 61 6e 75 66 61
63 74 75 72 65 72 20 20 20 00 00 00 00 00 00 00

 

The bootloader at the start of the cart:

ff 81 ca 33 be 80 a2 c4 6d 98 fe 8d bc 66 c0 7a
09 50 23 28 18 c8 06 70 58 4f 1b e1 c7 90 08 cd
1a 6e 5a 45 32 d7 6d c6 8a e5 d8 5c a0 e8 4f 7a
5f 73 8d 22

 

and the secondary loader that follows the encrypted loader

a2 00 a0 08 ad b2 fc 95 26 e8 88 d0
f7 a5 26 85 2e 20 ca fb a5 28 49 ff a8 a5 27 49
ff aa 20 a1 fb a5 2a a6 2b 85 31 86 32 a5 2d 49
ff a8 a5 2c 49 ff aa 20 ac fb 6c 2a 00 e8 d0 03
c8 f0 57 20 bf fb 80 f5 e8 d0 03 c8 f0 4c 20 bf
fb 92 31 e6 31 d0 f1 e6 32 80 ed ad b2 fc e6 2f
d0 38 e6 30 d0 34 48 da 5a a5 1a 29 fc a8 09 02
aa a5 2e e6 2e 38 80 0b 90 04 8e 8b fd 18 e8 8e
87 fd ca 8e 87 fd 2a 8c 8b fd d0 ec a5 1a 8d 8b
fd 64 2f a9 fc 85 30 7a fa 68 60

 

After the secondary loader you need one directory entry:

00	 = block number
d3 00 = offset $00d3
88	 = dummy byte
00 02 = start address in RAM $0200
07 10 = length of binary to load $1007

 

Then you just append your binary code you want to load.

 

You can change the start address and length to match your code.

 

The last fc in the secondary loader should be different for different cart types.

 

fe = 128k cart

fc = 256k cart

f8 = 512k cart

--

Karri

Edited by karri
  • Like 1
Link to comment
Share on other sites

I just cannot help spamming this group with the smallest bootloader EVER!

 

This bootloader reads in a single Lynx cart block to $0400. I admit that the maximum length it can handle is a 2048 byte binary. Not very useful. But it would make a nice competition loader for minimal games :).

 

ff 5e 9b aa 96 8b f8 16 1f 4a d3 3b 03 c1 ce 35
bb 74 10 34 d2 54 09 cc 91 28 48 1f 56 5c 4e 01
4b e0 30 39 5e 00 36 58 a5 2e 0c 16 57 db 79 cd
4c b5 7a 2d

 

Here you don't have secondary bootloader at all and no directory entries. Just the bootloader and your code starting at 0400.

 

--

Karri

Link to comment
Share on other sites

forget the loader and just encrypt your complete game.

you would have to split it into 4 chunks of 250 bytes each and have just jump to the boot image three times.

the mein problem is the initialization of the hardware on the _real_ machine, which eats up space.

and, you will not even notive that it wont run if you always test in on the emulator :-b

Link to comment
Share on other sites

Thank you Karri for keeping it alive.

To make things simple, do you have a sample of the new cc65 directory structure source file ?

 

The default directory looks like this:

 

;
; Karri Kaksonen, 2011
;
; A default directory with just the main executable.
;
 .include "lynx.inc"
 .import		 __STARTOFDIRECTORY__
 .import		 __RAM_START__
 .import		 __CODE_SIZE__,__DATA_SIZE__,__RODATA_SIZE__
 .import		 __STARTUP_SIZE__,__INIT_SIZE__
 .import		 __BLOCKSIZE__
 .export		 __DEFDIR__: absolute = 1


; ------------------------------------------------------------------------
; Lynx directory
 .segment "DIRECTORY"

__DIRECTORY_START__:
off0=__STARTOFDIRECTORY__+(__DIRECTORY_END__-__DIRECTORY_START__)
blocka=off0/__BLOCKSIZE__
; Entry 0 - first executable
block0=off0/__BLOCKSIZE__
len0=__STARTUP_SIZE__+__INIT_SIZE__+__CODE_SIZE__+__DATA_SIZE__+__RODATA_SIZE__
 .byte <block0
 .word off0 & (__BLOCKSIZE__ - 1)
 .byte $88
 .word __RAM_START__
 .word len0
__DIRECTORY_END__:

 

If you want to make the directory larger you need to remove the line containing __DEFDIR__ in the config file.

SYMBOLS {
__STACKSIZE__: type = weak, value = $0800; # 2k stack
__STARTOFDIRECTORY__: type = weak, value = $00CB; # start just after loader
__BLOCKSIZE__: type = weak, value = 1024; # cart block size
__EXEHDR__: type = import;
__BOOTLDR__: type = import;
__DEFDIR__: type = import; # REMOVE THIS LINE
}

 

Instead you create your own version of this file and link it in after the bootloader.

You need to increase the size from 8 bytes of the DIR memory segment to 8 times number of directory entries.

MEMORY {
ZP:	 file = "", define = yes, start = $0000, size = $0100;
HEADER: file = %O,			 start = $0000, size = $0040;
BOOT: file = %O,			 start = $0200, size = __STARTOFDIRECTORY__;
DIR: file = %O,			 start = $0000, size = 8;
RAM: file = %O, define = yes, start = $0200, size = $BE38 - __STACKSIZE__;
}
SEGMENTS {
EXEHDR: load = HEADER, type = ro;
BOOTLDR: load = BOOT, type = ro;
DIRECTORY:load = DIR, type = ro;
STARTUP: load = RAM, type = ro, define = yes;
LOWCODE: load = RAM, type = ro,			 optional = yes;
INIT:	 load = RAM, type = ro, define = yes, optional = yes;
CODE:	 load = RAM, type = ro, define = yes;
RODATA: load = RAM, type = ro, define = yes;
DATA:	 load = RAM, type = rw, define = yes;
BSS:	 load = RAM, type = bss, define = yes;
ZEROPAGE: load = ZP,	 type = zp;
EXTZP: load = ZP,	 type = zp,			 optional = yes;
APPZP: load = ZP,	 type = zp,			 optional = yes;
}

 

I am using a macro to calculate the offset and block number.

 

.macro entry old_off, old_len, new_off, new_block, new_len, new_size, new_addr
new_off=old_off+old_len
new_block=new_off/__BLOCKSIZE__
new_len=new_size
 .byte <new_block
 .word (new_off & (__BLOCKSIZE__ - 1))
 .byte $88
 .word new_addr
 .word new_len
.endmacro

 

The directory entries are then built by calling the macro:

_MAIN_FILENR=2+$1000
entry off1, 0, off2, block2, len2, len1, __CODE_LOAD__

_TITLEBG_FILENR=_MAIN_FILENR+1
entry off0, 0, titlebgoff, titlebgblock, titlebglen,__TITLE_SIZE__, __TITLE_START__

_INTRO_FILENR=_TITLEBG_FILENR+1
entry off2, len2, introoff, introblock, introlen,__STARTUP_SIZE__+__INIT_SIZE__+__INTRO_CODE_SIZE__+__INTRO_RODATA_SIZE__+__INTRO_DATA_SIZE__, __STARTUP_LOAD__

Edited by karri
Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Loading...
  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...