Jump to content
IGNORED

New Atari BBS software.


tschak909

Recommended Posts

The linker configuration has changed significantly with the newest builds of cc65, am going to have to piece together something between danwinslow's example and the cc65 overlay example:

FEATURES {
    STARTADDRESS: default = $2000;
}
SYMBOLS {
    __EXEHDR__:          type = import;
    __SYSTEM_CHECK__:    type = import;  # force inclusion of "system check" load chunk
    __AUTOSTART__:       type = import;  # force inclusion of autostart "trailer"
    __STACKSIZE__:       type = weak, value = $0800; # 2k stack
    __OVERLAYSIZE__:     type = weak, value = $1000; # 4k overlay
    __STARTADDRESS__:    type = export, value = %S;
    __RESERVED_MEMORY__: type = weak, value = $0000;
}
MEMORY {
    ZP:            file = "", define = yes, start = $0082, size = $007E;
 
# file header, just $FFFF
    HEADER:        file = %O,               start = $0000, size = $0002;
 
# "system check" load chunk
    SYSCHKHDR:     file = %O,               start = $0000, size = $0004;
    SYSCHKCHNK:    file = %O,               start = $2E00, size = $0300;
    SYSCHKTRL:     file = %O,               start = $0000, size = $0006;
 
# "main program" load chunk
    MAINHDR:       file = %O,               start = $0000, size = $0004;
    RAM:           file = %O, define = yes, start = %S + __OVERLAYSIZE__,
                                                           size = $BC20 - __OVERLAYSIZE__ - __STACKSIZE__ - __RESERVED_MEMORY__ - %S;
    TRAILER:       file = %O,               start = $0000, size = $0006;
 
    OVL1:          file = "%O.1",           start = %S,    size = __OVERLAYSIZE__;
    OVL2:          file = "%O.2",           start = %S,    size = __OVERLAYSIZE__;
    OVL3:          file = "%O.3",           start = %S,    size = __OVERLAYSIZE__;
    OVL4:          file = "%O.4",           start = %S,    size = __OVERLAYSIZE__;
    OVL5:          file = "%O.5",           start = %S,    size = __OVERLAYSIZE__;
    OVL6:          file = "%O.6",           start = %S,    size = __OVERLAYSIZE__;
    OVL7:          file = "%O.7",           start = %S,    size = __OVERLAYSIZE__;
    OVL8:          file = "%O.8",           start = %S,    size = __OVERLAYSIZE__;
    OVL9:          file = "%O.9",           start = %S,    size = __OVERLAYSIZE__;
}
SEGMENTS {
    EXEHDR:    load = HEADER,     type = ro;
    SYSCHKHDR: load = SYSCHKHDR,  type = ro,                optional = yes;
    SYSCHK:    load = SYSCHKCHNK, type = rw,  define = yes, optional = yes;
    SYSCHKTRL: load = SYSCHKTRL,  type = ro,                optional = yes;
    MAINHDR:   load = MAINHDR,    type = ro;
    STARTUP:   load = RAM,        type = ro,  define = yes;
    LOWCODE:   load = RAM,        type = ro,  define = yes, optional = yes;
    INIT:      load = RAM,        type = ro,                optional = yes;
    CODE:      load = RAM,        type = ro,  define = yes;
    RODATA:    load = RAM,        type = ro;
    DATA:      load = RAM,        type = rw;
    BSS:       load = RAM,        type = bss, define = yes;
    ZEROPAGE:  load = ZP,         type = zp;
    EXTZP:     load = ZP,         type = zp,                optional = yes;
    AUTOSTRT:  load = TRAILER,    type = ro;
    OVERLAY1:  load = OVL1,       type = ro,  define = yes, optional = yes;
    OVERLAY2:  load = OVL2,       type = ro,  define = yes, optional = yes;
    OVERLAY3:  load = OVL3,       type = ro,  define = yes, optional = yes;
    OVERLAY4:  load = OVL4,       type = ro,  define = yes, optional = yes;
    OVERLAY5:  load = OVL5,       type = ro,  define = yes, optional = yes;
    OVERLAY6:  load = OVL6,       type = ro,  define = yes, optional = yes;
    OVERLAY7:  load = OVL7,       type = ro,  define = yes, optional = yes;
    OVERLAY8:  load = OVL8,       type = ro,  define = yes, optional = yes;
    OVERLAY9:  load = OVL9,       type = ro,  define = yes, optional = yes;
}
FEATURES {
    CONDES: type    = constructor,
            label   = __CONSTRUCTOR_TABLE__,
            count   = __CONSTRUCTOR_COUNT__,
            segment = INIT;
    CONDES: type    = destructor,
            label   = __DESTRUCTOR_TABLE__,
            count   = __DESTRUCTOR_COUNT__,
            segment = RODATA;
    CONDES: type    = interruptor,
            label   = __INTERRUPTOR_TABLE__,
            count   = __INTERRUPTOR_COUNT__,
            segment = RODATA,
            import  = __CALLIRQ__;
}

It must be the stress of my day job, or i'm burning out, but am having trouble understanding how the different segments fit together and which ones are needed at a minimum for an atari binary. (e.g. I know I need at least the header, and one segment with a run and/or init address pointer...but what does CC65 need for its own housekeeping on top of that?)

 

-Thom

 

Well, I'm not familiar with the latest changes in the linker formats, but it looks like the overlay stuff is not actually setting up to use ext memory, its just defining a 4k block at $2000 into which you can load code. Seems like a fine idea to me, if you can live with the IO between modules. It leaves everything else pretty much in place, meaning all your data and so forth. It's basically what I did with the LOWCODE segment in my example. Its kind of a small overlay space though...the overlays would have to be very self contained, and only call functions that are not overlays themselves. I would think you could combine it with using ext banks, but you'd have to design the various chunks thoughtfully.You'd need some kind of an overlay loader, but that would be a simple task. Of course you could always just not use the overlays and go with regular ext banking.

 

What CC65 needs is the startup code from crt0.s, the stack, the C library code, all not banked or overlaid. Actually you could probably bank the startup code if you really wanted to but it wouldn't be worth it.

 

I have no idea what the SYSCHK stuff is all about.

Edited by danwinslow
Link to comment
Share on other sites

Ok, so there's a late binding facility to look up symbols. Very cool. Thanks Drac030. So it sounds like, technically at least, one can look up the malloc/free routines from a non-relocatable binary, provided that the cartridge is not disabled.

Yes. In fact, it is not relevant, if the library is enabled or not: in the latter case, the jfsymbol call will enable the library, do the search, and disable the library. The only inconvenience is a glitch in the screen, if the screen RAM and DL are located "under" the cartridge ($a000-$bfff).

 

I imagine, even if the cart was disabled, you could write a wrapper that would enable it and make the call, and the disable it again, which is what you were referring to when you said 'or the access becomes complicated'.

Yes. There is a kernel call which centralizes cartridge management (and makes it hardware abstracted, so that you do not have to worry what type of a cartridge the SDX is running on). You use it to enable the cartridge, when it is disabled, or disable it when it is enabled (this is obviously exactly the thing X.COM does). The only problem is the screen, the program wishing to manipulate the cartridge state must take care of the screen memory.

Link to comment
Share on other sites

Maybe this will help you?
http://cc65.github.io/cc65/doc/atari.html#ss11.4

Hmm, that's actually pretty old. Here's a quick example I whipped up:

FEATURES {
    STARTADDRESS: default = $2000;
}
SYMBOLS {
    __EXEHDR__:          type = import;
    #__SYSTEM_CHECK__:    type = import;  # force inclusion of "system check" load chunk
    __AUTOSTART__:       type = import;  # force inclusion of autostart "trailer"
    __STACKSIZE__:       type = weak, value = $0800; # 2k stack
    __STARTADDRESS__:    type = export, value = %S;
    __RESERVED_MEMORY__: type = weak, value = $0000;
}
MEMORY {
    ZP:            file = "", define = yes, start = $0082, size = $007E;

# file header, just $FFFF
    HEADER:        file = %O,               start = $0000, size = $0002;

# "system check" load chunk
    SYSCHKHDR:     file = %O,               start = $0000, size = $0004;
    SYSCHKCHNK:    file = %O,               start = $2E00, size = $0300;
    SYSCHKTRL:     file = %O,               start = $0000, size = $0006;

# "main program" load chunk
    MAINHDR:       file = %O,               start = $0000, size = $0004;
    RAM:	   file = %O, define = yes, start = %S, size = $4000 - %S, fill = yes;
    BANK0:	   file = %O, define = yes, start = $4000, size = $4000, fill = yes;
    RAMHI:         file = %O, define = yes, start = $8000, size = $BC20 - __STACKSIZE__ - __RESERVED_MEMORY__ - $8000;
    TRAILER:       file = %O,               start = $0000, size = $0006;
    
    BANK1:	   file = "%O.1", define = yes, start = $4000, size = $4000;
    BANK2:	   file = "%O.2", define = yes, start = $4000, size = $4000;
    
}
SEGMENTS {
    EXEHDR:    load = HEADER,     type = ro;
    SYSCHKHDR: load = SYSCHKHDR,  type = ro,                optional = yes;
    SYSCHK:    load = SYSCHKCHNK, type = rw,  define = yes, optional = yes;
    SYSCHKTRL: load = SYSCHKTRL,  type = ro,                optional = yes;
    MAINHDR:   load = MAINHDR,    type = ro;
    STARTUP:   load = RAM,        type = ro,  define = yes;
    LOWCODE:   load = RAM,        type = ro,  define = yes, optional = yes;
    INIT:      load = RAM,        type = ro,                optional = yes;
    CODE:      load = RAM,        type = ro,  define = yes;
    RODATA:    load = RAMHI,      type = ro;
    DATA:      load = RAMHI,      type = rw;
    BSS:       load = RAMHI,      type = bss, define = yes;
    ZEROPAGE:  load = ZP,         type = zp;
    EXTZP:     load = ZP,         type = zp,                optional = yes;
    AUTOSTRT:  load = TRAILER,    type = ro;
    
    BANK0SEG:  load = BANK0,	  type = ro, define = yes;
    BANK1SEG:  load = BANK1, 	  type = ro, define = yes;
    BANK2SEG:  load = BANK2,      type = ro, define = yes;
    
}
FEATURES {
    CONDES: type    = constructor,
            label   = __CONSTRUCTOR_TABLE__,
            count   = __CONSTRUCTOR_COUNT__,
            segment = INIT;
    CONDES: type    = destructor,
            label   = __DESTRUCTOR_TABLE__,
            count   = __DESTRUCTOR_COUNT__,
            segment = RODATA;
    CONDES: type    = interruptor,
            label   = __INTERRUPTOR_TABLE__,
            count   = __INTERRUPTOR_COUNT__,
            segment = RODATA,
            import  = __CALLIRQ__;
}
#include <stdlib.h>
#include <stdio.h>
#include <conio.h>


void bank0_stuff(void);


void main(void) {
  cprintf("Hello World! This is in main RAM.\n\r");
  cgetc();

  bank0_stuff();

  return;
}

#pragma code-name("BANK0SEG")
#pragma data-name("BANK0SEG")

unsigned int bank0_var = 0x0101;

void bank0_stuff(void)
{
	cprintf("This is in the main executable, and already loaded to $4000, no banking required.\n\r");
	cgetc();

	bank0_var = 0x3333;

    return;
}


#pragma code-name("BANK1SEG")
#pragma data-name("BANK1SEG")

unsigned int bank1_var = 0x1111;

void bank1_stuff(void)
{
	cprintf("This is in the bank1 file, you must load it into $4000 before jumping here!\n\r");
	cgetc();

	bank1_var = 0x3333;

    return;
}


#pragma code-name("BANK2SEG")
#pragma data-name("BANK2SEG")

unsigned int bank2_var = 0x2222;

void bank2_stuff(void)
{
	cprintf("This is in the bank2 file, you must load it into $4000 before jumping here!\n\r");
	cgetc();

	bank2_var = 0x3333;

    return;
}

This example is a bit contrived... but it shows the basic config file, splitting your "banked" code into different overlay files that you can then load into $4000 before calling. Ideally you would put each banks functions, data and rodata into it's own file (notice I didn't ask to put RODATA into the banks... cc65 by default tries to optimize your rodata, you are better off only using #pragma rodata-name("BLAH") only once in a source file. There is a switch to disable this, but I don't think you want to disable optimizations, just be aware of it!)

A few other things to be aware of..

- The default crt0.s file defines your C stack (and heap I believe), so be aware of what it's doing for you. I've rewritten crt0.s for some of my projects and just compile my own to have complete control of things like the stack.

- If you want to keep a "hole" in your code at $4000 then you need to somehow take account of that in your output file. I've used "fill = yes" to make the executable line up properly in memory when it's loaded in. The config file I linked to above in the cc65 documentation takes another approach.

- BSS is cleared by crt0.s, but only the BSS segment. Any BSS you try to put in other places won't get cleared, and you may have to do it yourself.

- Use the memory map file, it'll help you figure out what's going where.

I hope that helps somewhat. You're going to have to learn the linker config (I'm no expert either), and place your code where you want it, and make sure to keep in mind where things are when calling functions!

 

PS. I commented out the SYSCHECK stuff... if you want it, uncomment it.

extbank.zip

Edited by Shawn Jefferson
Link to comment
Share on other sites

syschk just checks that your machine has enough memory before loading the rest of the executable (it uses a separate executable segment to load and run first.)

 

The default crt0.s for the Atari (not atarixl) target puts the stack always at the top of memory:

72         sec
73         lda     MEMTOP
74         sbc     #<__RESERVED_MEMORY__
75         sta     APPMHI                  ; initialize our APPMHI value
76         sta     sp                      ; set up runtime stack part 1
77         lda     MEMTOP+1
78         sbc     #>__RESERVED_MEMORY__
79         sta     APPMHI+1
80         sta     sp+1                    ; set up runtime stack part 2
81
The heap, I believe sits just under the stack and runs down to BSS (I left BSS in high memory for that reason, but typically I don't use the heap in cc65, it's too heavy.)

16 __heaporg:
17         .word   __BSS_RUN__+__BSS_SIZE__        ; Linker calculates this symbol
18 __heapptr:
19         .word   __BSS_RUN__+__BSS_SIZE__        ; Dito
20 __heapend:
21         .word   __BSS_RUN__+__BSS_SIZE__
22 __heapfirst:
23         .word   0
24 __heaplast:
25         .word   0
26
 
27
 
28 ; Initialization. Will be called from startup!
29
 
30 .segment        "INIT"
31
 
32 initheap:
33         sec
34         lda     sp
35         sbc     #<__STACKSIZE__
36         sta     __heapend
37         lda     sp+1
38         sbc     #>__STACKSIZE__
39         sta     __heapend+1
40         rts

Is there enough room?

No, there never is. :) You have to manage your RAM... It depends on how many library functions you are trying to use. Heap will use a lot of RAM, so will printf, file routines, etc...

Edited by Shawn Jefferson
Link to comment
Share on other sites

  • 2 weeks later...

So, I pulled down the Syncterm source and tried to build it. Unfortunately, it looks like there are many compatibility issues with the source and the Xcode 6 libraries I have installed, and I don't have time to try and get a clean version built. I may try building on another Mac (one without Xcode 6) and see if that goes more smoothly. I did track down the code that does the copy/paste, in /src/conio/sdl_con.c:

 

[snip]

 

It looks like there's a 1:1 mapping of the functions, so it should be pretty straightforward to update the code.

 

Also, the app doesn't respond to Cmd-Q to quit, so it would be nice to fix that as well.

 

Al, just wondering if anything ever came of this.

 

--Josh

Link to comment
Share on other sites

  • 3 months later...
  • 2 months later...

Yes, this project is on hold. I managed to accomplish the bits I initially wanted to accomplish, that is, is it possible to do real software development for a 6502 target in C? Yes.

 

I am coming back to the project when i get a concise plan of attack, but I already know that I do not want to load the whole BBS into RAM. There simply is no reason for it, and not worth the added expense.

 

What I am intending to do, is to seperate the BB65 into seperate program modules:

 

* a resident core program, that provides common routines for all the programs, namely MODEM I/O, and simple input field processing for the sake of consistency.

* a menu processor, load a menu file, grab an input, exit with an exit code for what was selected.

* a message board reader

* a message board editor

* a file viewer (for bulletins, art, etc.)

* etc.

 

Plumbing through exit codes, as needed with probably a batch file.

 

This would keep memory consumption down, and allow the different modules to use memory best for what is needed at that moment.

 

No, it would not be slow. Keep in mind, I am running all of this on a SIDE 2, a hard drive is required for this BBS, full stop, and loading the modules won't slow down interactivity.

 

Some more to think about...

-Thom

  • Like 2
Link to comment
Share on other sites

  • 5 months later...

So, I pulled down the Syncterm source and tried to build it. Unfortunately, it looks like there are many compatibility issues with the source and the Xcode 6 libraries I have installed, and I don't have time to try and get a clean version built. I may try building on another Mac (one without Xcode 6) and see if that goes more smoothly. I did track down the code that does the copy/paste, in /src/conio/sdl_con.c:

 

[snip]

It looks like there's a 1:1 mapping of the functions, so it should be pretty straightforward to update the code.

 

Also, the app doesn't respond to Cmd-Q to quit, so it would be nice to fix that as well.

 

FWIW, Deuce did finally release SyncTerm v1.0 (Mac download link).

 

As part of 1.0, he fixed the broken copy-and-paste on the Mac!

 

So I unequivocally recommend SyncTerm to anyone using a Mac who wants to call BBSes. Tons of great features, including support for ATASCII.

 

--Josh

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...