Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

C64: Memory location for custom character set? #806

Closed
MartinEmrich opened this issue Nov 28, 2018 · 33 comments
Closed

C64: Memory location for custom character set? #806

MartinEmrich opened this issue Nov 28, 2018 · 33 comments
Labels

Comments

@MartinEmrich
Copy link

Hello!

I'd like to define a custom character set. As this works by copying the Character ROM to RAM and switching around VIC banks and offsets, I need a free memory block at a correctly aligned position.
Ideally, the location would be defined at compile/link time.

Some Ideas I had:

  • Instruct compiler/Linker to ignore e.g. 0x3800-0x3fff?
  • Somehow declare a global unsigned char charset[0x0800] with a fixed memory address?

Is there a way?

Thanks

Martin

/label Question

@mrdudz
Copy link
Contributor

mrdudz commented Nov 28, 2018

i would put it at the start or the end of the memory. ie either $0800 or below i/o or kernal (somehwhere after $d000, this will require relocating the screen too). both will require custom linker config and possibly a modified crt0/startup code.

@silverdr
Copy link
Contributor

silverdr commented Nov 29, 2018 via email

@groessler
Copy link
Contributor

i would put it at the start or the end of the memory. ie either $0800

How could this work? C64 progs start at $0801 by default, if you want a BASIC SYSxxxx starter, unless you load them with ,8,1 (and then type SYS<entry_pt>.

@MartinEmrich
Copy link
Author

@silverdr Thanks, that seems to be what I need.

Took quite some trial and error.
I copied the default c64.cfg, and added some lines for the charset:

SYMBOLS {
    __LOADADDR__:  type = import;
    __EXEHDR__:    type = import;
    __STACKSIZE__: type = weak, value = $0800; # 2k stack
    __HIMEM__:     type = weak, value = $D000;
    __VIDEO__:     type = weak, value = $C000;
    __CHARSET__  : type = weak, value = $C800;
}
MEMORY {
    ZP:       file = "", define = yes, start = $0002,           size = $001A;
    LOADADDR: file = %O,               start = %S - 2,          size = $0002;
    HEADER:   file = %O, define = yes, start = %S,              size = $000D;
    MAIN:     file = %O, define = yes, start = __HEADER_LAST__, size = __VIDEO__ - __HEADER_LAST__;
    VIDEO:    file = %O, define = yes, start = __VIDEO__,       size = $0800;
    CHARSET:  file = %O, define = yes, start = __CHARSET__,     size = $0800;
    BSS:      file = "",               start = __ONCE_RUN__,    size = __VIDEO__ - __STACKSIZE__ - __ONCE_RUN__;
}
SEGMENTS {
    ZEROPAGE: load = ZP,       type = zp;
    LOADADDR: load = LOADADDR, type = ro;
    EXEHDR:   load = HEADER,   type = ro;
    CHARSET:  load = CHARSET,  type = bss,  define   = yes;
    VIDEO:    load = VIDEO,    type = bss,  define   = yes;
    STARTUP:  load = MAIN,     type = ro;
    LOWCODE:  load = MAIN,     type = ro,  optional = yes;
    CODE:     load = MAIN,     type = ro;
    RODATA:   load = MAIN,     type = ro;
    DATA:     load = MAIN,     type = rw;
    INIT:     load = MAIN,     type = rw;
    ONCE:     load = MAIN,     type = ro,  define   = yes;
    BSS:      load = BSS,      type = bss, define   = yes;
}

(left the FEATURES as-is).

@mrdudz I decided to steal 4k from between the MAIN and HIMEM thing. Fiddling around at 0x800 just gave crashes, and after 0xD000 is only I/O and KERNAL, I guess cc65 runtime needs them?

A little test program seems to run fine, but it would be great if an expert proof-read that my modified configuration is valid. (I have no idea what a BSS or bss is...)

Thanks,

Martin

@mrdudz
Copy link
Contributor

mrdudz commented Nov 29, 2018

How could this work?

easy, just use some packer on the resulting binary - which you likely want to do anyway. adding "basic starters" to non packed binary isnt a great idea to begin with, its just a waste of bytes :)

(this whole discussion once again shows that before using cc65 its a good idea to understand the target and write a bunch of assembler programs)

@mrdudz
Copy link
Contributor

mrdudz commented Nov 29, 2018

after 0xD000 is only I/O and KERNAL, I guess cc65 runtime needs them?

there is RAM under I/O and "under" kernal (at the same address, in a different memory configuration), its a perfect location for graphics data. as said, you'd have to relocate the screen to then. (and yet again, it helps to understand the platform)

@groessler
Copy link
Contributor

easy, just use some packer on the resulting binary

I don't see how that would work with a "standard" cc65 C64 which was compiled with "cc65 -t c64 infile.c -o outfile". I understand how it can work with a program which starts at e.g. $2000.

adding "basic starters" to non packed binary isnt a great idea to begin with, its just a waste of bytes :)

I disagree. Such a program would be the typical output of a cc65 invocation as I've written above. You can add a packer if you want, but I don't consider that as crucial. To each his own....

that before using cc65 its a good idea to understand the target and write a bunch of assembler programs

I've written quite some assembler programs for C64 30 years ago. So I'm considering myself semi-knowlegeable (or make that semi-semi-knowledgeable, given that I haven't done much C64 related stuff the last 30 years....)

@mrdudz
Copy link
Contributor

mrdudz commented Nov 29, 2018

I don't see how that would work with a "standard" cc65 C64 which was compiled with "cc65 -t c64 infile.c -o outfile"

of course it doesnt. you'll push the low memory boundary to $1000. obviously. every solution for this problem needs a custom linker config...

I disagree. Such a program would be the typical output of a cc65 invocation as I've written above.

that doesnt make the statement less valid. in a typical c64 build chain the binary is pretty much always packed in the last step. putting a basic starter into the non packet binary is a pointless exercise in that situation. and $800 is infact a pretty common location for a characterset too :)

@groessler
Copy link
Contributor

@mrdudz: From the OP's question I cannot infer that he's writing an ASM Program. All what you are saying makes sense to me (except that a final EXE "needs" to be packed). IDK right now if it's currently possible (without much fiddling) to create a cc65 C EXE which starts at e.g. $1000 and doesn't contain the BASIC loader.

So I guess, we started from different positions. I was thinking of a C program which which gets compiled with default options with cc65, while you were starting from an already advanced setup where defaults were changed (no BASIC loader). That the linker cfg file needs to be changed is obvious.

@mrdudz
Copy link
Contributor

mrdudz commented Nov 29, 2018

i dont get your point. just read my first answer, its all there. adding a custom charset to a cc65 program required fiddling, no matter how you do it. and you cant do it with default options (because that implies default linkerscript).

and no, the exe doesnt "need" to be packed. however in practise you almost always want to do this anyway. and throwing the opportunity to have a charset at $800 away just so you can have a basic start in your non packed binary is just silly. the alternative is to put it at the memory top like in the posted config - now the binary will be really huge, and you want to pack it =) and it will require more fiddling because the screen needs to be relocated too. and as soon you want to add more gfx data, it will go under i/o - which results in a binary that you cant even load in non packed form. did i say you typically end up packing the binary? :=)

that said, this linker config doesnt look right. you'll have to use fill=yes to the sections prior those you added so the screen/charset actually ends up at $c000/$c800. you also want to put bss and stack after MAIN and before VIDEO, else they will overwrite the charset/screen.

i still recommend to put the charset to $800, that is actually easier and requires less fiddling. but yes, it will require to pack the resulting binary in order to run it.

@groessler
Copy link
Contributor

i dont get your point. just read my first answer

Yes. This answer is accurate. I've been just stumbling over the $800 address which is not possible with means out of the cc65 box. At least, to get a program which you can 'LOAD"xxx",8"' and then 'RUN' it.

I agree that it makes more sense to put the charset to $800, and not to the end of the program.

What I don't like is the need of a 3rd-party packer (or some kind of loader) to setup this memory layout.
Maybe such a loader could be added to the runtime and selectable with a different linker config file. Instead of c64.cfg use e.g. c64-varaddr.cfg to have a program at arbitrary addresses, but still be able to load it with 'LOAD"xxx",8' and 'RUN'. Startaddress could be selected on the cc65 command line with --start-addr like on Atari.

@mrdudz
Copy link
Contributor

mrdudz commented Nov 29, 2018

What I don't like is the need of a 3rd-party packer (or some kind of loader) to setup this memory layout.

but why in heavens name? 99.999% of all programs made in the last decades are like that. its the norm. and adding yet another layer of moving things around just so you can have that silly basic starter only adds more useless unwanted overhead and makes the program longer than it has to be. and then when you actually pack this program - which you likely want to do anyway - all that overhead becomes even more useless and pointless. just dont.

@groessler
Copy link
Contributor

but why in heavens name?

Yeah, maybe I'm a bit stubborn in this regard. I'd like to have cc65 be "self-contained", so it can create programs without external tools. This doesn't include tools which convert the EXE to a ROM image, or disk image. But the output of cc65 should be "the thing".

Given that I'm not much into the C64 (or any CBM) scene these days, I don't want to argue much more about the issue at hand. If a 3rd-party tool is needed to create such a C64 image from a cc65 program, I'm fine with it.

Maybe a documentation update could be done to explain this use case? :-)

@mrdudz
Copy link
Contributor

mrdudz commented Nov 29, 2018

To be honest, for me it all boils down to "know the platform before using cc65". i'd even go as far as saying that if you dont master the platform already, you shouldnt be using cc65 - because most likely, for anything non trivial, you will run into problems that require you to know a lot of things about the platform that the cc65 docs do not, and should not, explain.

@groessler
Copy link
Contributor

know the platform before using cc65

Just to say, that's a nice theory, but sadly doesn't work in real-world :-(
We should give the newbies a hand.

@mrdudz
Copy link
Contributor

mrdudz commented Nov 29, 2018

it does work. i frequently tell them to learn a bit of asm first and stay away from cc65 unless they know what they are doing. C on 8bit systems is not for beginners. period.

@groessler
Copy link
Contributor

OTOH, I'm always trying to get people into cc65, even beginners. It's not that hard, after all :-)

@mrdudz
Copy link
Contributor

mrdudz commented Nov 29, 2018

It's a bad idea. Neither is cc65 a good platform to learn C, nor is it a good platform to learn programming any 8 bit computer.

@groessler
Copy link
Contributor

To learn C not, sure. But if anyone wants to write 6502 ASM programs (or learn how to do), ca65 is almost as good as anything else. The concept of linker scripts is the most difficult part I guess.

@mrdudz
Copy link
Contributor

mrdudz commented Nov 29, 2018

the concept of linkerscripts is actually what makes it a lot harder for beginners. traditional assemblers are much better suited for most things that beginners are doing.

@groessler
Copy link
Contributor

the concept of linkerscripts is actually what makes it a lot harder for beginners

Agreed.

@silverdr
Copy link
Contributor

FWIW - using an external packer is not required and should not be required (IMNSHO). Also you shouldn't have to know all the target's specifics to program something in C. Once you get to know all the more advanced stuff, you can start optimising things. But even then I stopped depending on packers the moment I started to use fast DOS (first Dolphin, then IDEDOS) as it's a pure waste of time to have things loaded in 1 - 2 seconds and then having to wait 15 for it to depack itself (sic!) before you can run it. Yes, if things need to be crunched to fit (e.g. the infamous 202 blocks) it's a different story or if you target audience that is most probably devoid of any fast DOS (who is this non-negligible audience of this kind today?), yes - then OK, but not when you just want to get your program compiled and running. As for linker configs - yes, it's tricky for users who got used to classic assemblers with no proper linker. But if you begin learning, you can jump right into it and never look back once you get to know how these work. I don't know how is this harder than learning bad habits with no-linker-tools.

@mrdudz
Copy link
Contributor

mrdudz commented Nov 29, 2018

it's a pure waste of time to have things loaded in 1 - 2 seconds and then having to wait 15 for it to depack itself

it will hardly take a second or two using the right tools for the job. and its still stupid to use 202 blocks on disk only because you have put the charset to $c800.

and no, it doesnt have anything to do with bad habits. while the linker is really great for certain situations, for typical small projects like beginners are making its not only unnecessary, it also gets in the way a lot. i moved my demo stuff to acme myself after i made some demo things in ca65 and learned that the linker most of the time causes more hazzle than it solves. again, use the right tool for the job, there is no silver bullet.

@groessler
Copy link
Contributor

its still stupid to use 202 blocks on disk only because you have put the charset to $c800.

Definitely, if your program ends at $5000 and you fill the memory until $c800 in the output file. I guess nobody is arguing about that.
Seems to bold down to know what you're doing. In this case, to me, it would be acceptable to have the charset at the end of the EXE and run a small routine at startup which copies it into place ($c800).

i made some demo things in ca65 and learned that the linker most of the time causes more hazzle than it solves

examples?

@silverdr
Copy link
Contributor

silverdr commented Nov 30, 2018

it will hardly take a second or two using the right tools for the job. and its still stupid to use 202 blocks on disk only because you have put the charset to $c800.

You can pack it then :-P (SCNR :-)

and no, it doesnt have anything to do with bad habits. while the linker is really great for certain situations, for typical small projects like beginners are making its not only unnecessary, it also gets in the way a lot.

Only if you already have some habits. For all the hello.c stuff there are default configs, which beginners don't even see.

i moved my demo stuff to acme myself after i made some demo things in ca65 and learned that the linker most of the time causes more hazzle than it solves.

Which basically says that you haven't got accustomed to the way things are to be written in cc65/ca65 and tried to carry-in the ways you learned elsewhere. Sorry - if there is only one feature that makes ca65 preferable over other tools of this kind, then it is the linker. <-- period. But you have to unlearn what you have learnt before you started with ca65. I know, I had to. Once you do though, then there is no way back.

again, use the right tool for the job, there is no silver bullet.

Sure, if you look for example for some predefined demo-coding stuff then you may prefer e. g. KickAss or whatever gives you the best set of options but if we talk about efficient, generic 6502 programming then get proficient with the linker and stop saying the same things I kept stubbornly saying some good years ago when I first encountered linker configs in ca65 and kept returning to my earlier toolchain because I sincerely believed that the linker "caused more hazzle than it solves" ;-)

@oliverschmidt
Copy link
Contributor

In this case, to me, it would be acceptable to have the charset at the end of the EXE and run a small routine at startup which copies it into place ($c800).

Although the OP didn't ask for that it might have been beneficial to add the hint in the first place that instead of jumping through loops to have the charset at the right place after loading (and potentially unpacking) one might as well consider to memcpy() it to the right place - obviously with the knowledge that this place isn't used by the program for other purposes.

@oliverschmidt
Copy link
Contributor

To be honest, for me it all boils down to "know the platform before using cc65". i'd even go as far as saying that if you dont master the platform already, you shouldnt be using cc65 - because most likely, for anything non trivial, you will run into problems that require you to know a lot of things about the platform that the cc65 docs do not, and should not, explain.

I heartily disagree. If I had to chose a single aspect of cc65 that I consider most relevant then it would be for sure that it allows to create significant stuff for targets you don't know because of all the target knowledge distilled into the target libraries.

E.g. I some day stumbled over http://www.atari8ethernet.com/. I figured out the CS8900A was mapped at $D500. So I built Contiki for the Atari. I had never done anything whatsoever with or for that machine before. It took me a few hours, of which most were spend figuring out how to create an .ATR disk image containing the binaries. I sent it to Mark and the binaries worked out-of-the-box. I guess you can imagine how surprised he was to get the by far most interesting programs for his hardware from somebody not knowing the platform - not even talking about having his hardware (and there was no emulation of that hardware available by then).

In these scenarios cc65 doesn't just simplify or expedite projects like yet another even more overfeatured assembler. It brings projects to the world which just wouldn't exist without it - that's what I personally consider cool !

@oliverschmidt
Copy link
Contributor

i would put it at the start or the end of the memory. ie either $0800 or below i/o or kernal (somehwhere after $d000, this will require relocating the screen too). both will require custom linker config and possibly a modified crt0/startup code.

At least as far as I can see the last sentence isn't true. I seem to understand that having the font data at $C800 is aviable option. So one can...

@oliverschmidt
Copy link
Contributor

At least according to my understanding there's also no need for a custom linker config when the goal is to produce a binary without the BASIC SYS line (and a non-default start addr):

BTW: The Apple II linker config has the very same type of flexibility (using the same names). The only difference is that I created doceumentation for it (https://cc65.github.io/doc/apple2.html#ss4.1) while nobody cared enough to do the same for the C64 - although the C64 is generally perceived as the prime cc65 target...

@mrdudz
Copy link
Contributor

mrdudz commented Nov 30, 2018

examples?

that would be a bit much to tell here. however there are various situations when you have to eg put certain data - or even code - to specific places in memory and then carefully craft the rest of the program and data around it. this stuff can get confusing quickly, and having the memory placement in a linker config doesnt really help (you have to do all the placement manually anyway).

Which basically says that you haven't got accustomed to the way things are to be written in cc65/ca65 and tried to carry-in the ways you learned elsewhere.

nonsense. i am using the linker extensively in other projects (eg the chameleon menu system) and i really wouldnt want to do this stuff without a linker. as said, its not about habits, its about the right tool for the job.

If I had to chose a single aspect of cc65 that I consider most relevant then it would be for sure that it allows to create significant stuff for targets you don't know because of all the target knowledge distilled into the target libraries.

we have different approaches to this for sure. i am not very interested in generic textmode applications, or graphics output so slow its unuseable for anything half serious for that matter. this was different 10 years ago when i played with cc65 first, but it became boring quickly :)

In these scenarios cc65 doesn't just simplify or expedite projects like yet another even more overfeatured assembler.

ca65 looks overfeatured compared to the assembler i am using now (acme) :=)

that said, i'd really like to see one of those non fiddly solutions for the original problem now :) preferably without sacrificing any other cc65 features (keep conio working, and the mouse drivers, etc). i still say $800 is the way to go =P

@oliverschmidt
Copy link
Contributor

i'd really like to see one of those non fiddly solutions for the original problem now :) preferably without sacrificing any other cc65 features (keep conio working, and the mouse drivers, etc). i still say $800 is the way to go =P

I agree with that one - the question of C64 custom font support has definitively appeared often enough to deserve an out-of-the-box solution suitable for beginners - and as mentioned without breaking stuff.

@MartinEmrich
Copy link
Author

Great scott, what happened here? What did I start here? :)

Just to give my opinion here as the (beginner to intermediate) target audience(?):

I just wanted to code some stuff for my C64 to play around with my new User Port WiFi modem, and I saw 3 options:

  • BASIC: I'd rather remove my left toe with a spoon than fiddling with line numbers again.
  • Assembler: Barely touched that "in the old days", and that was when I could skip homework at 1pm and get right at playing around with raster interrupt lines... Today I am happy if I have 1-2h in the evening. (And I feel my right toe tingling, too).
  • Google around for some modern means... Maybe someone even wrote a C compiler for C64?

And I found cc65, and as a C/C++/Java/Whatever dev, I found myself right at home, and got results fast, also thanks to the serial driver framework, conio and the familiar POSIX C library functions.

And indeed, unless I have to, I do not want to dive into platform specifics more than necessary.

I now had to do it for the fonts, and (hopefully) got them working.
But I still don't see the need for raster interrupts, self-unpacking code, etc., and I do not care whether there's a BASIC stub with a SYS call or some other means to start my program, as long as I can RUN it :)

Just my 0.02€

@oliverschmidt A ootb library would indeed be great. I got conio working so far by setting 0x00D1 to the new video memory address 0xC000.

@oliverschmidt
Copy link
Contributor

@MartinEmrich: Thanks for your detailed feedback. It's appreciated :-)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

5 participants