How to call Applesoft subroutines from assembly language subroutines
How to define Applesoft procedural functions and call them from Applesoft
I hereby take for granted that every reader of this article knows how to
call assembly language routines from Applesoft programs (this has been
described in numerous reference manuals, articles and books, cf. keywords
USR, CALL and & in such literature), or at least has heard about that
feature.
Hereby I propose a mechanism leading to the Applesoft side of an application
being called upon by the machine code side of this application (that is the
reverse path of the previous one and much, much less common AFAIK), thus by
using what I'm naming "Ex nihilo GOSUB stack frames" in this document.
"Ex Nihilo GOSUB stack frames" have been around (at least in my mind) since
I implemented co-routines as part of an Applesoft extension utility some
months/years ago.
Every subsequent version of this utility (name is Peersoft) has found a
creative use of this small hack, the most recent use being the support for
hardware interrupt servicing (mouse card based for the time being) by
Applesoft subroutines (to be released by the end of this week or next).
In this way,
actual mixed arithmetic (i.e. with both integer and FP) can be performed ;
assembly language routines can be customized using scripts of an
interpretive language like BASIC and easily tailored to specific contexts
on the field and without any reassembly or recompile.
Well, this article is to be considered as a demo of the potential of such
mechanism: I'll show how to call Applesoft subroutines from assembly
language programs, themselves could have been called from within an
Applesoft application programs (we'll stop here the "russian puppets"
game). Two mechanisms are illustrated here:
simple generic call from assembly to Applesoft and return;
Procedural functions (or PF for short): call from assembly as part of the
processing of a function definition written in Applesoft. In the codlet
above, line 20 tells that for every function call:
The actual argument value (at time of call) will be passed in the N named FP
variable;
The returned result will be extracted from the S FP variable value
The function body begins at line 2000. The unusual aspect is that it is
Applesoft code and not ML despite the fact that, from an end user/programmer
point of view, the interface be identical to a ML user defined function.
One thing that is missing from the provided code here is the backup of the
original value of variables N and S here and adhoc restore operation
afterward. By the way the N and S variables could be either integer or FP
but not of string type.
1 PRINT CHR$(4)”MAXFIELS 1”: HIMEM: 38400:BA = 38400: PRINT CHR$
(4)”BLOAD JAH2.0”
9 REM EARLY TESTS
10 CALL BA,1000:N = 4: CALL BA,2000: PRINT S
19 REM PF PREPARATION STEP
20 CALL BA + 3,N,S,2000: POKE 10,76: POKE 11,6: POKE 12,150
29 REM PF EXECUTE STEP
30 FOR J = 1 TO 8: PRINT J” ” USR (J): NEXT: END
1000 PRINT “HELLO”: RETURN
1997 REM THE LINE BELOW IS THE
1998 REM BODY OF THE PROCEDRAL
1999 REM FUNCTION S=F(N)
2000 S = 0: FOR I = 0 TO N:S = S + I : NEXT : RETURN
]RUN
HELLO
10
1 1
2 3
3 6
4 10
5 15
6 21
7 28
8 36
And now the Assembly code source file content. For the folks who are too
lazy to type in the source code and assemble it, I'll put a disk image on my
Web site in the days to come and advise within this thread once uploaded.
* 22àAnnières (for french speaking countries) or
* JAH (just a hack) (for the rest of the world)
* just get a look @
https://www.youtube.com/watch?v=nkSYLi2N9zs
* to get the point.. (french language understanding required).
* Usage: CALL 38400,1000: REM will call Applesoft subroutine at line 1000
* from assembly
and return.
* CALL 38403,<argVName>,<resVName>,<BodyStartLine#> for the PF prepare
* and PRINT USR (<expression>) for the PF execute.
* for more elaborate usage (procedural functions)
* Published under the Creative commons license
* Benoit Gilon
* Merlin assembler
TOKGOSUB = $B0
* Page zero equates
REMSTK EQU $F8
TXTPTR EQU $B8
OLDTEXT EQU $79
TRCFLG EQU $F2
CURLIN EQU $75
LOWTR EQU $9B
VALTYP EQU $11
FORPNT EQU $85
ARYTAB EQU $6B
VARPNT EQU $83
* Some helpful ROM routines from Applesoft
NEWSTT EQU $D7D2
PTRGET EQU $DFE3
CHKCOM EQU $DEBE
LINGET EQU $DA0C
FNDLIN EQU $D61A
UNDERR EQU $D97C
CHKNUM EQU $DD6A
MOVMF EQU $EB2B
MOVFM EQU $EAF9
UNDERR EQU $D97C
TMERR EQU $DD76
MOVFM EQU $EAF9
GIVAYF EQU $E2F2
LET2 EQU $DA63
DO 0
STD MAC
LDA ]1
STA ]2
LDA ]1+1
STA ]2+1
<<<
FIN
XC ;Enable 65C02 opcodes
ORG $9600
CLC
BCC GENFEAT Always
JMP PREPARE
* Here the entry point for procedural functions
FUNC JSR CHKNUM
STD ARVPTR;FORPNT
BIT ARINTYP bit N set iif integer var.
JSR LET2
STD LINPTR;LOWTR
SEC ;Flag that we come from procedural functions
GENFEAT ROR FMODE
BMI :0 COMMON1 already called as part of the PREPARE step
JSR COMMON1
0 LDY #8-1
]LOOP LDX SVOFST,Y
LDA 0,X
STA SVAREA,Y
DEY
BPL ]LOOP
STZ TRCFLG
TSX
STX SAVSTP
* This is the critical code segment
LDA #>RETOUR-1
PHA
LDA #RETOUR-1
PHA
LDA TXTPTR+1
PHA
LDA TXTPTR
PHA
LDA CURLIN+1
PHA
LDA CURLIN
PHA
LDA #TOKGOSUB
PHA
* Let (TXTPTR) set to (LOWTR) minus 1
LDX LOWTR+1
LDY LOWTR
BNE :0
DEX
:0 DEY
STX TXTPTR+1
STY TXTPTR
JMP NEWSTT
* Procedural functions: prepare step
PREPARE LDA #0 Handle arg variable
JSR COMMON2
LDA #3 Handle Result variable
JSR COMMON2
JSR COMMON1 Handle PF start line #
STD LOWTR;LINPTR
RETOUR LDY #8-1
]LOOP LDX SVOFST,Y
LDA SVAREA,Y
STA 0,X
DEY
BPL ]LOOP
BIT FMODE
BMI :0
LDA #RESVPTR
LDY #>RESVPTR
JMP MOVFM
:0 RTS
COMMON1 JSR CHKCOM
JSR LINGET
JSR FNDLIN
BCS :0
JMP UNDERR
:0 RTS
COMMON2 PHA
JSR CHKCOM
JSR PTRGET
BIT VALTYP
BPL :1
]ERR JMP TMERR
:1 CMP ARYTAB
TYA
SBC ARYTAB+1
BCS ]ERR
PLX
TYA
STA SVVAR+2,X
LDA VARPNT
STA SVVAR+1,X
LDA VALTYP+1
STA SVVAR+0,X
RTS
SVVAR EQU *
ARINTYP DS 1
ARVPTR DS 2
RSINTYP DS 1
RSVPTR DS 2
FMODE DS 1
RESVPTR DS 2
SAVSTP DS 1
LINPTR DS 2
SVAREA DS 8
SVOFST DFB REMSTK, TRCFLG
DFB TXTPTR, TXTPTR+1
DFB CURLIN, CURLIN+1
DFB OLDTEXT, OLDTEXT+1
END
In the same vein, why not think about a similar hack for calling Integer
BASIC routines from Applesoft?.. Humm.. that will extend the 2015 todo list
by 1 item...
In the meantime I wish all an happy PI day (I'll be not late on this one at
least).
Benoît
--
Growing old is mandatory..
growing up is optional..
But the other way round is as true.