a2stuff / a2d

Disassembly of the Apple II Desktop - ProDOS GUI
https://a2desktop.com
277 stars 20 forks source link

Make linker do the work for code segment #49

Open inexorabletash opened 6 years ago

inexorabletash commented 6 years ago

It can do padding (fill) etc.

eric-ja commented 6 years ago

@mgcaret I've got the MGTK disassembly to the point where it makes sense to package it up as module/library, being generally useful to DA's and apps besides DeskTop, as well as feature enhancements (Apple /// support, ...). Do you have any suggestions on how to run the linker? I believe we are leaning towards static linking.

mgcaret commented 6 years ago

@eric-ja

The ca65 linker is pretty powerful... there's a lot that can be done. I'll cover the case where we are building independent application first:

For a library, all symbols that other code is expected to use need to have an associated .export, and an associated .inc file should exist that has matching .import directives along with the constants, macros, and other related things.

The application then .includes the .inc file, and has access to all the defined symbols and such.

When linking the application in the simple case, specifying -( mgtk.o otherlib.o -) myapp.o (and assuming a valid linker configuration is also specified, in the simplest case this can be -t none -S 0x2000 to produce a ProDOS SYS file, for instance), it will do a fully resolved static linking with the specified libraries against myapp.o and produce an executable with the coded in myapp followed immediately by the code in the libraries with no alignment, etc.

Now, there may be cases where code has to be loaded specifically into certain locations. In that case, we need to segment all such code using .segment and .endsegment directives in the assembly source for each piece that needs specific placement or alignment.

So say we compile MGTK as relocatable object in segment of the same name. Then we can create a custom linker configuration that specifies where they go in the file, and where they run from memory. In particular, ld65 separates the file position from the run location, because things often need to move.

In the case of something that uses MGTK, it's certainly not going to run from the default ProDOS load address of $2000, because that's the graphics page. So, minimally, you create a segment for your loader code which is responsible for moving everything to its target locations, and create one or more segments for your application code. Then you create a custom linker config that tells it where the code is placed in the output file, and where it will ultimately run from. How this is done:

MEMORY {
   P8SYS: start = $2000, size=$3F00;
   APPRUN: start=$8000, size=$3F00;
}

SEGMENTS {
  LOADER: load=P8SYS;
  MYAPP: load=P8SYS, run=APPRUN, define=yes, align = $100;
}

Note that by saying define=yes, the linker automatically generates some exported symbols that describe the segment: __MYAPPLOAD__, \_MYAPPRUN__, and \_MYAPPSIZE_\ that the loader code can use to move the code to its run location.

So linking the application with this config produces a single binary that is ready to go as a SYS file.

Now, in the case of an application chunk like DESKTOP2, which has multiple overlays and output files, we have to combine a whole bunch of tricks, but here's what a chunk of linker config for it might look like, based on https://github.com/inexorabletash/a2d/blob/master/desktop/README.md

MEMORY {
  OUTPUT: start=$0000, size=$1BCE1, file ="DESKTOP2";
  LOADER: start=$2000, size=$580;
  AUX: start=$4000, size=$8000;
  AUXLC1: start=$D000, size=$1D00;
  AUXLC2: start=$FB00, size=$0500;
  MAIN: start=$4000, size=$7F00;
  MAIN0800: start=$0800, size=$0800;
  MAIN0290: start=$0290, size=$03EF;
  DISKCOPY: start=$0800, size=$0200;
  DISKCOPY1: start=$1800, size=$0200;
  DISKCOPY2: start=$D000, size=$2200;
  DISKCOPY3: start=$0800, size=$0B00;
  FORMAT: start=$0800, size=$1400;
  SELECTOR1: start=$9000, size=$1000;
  COMMON: start=$5000, size=$2000;
  FILECOPY: start=$7000, size=$0800;
  FILEDEL: start=$7000, size=$0800;
  SELECTOR2: start=$7000, size=$0800;
}

SEGMENTS {
  LOADER: load=OUTPUT, run=LOADER;
  MGTK: load=OUTPUT, run=AUX;
  DESKTOP1: load=OUTPUT, run=AUX;
  DESKTOP2: load=OUTPUT, run=AUXLC1;
  DESKTOP3: load=OUTPUT, run=AUXLC2;
  DESKTOP4: load=OUTPUT, run=MAIN;
  DESKTOP5: load=OUTPUT, run=MAIN0800;
  INVOKER: load=OUTPUT, run=MAIN0290:
  DISKCOPY: load=OUTPUT, run=DISKCOPY;
  DISKCOPY1: load=OUTPUT, run=DISKCOPY1;
  DISKCOPY2: load=OUTPUT, run=DISKCOPY2;
  DISKCOPY3: load=OUTPUT, run=DISKCOPY3;
  FORMAT: load=OUTPUT, run=FORMAT;
  SELECTOR1: load=OUTPUT, run=SELECTOR1;
  COMMON: load=OUTPUT, run=COMMON;
  FILECOPY: load=OUTPUT, run=FILECOPY;
  FILEDEL: load=OUTPUT, run=FILEDEL;
  SLEECTOR2: load=OUTPUT, run=SELECTOR2;
}

The file would also contain define=yes as needed, among other options.

Then, you would do ld65 -c DeskTop.l loader.o mgtk.o desktop.o invoker.o ... and hopefully land with the file correctly generated and everything lined up as it is supposed to be. I haven't looked too closely at alignment for the various modules, but the memory section can specify alignment in conjunction with the assembler .align statement.

Finally, there's the matter of a shared MGTK in the case of DeskTop. A separate .inc with symbols specific to the DeskTop MGTK is probably desirable in this case. Ideally it defines everything needed so that you could write an independent application or something that runs within the desktop as an overlay, and only need to change which .inc file you use and how it's linked to change the two.

inexorabletash commented 6 years ago

For a library, all symbols that other code is expected to use need to have an associated .export, and an associated .inc file should exist that has matching .import directives along with the constants, macros, and other related things.

For something like the LC segment of desktop.s which is all resources shared by the main/aux segments, is there an easy way export everything? I was thinking maybe we use a script to process a source file and output the actual .s and a corresponding .inc, but perhaps there's a better way?

(This is the main reason I still have desktop.s as a monolithic file, and haven't actively pursued using the linker properly.)

eric-ja commented 6 years ago

Thank you! Great info. Due to the size of MGTK, I was thinking of building it as a .lib, that way programs would get what they need. Could also be useful for resources like fonts and icons. I guess the same linking strategy would work?