commanderx16 / x16-rom

Other
153 stars 44 forks source link

Expose more KERNAL and BASIC functions #16

Open mist64 opened 5 years ago

mist64 commented 5 years ago

Applications should rely as little as possible on fixed addresses in KERNAL and BASIC, otherwise it will be impossible to change the layout of the code in the future, so it would make more sense to expose more functions through a jump table. Here is a list of useful functions:

  SGNFAC = $BC2B
  MEMARG = $BA8C
  ARGADD = $B86A
  ARGAND = $AFE9
  ARGDIV = $BB14
  FACMUL = $BA30
  MEMMUL = $BA28
  FACADD = $B867
  FACLOG = $B9EA
  FACSQR = $BF71
  FACEXP = $BFED
  FACABS = $BC58
  FACSIN = $E26B
  FACCOS = $E264
  FACTAN = $E2B4
  FACATN = $E30E
  FACSGN = $BC39
  FACNOT = $AED4
  FACRND = $E097
  FACWORD = $B7F7
  FACDIV = $BB0F
  BASINT = $BCCC
  FACPOW = $BF7B
  FACSUB = $B853
  FACOR = $AFE6
  FACMEM = $BBD7
  ARGFAC = $BBFC
  FACARG = $BC0C
  FACSTR = $BDDF
  FACINT = $B1AA
  RNDFAC = $BC1B
  REALFAC = $BBA2
  INTFAC = $B391
  WRITETIS = $A9E7
  GETTI = $BE68
  GETTIME = $AF7E
  COPYTIME = $AF87
  TI2FAC = $AF84
  PRINTSTRS = $AB25
  VALS = $B7B5
  CMPFAC = $BC5B
  BYTEFAC = $B3A2
  CRSRRIGHT = $AB3B
  INPUT = $A560
  OPENCH = $F34A
  CLOSECH = $F291
  TWAIT = $F6ED
EgonOlsen71 commented 5 years ago

Any plans on when this might happen?

mist64 commented 5 years ago

Can you give me some context on what kind of application you have in mind and what kinds of calls you would need?

EgonOlsen71 commented 5 years ago

It's a BASIC V2.0 (cross) compiler: https://github.com/EgonOlsen71/basicv2

I would like to port it to the Commander X16 and the runtime currently uses all the calls above to do its thing.

mist64 commented 5 years ago

In the meantime, you can use the symbol table from rom.txt. It won't stay the same between releases, but you can start working now, and switch over to the jump table once it has been added.

EgonOlsen71 commented 5 years ago

That should help. I assume the rom-c64.txt is the symbol table for the original C64 rom? At least it seems to be at first glance... I'll write a parser that takes both tables as input and tries to match them with the calls that my runtime uses. That should cover most of the calls already...i hope...

mist64 commented 5 years ago

No, rom-c64.txt are the symbols when building the (reduced functionality) X16 ROM for the C64.

If you want the original C64 symbols, build this: https://github.com/mist64/c64rom

EgonOlsen71 commented 5 years ago

Thanks! So what I'm doing now is to read the call addresses needed for my runtime, read both the symbol tables of the original C64 ROM and the X16 ROM and then search for a symbol at my runtime's call address in the C64 ROM. If there is one, I search for the address of the corresponding symbol in the X16 ROM and use that instead. If there is no symbol for that address, then it's a jump directly into some routine. I that case, I calculate the offset to the closest symbol in the C64 ROM that comes before it and apply this offset to the matching symbol's address in the X16 ROM. This process will work as long as the symbols have equal names in both ROMs and the routines themselves don't change in a way that the offset changes between C64 and X16, which might happen but doesn't seem that likely to me in the cases in which it matters.

I've not implemented DOS (or even MONITOR, makes no sense anyway in this context), but I've implemented VPOKE and VPEEK and...hey presto: https://github.com/EgonOlsen71/x16-compiled/tree/master/bin

EgonOlsen71 commented 5 years ago

I've added support for DOS to the compiler but this time, my mapping didn't work 100% because my rom.txt is from a newer version than the r30 release version. I fixed it by using the monitor and hacking the file for this particular call...no big deal. However, this will remain a common problem. So my question is: Would it be an option to add the rom.txt to the release of the emulator? That way, one could just point the compiler towards that file and compile a program that matches the used emulator version.

EgonOlsen71 commented 5 years ago

Oh, and referring to the initial post, forgot a few calls in that list:

ERRALL = $A437
ERRIQ = $B248
ERREI = $ACF4
ERRSYN = $AF08
ERRFNF = $F12F
mist64 commented 5 years ago

rom.txt will be included in releases: https://github.com/commanderx16/x16-emulator/commit/c8b001d10b25f9081ea02da475438a27148494d9

kktos commented 5 years ago

What about one step further with only 1 public address to call with a code/num of the function you want call ?

0800: LDX #$03 ; ARGAND
0802: JSR TOOLBOX_DISPATCH

or

0800: JSR TOOLBOX_DISPATCH
0803: db 03

Only 1 thing to publish and you can do whatever you wish/need with the jumptable. What do you think ?

EgonOlsen71 commented 5 years ago

I fail to see the benefit of that approach. It just adds another layer of abstraction but what does it give you in return? Instead of documenting and remembering the actual call, you have to document/remember the call's number. So no gain there. Instead, it complicates the usage in the client code. In the backend, you still have to maintain a kind of jump table that branches based on the code, so there's no benefit either. Plus it's slower. So what I'm not seeing here?

EgonOlsen71 commented 5 years ago

Oh, and the first approach wouldn't work anyway, because you might need X (or any other register) for the actual routine you are calling. The second approach doesn't have that problem, but it's ugly and you still have to juggle around registers to read that value from memory while keeping your call parameters in place.

kktos commented 5 years ago

Egon, you are right. It adds another layer of abstraction, and that's precisely the point :) Slower, again, you are correct. But it should simply adds a few cycles. That's usually the price for abstraction. You have to remember the call number. Yes, but you have to remember the address otherwise.... Ugly. Damn, you are right again :):) With the 68000, we used the TRAP instruction for that. This was a little bit more elegant. With the 6502, we could use the BRK $XX..... And with the 65816, they simply use a JSR to the dispatcher with a register having the function number, parms on the stack.... Very much the way a compiler would have generate it. So, it's just an idea I've already saw elsewhere and I thought worth sharing. And I can't argue with you as I agree with all your points :):) Viel Spass ;)

kktos commented 5 years ago

just another thought about Bank Switching....

One advantage to the toolbox_dispatcher is that the functions can be spread in different banks so with only an opcode/index for a specific function and a call to the TB_DIPATCH, the system can switch to the proper bank to call the function.....

Yes, that's the advantage of a layer of abstraction

EgonOlsen71 commented 5 years ago

I don't see that as an advantage. You could do the same thing with a jump table (granted, it's more than a simple redirect in that case, but that's hidden from you anyway). The only advantage of a dispatcher of that kind that I can see is, that you can plug different implementations of a functionality into a system and don't have to change the calling program. That's why DOS and BIOS calls on the PC worked in a similar way. But I don't see this in this particular case especially not when using JSR.

kktos commented 5 years ago

Yep, that's correct. This is how, for instance, Apple used to patch their system with the Apple IIgs and the Mac 68k.

kktos commented 5 years ago

Something came up today as some want to program the little beast with C. The calling convention. So far, all the KERNAL and BASIC functions are using the "ASM" calling convention, a.k.a, no convention :) It looks like the way of calling function I was suggesting has one good argument, the ability to be called by higher level languages. That is a strong argument for it, don't you think ?

EgonOlsen71 commented 5 years ago

Maybe we are talking about different things? Everything that your approach does can be done with a jump table and vice versa. Your approach makes sense in an environment where different implementations of the same functionality exist at a time and where you don't want to be forced to (or can't) reserve a defined and fixed memory location for a jump table. The PC BIOS is an example for that, where you would do something like

mov ah,0x0e
mov al,'a'
int 0x10

to print out an "a". But that's not in itself better than something like

mov al,'a'
call 0xC0001

Granted, you can't do that in the BIOS context, but that's not the point here.

Here, you have a fixed machine with a fixed and very simple memory layout, no virtual memory or anything. You don't need this extra layer of abstraction and I fail to see how that would improve anything when calling these things from a compiled C program. I call them from compiled BASIC programs and it works just fine this way. If we go one step further and look at a high level language instead, I fail see why something like

const ADD=1;
const SUB=2;
const MUL=3;
const SIN=4;

function calc(type, ...nums) {...}

is supposed to be better than making it explicit like

function add(num1, num2) {...}

function sub(num1, num2) {...}

function mul(num1, num2) {...}

function sin(num1) {...}

There are reaons for doing it in the former style and I sure did it myself every now and then, but it's nothing that I would prefer doing if I can avoid it.

kktos commented 5 years ago

with no calling convention, in ASM, you can pass parms to a function anyway you like, that is thru registers, thru stack, thru both. Most of the time, the registers' way is used as it's the easiest and fastest. So sometimes you'll be using Acc, some other times, X or Y. Not to mention when you're using some Status register flags. In ASM, that's perfectly understandable. But if you're programing in C, it'll become a nightmare and you will need a stub for every functions.

So yes, I'm talking of 2 diffrent things in one, you're right (as always :):) ) but 2 complementary things.

-1- to have a single point of entry -2- using a well defined calling convention

Even if the later could be done without the former, former you don't really fancy.

Kroc commented 5 years ago
  1. The X16 aims to follow the Commodore 64 style; jump-tables is that style. Having a mixture of two styles increases the burden on documentation and difficulty learning the system
  2. There are only three registers; adding a layer of indirection slows things down, drastically
kktos commented 5 years ago

Kroc, even if I totally disagree with the second sentence of your first point, I'm not against the jump tables, I was merely suggesting another way. And for the second point, keep in mind that the little beast clock will tick faster so we may have some more time :) And my last point was calling convention. As long as we ALWAYS use, for instance, Acc for 1st parm, X for second parm and Y for third parm, meaning that we stick to a well defined calling convention, this will be alright to make a stub for calls from high levels languages

Kroc commented 5 years ago

This is the code needed to read inline arguments and patch the stack:

some_func:
    ; this must be a sub-routine because there is
    ; not enough room in ROM to repeat this code
    jsr get_inline_args

    lda ($02), y            ; first argument...
    iny
    lda ($02), y            ; second argument...
    iny
    lda ($02), y            ; third argument, etc

    ; patch the stack to return to the location after
    ; the inline-arguments; this must be a sub-routine
    ; due to lack of space in the ROM
    ;
    ; JMP must be used to return otherwise the wrong
    ; return address will be patched
    jmp fix_stack_rts

get_inline_args:
    ; put aside the carry flag for later; this will cause
    ; the stack to look like this:
    ;       
    ; stack pointer ->  (unused stack value)
    ;               +1  processor status
    ;               +2  this routine's address, lo-byte
    ;               +3  this routine's address, hi-byte
    ;               +4  original call address, lo-byte
    ;               +5  original call address, hi-byte
    php

    tsx                     ; get current stack pointer
    clc                     ; we need to read the caller's address:
    lda $0100+4, x          ; read the lo-byte from the stack
    adc # 1                 ; add 1 to get to move into the inline arguments
    sta $02
    lda $0100+5, x
    adc # 0
    sta $03

    ldy # 0                 ; start with first inline argument
    plp                     ; retrieve the original carry flag
    rts

fix_stack_rts:
    ; we will need to add the length of the inline arguments to the call
    ; address so as to 'step-over' those bytes and return execution to
    ; the point after the inline arguments
    ;
    tsx                     ; retrieve current stack position
    iny
    tya
    adc $0100+1, x
    sta $0100+1, x
    bcc @x
    inc $0100+2, x

    rts

You are significantly under-valuing the cost here and need to present valid technical demonstrations why this is better than using jump-tables.

kktos commented 5 years ago

Oops, bit of a misunderstanding here. sorry about that. you were saying : <<The X16 aims to follow the Commodore 64 style; jump-tables is that style. Having a mixture of two styles increases the burden on documentation and difficulty learning the system>> the part I disagree with was only : <> But as it is a matter of appreciation, I'll give you the point :)

kktos commented 5 years ago

Kroc, you got me curious about the code you posted so I sum the cycles to compute the cost.

some_func:
    jsr get_inline_args

    lda ($02), y            ; first argument...
    iny
    lda ($02), y            ; second argument...
    iny
    lda ($02), y            ; third argument, etc

    jmp fix_stack_rts

get_inline_args:
    php              ; 4 

    tsx                             ; 2
    clc                             ; 2
    lda $0100+4, x          ; 4
    adc # 1                      ; 2
    sta $02           ; 3
    lda $0100+5, x        ; 4
    adc # 0           ; 2
    sta $03           ; 3

    ldy # 0                       ; 2
    plp                            ; 4
    rts              ; 6
                     ; 38 cycles

fix_stack_rts:
    tsx                             ; 2
    iny               ; 2
    tya               ; 2
    adc $0100+1, x       ; 4
    sta $0100+1, x       ; 4
    bcc @x           ; 2/4
    inc $0100+2, x       ; 7
    rts              ; 6
                      ; 29/31 cycles

So it around 69 cycles added to each function.

And chasing for cycles, I managed to gain some (14) :) Instead of inc the address, I simply start at offset 1

get_inline_args:
    tsx                             ; 2
    lda $0100+4, x          ; 4
    sta $02          ; 3
    lda $0100+5, x       ; 4
    sta $03              ; 3

    ldy # 1                      ; 2
    rts              ; 6
                                     ; 24 cycles

So it around 55 cycles added to each function. At 1MHz, that's an heavy cost. At 8MHz, it seems pretty ok to me.

EgonOlsen71 commented 5 years ago

Apart from all of that, this is actual list of calls that I'm using right now (excluding the ones that go into the already existing jump table):

SGNFAC = $BC2B
MEMARG = $BA8C
ARGADD = $B86A
ARGAND = $AFE9
ARGDIV = $BB14
FACMUL = $BA30
MEMMUL = $BA28
FACADD = $B867
FACLOG = $B9EA
FACSQR = $BF71
FACEXP = $BFED
FACABS = $BC58
FACSIN = $E26B
FACCOS = $E264
FACTAN = $E2B4
FACATN = $E30E
FACSGN = $BC39
FACNOT = $AED4
FACRND = $E097
FACWORD = $B7F7
FACDIV = $BB0F
BASINT = $BCCC
FACPOW = $BF7B
FACSUB = $B853
FACOR = $AFE6
FACMEM = $BBD7
ARGFAC = $BBFC
FACARG = $BC0C
FACSTR = $BDDF
FACINT = $B1AA
RNDFAC = $BC1B
REALFAC = $BBA2
INTFAC = $B391
WRITETIS = $A9E7
GETTI = $BE68
GETTIME = $AF7E
COPYTIME = $AF87
TI2FAC = $AF84
PRINTSTRS = $AB25
VALS = $B7B5
CMPFAC = $BC5B
BYTEFAC = $B3A2
CRSRRIGHT = $AB3B
INPUT = $A560
OPENCH = $F34A
CLOSECH = $F291
LOADXX = $F49E
SAVEXX = $F5ED
TWAIT = $F6ED
ERRALL = $A437
ERRIQ = $B248
ERREI = $ACF4
ERRSYN = $AF08
ERRFNF = $F12F
kktos commented 5 years ago

@mist64 As now we will have to jsfar for the call we want to make to the toolbox and let's say, you want to change bank for parts of the toolbox ( kernal from 7 to 0 ;) -- yes, saw your branch ), it seems my initial point is having a far better weight. That is, instead of doing in our code a jsrfar followed by a 24bits address, address that can change over the time depending of the changes you want to make on the toolbox, why not having a jsrfarToolbox followed by a 16bits code/index/number which is an index in an internal table of toolbox calls (bank:address) ? (oh and, we can have both, of course)

mist64 commented 5 years ago

@kktos You don't need a jsrfar in most cases today, because e.g. BASIC has a compatible jump table at the correct spot which will do the jsrfar for you, and abstract what bank the target code lives on.

Kroc commented 5 years ago

Could we please have a public KERNAL 'string-print' function, so that users don't have to write manual jsr $ffd2 loops? A fast-path could optimise printing a null-terminated string to the display.

mist64 commented 5 years ago

The suggested way is the KERNAL call PRIMM.

LRFLEW commented 5 years ago

Since I accidentally duplicated this issue, I wanted to chime in, share my thoughts, and ask about some of this I haven't been able to figure out.

I tried to map some of what I saw as function addresses in this issue to the rom.txt file I compiled myself, and I can't figure it out. I can't find anything called FACADD in rom.txt, but I did find something called .fadd at 00D99C. Is this the same thing? And what's the meaning of the values in rom.txt? They don't seem to match up with offsets in rom.bin to me (mostly because they never go above 0xFFFF). I am having the same issue with trying to parse rom.cfg (which I think is where the addresses are coming from). I was thinking about experimenting with calling the functions directly to get started, but I'm getting confused by this.

I also noticed that recently the documentation about how the ROM banking was changed. Instead of 8 8K banked ROM and 8K fixed ROM, it's now 8 16K banked ROM and no fixed ROM. This would mean that the Kernal API would be affected by this change. Is the plan to copy the Kernal API to all the ROM banks, or just keep them in the Kernal bank? If the latter, then I think that might be an opportunity for how to handle adding jump tables for BASIC's (and other banks') functions. We could have each ROM bank have its own "public API" exposed as a jump table in $FF00-$FFFE of that bank. I noticed that the Kernal API already has a JSRFAR function for calling code in another ROM bank, but maybe we could add a JSRFARIND (JSR Far Indirect) to make using jump tables in other ROM banks easier as well.

EgonOlsen71 commented 5 years ago

The names in this list are just a straight copy of the names in my compiler's runtime. Mostly, they came from old books and newer online resources. Because there's no official, documented source code, everybody could name them anyway they liked (and so did I in some places). For my mapping, I don't care about these names at all. They could be named HENRY(1..n) and it would still work. I'm only caring about the addresses. The address of each function in that list is the one used for calling it in the C64's ROM. Armed with that information, I parse the rom.txt for the C64 ROM, look up the label that it has there at the address in question and search the same label in the X16's sym files. The only exceptions are calls that jump directly into the ROM (i.e. there's no label at that address). In that case, I'm calculating the offset to the closest preceding label in the C64's ROM, search for that label in the X16's ROM and apply the same offset to it. So far, this works fine. You have to do some special handling for the newly introduced ROM banks, but that's actually quite easy as well once you know how. Each call to an address >=$E500 in the C64 ROM goes into the X16's kernal, each call to an address below goes into the BASIC ROM.

Kroc commented 5 years ago

The suggested way is the KERNAL call PRIMM.

Ah, that's a C128 addition, which was why I wasn't aware of it! NB: there's code here to improve the speed of the original function, including 65c02 specifically: http://www.6502.org/source/io/primm.htm

mist64 commented 5 years ago

They don’t work in ROM, but maybe adapting them to ROM might still be faster than what we have now. I’m open to pill requests! :-)

BruceMcF commented 4 years ago

The benefit of the function call over the jump table is space when there is a large number of calls ... the jump table needs 3 and the function call can be implemented with a vector table that needs 2 per routine.

But there is (1) the space overhead of DOING the call and (2) the fact that the most efficient way to pass parameters is via registers, and so the function call will be USING a register to hold the function number which would be more efficient to use for passing a parameter TO the function.

The solution which gets even more space efficiency in the ROM (but at a cost, of course) while retaining the compatibility between revisions of the Basic ROM is to publish a VECTOR TABLE for routines made available. Then the code that uses the Basic routines includes it's own jump table of indirect jumps to the Basic ROM routines that it uses ... then in the main program logic, JSR to the jump table and the return comes back to the calling code.

So rather than a Call to a Direct Jump in Basic ROM, you do a Call to an Indirect Jump in your own code, so the net cost is 2 cycles (in the simple case of code in RAM), but the Basic ROM only pays 2 bytes per exported routine compared to 3 bytes with a jump table. Looking at the list of routines in the discussion above, that would be a noticeable difference in jump table space overhead.

This wouldn't be used in the Kernel. The Kernel jump table would expanded as more Jump Table entries. This would be for the more specialized task of exporting Basic routines for programs that may benefit from having stable access points for them for different revisions of ROM Basic.

mist64 commented 4 years ago

For reference, these are the names the source uses for the requested symbols, together with their value on the C64:

$A437 error
$A560 inlin
$AB3B outspc
$AED4 notop
$AF08 snerr
$AF84 gettim
$AFE6 orop
$AFE9 andop
$B1AA flpint
$B248 fcerr
$B391 givayf
$B3A2 sngflt
$B7F7 getadr
$B853 fsubt
$B867 fadd
$B86A faddt
$B9EA log
$BA28 fmult
$BA8C conupk
$BB0F fdiv
$BBA2 movfm
$BBFC movfa
$BC0C movaf
$BC1B round
$BC2B sign
$BC39 sgn
$BC58 abs
$BC5B fcomp
$BCCC int
$BDDF foutc
$BE68 foutim
$BF71 sqr
$BF7B fpwrt
$BFED exp
$E097 rnd
$E264 cos
$E26B sin
$E2B4 tan
$E30E atn
$F12F msg
$F291 nclose
$F34A nopen
$F49E loadsp
$F5ED nsave
$F6ED nstop
mist64 commented 4 years ago

In 3520cab, I have separated the floating point library into its own component with its own jump table at $FC00 on the BASIC bank. This is the current jump table. It should remain fairly stable, but no guarantees yet.

ayint   = $fc00 ; facmo+1:facmo = (s16)FAC
givayf2 = $fc03 ; FAC = (s16).A:.Y
getadr2 = $fc06 ; .A:.Y = (u16)FAC
faddh   = $fc09 ; FAC += .5
fsub    = $fc0c ; FAC -= mem(.Y:.A)
fsubt   = $fc0f ; FAC -= ARG
fadd    = $fc12 ; FAC += mem(.Y/.A)
faddt2  = $fc15 ; FAC += ARG
faddt   = $fc18 ; BASIC ONLY, DO NOT USE
zerofc  = $fc1b ; FAC = 0
normal  = $fc1e ; Normalize FAC
negfac  = $fc21 ; FAC = -FAC
log = $fc24 ; FAC = log(FAC)
fmult   = $fc27 ; FAC *= mem(.Y:.A)
fmultt2 = $fc2a ; FAC *= ARG
fmultt  = $fc2d ; BASIC ONLY, DO NOT USE
mltply  = $fc30 ; FAC += .A * ARG
conupk  = $fc33 ; ARG = mem(.Y:.A) (5 bytes)
mul10   = $fc36 ; FAC *= 10
finml6  = $fc39 ; FAC = 2 * (FAC + ARG)
div10   = $fc3c ; FAC /= 10
fdiv    = $fc3f ; FAC = mem(.Y:.A) / FAC
fdivt2  = $fc42 ; FAC /= ARG
fdivt   = $fc45 ; BASIC ONLY, DO NOT USE
movfm   = $fc48 ; FAC = mem(.Y:.A) (5 bytes)
movmf   = $fc4b ; mem(.Y:.X) = round(FAC) (5 bytes)
movfa   = $fc4e ; FAC = ARG
movaf   = $fc51 ; ARG = round(FAC)
movef   = $fc54 ; ARG = FAC
round   = $fc57 ; Round FAC using rounding byte
sign    = $fc5a ; .A = sgn(FAC)
sgn = $fc5d ; FAC = sgn(FAC)
float   = $fc60 ; FAC = (u8).A
floats  = $fc63 ; FAC = (s16)facho+1:facho
floatc  = $fc66 ; BASIC ONLY, DO NOT USE
floatb  = $fc69 ; BASIC ONLY, DO NOT USE
abs = $fc6c ; FAC = abs(FAC)
fcomp   = $fc6f ; .A = FAC == mem(.Y:.A)
fcompn  = $fc72 ; BASIC ONLY, DO NOT USE
qint    = $fc75 ; facho:facho+1:facho+2:facho+2 = u32(FAC)
int = $fc78 ; FAC = int(FAC)
;fin    = $fc7b ; XXX TODO
finlog  = $fc7e ; FAC += (s8).A
fout    = $fc81 ; Convert FAC to ASCIIZ string at fbuffr
foutc   = $fc84 ; Convert FAC to ASCIIZ string at fbuffr - 1 + .Y
foutim  = $fc87 ; convert TI to TI$
sqr = $fc8a ; FAC = sqr(FAC)
fpwrt2  = $fc8d ; FAC = ARG^FAC
fpwrt   = $fc90 ; BASIC ONLY, DO NOT USE
negop   = $fc93 ; FAC = -FAC - 1
exp = $fc96 ; FAC = e^FAC
polyx   = $fc99 ; Polynomial Evaluation 1 (SIN/COS/ATN/LOG)
poly    = $fc9c ; Polynomial Evaluation 2 (EXP)
rnd2    = $fc9f ; FAC = rnd(A)
rnd = $fca2 ; FAC = rnd(FAC)
cos = $fca5 ; FAC = cos(FAC)
sin = $fca8 ; FAC = sin(FAC)
tan = $fcab ; FAC = tan(FAC)
atn = $fcae ; FAC = atn(FAC)

As a side effect, the zero page and vars layout has changed again.

This should give @EgonOlsen71 most symbols necessary for the compiler.

mist64 commented 2 years ago

Let me summarize my current thoughts.

First, if the function is only a convenience function, i.e. a function that does not use any internal state or access hardware directly, we should only expose it if it's non-trivial and super useful. The KERNAL exports PRIMM, memory copy and LZSA decompression, for example.

Second, these are the classes of calls that I can think of:

  1. the Math library: This is exposed with an API now.
  2. BASIC functions: The main user would be a BASIC extension (think: Simons' BASIC), which would need to have access to the CHRGET, CHRGOT, GETBYT etc. symbols. For the use case of a BASIC compiler that wants to reuse BASIC's internal functionality, I'd say: Better ship the compiled program with a runtime based on the BASIC source.
  3. KERNAL functions: I'm very open to this, but again, it'll have to access hardware directly, access some internal state, or be a non-trivial and super useful helper.

What functions are we thinking about?