QPC2 MultiBASIC: Load SBASIC machine code function resident

Anything QL Software or Programming Related.
User avatar
ql_freak
Super Gold Card
Posts: 581
Joined: Sun Jan 18, 2015 1:29 am

QPC2 MultiBASIC: Load SBASIC machine code function resident

Post by ql_freak »

I have now written a strlen%() function which returns the length of a string without using CA.GTSTRING(!). Works on QPC2 and sQLux but only with string literals, expressions and variables not with string array variables (IMHO a strange behaviour).

Problem: When loading with LRESPR in a MultiBASIC I can call it with "PRINT STRLEN%('123')" and the output is 3. But if I enter NEW the STRLEN% function isn't available anymore.

Is this normal?

Is it possible to load SBASIC extensions resident in a MultiBASIC job (without loading it in job 0,0)?


http://peter-sulzer.bplaced.net
GERMAN! QL-Download page also available in English: GETLINE$() function, UNIX-like "ls" command, improved DIY-Toolkit function EDLINE$ - All with source. AND a good Python 3 Tutorial (German) for Win/UNIX :-)
User avatar
janbredenbeek
Super Gold Card
Posts: 711
Joined: Wed Jan 21, 2015 4:54 pm
Location: Hilversum, The Netherlands
Contact:

Re: QPC2 MultiBASIC: Load SBASIC machine code function resident

Post by janbredenbeek »

ql_freak wrote: Sat Aug 16, 2025 9:27 pm Is it possible to load SBASIC extensions resident in a MultiBASIC job (without loading it in job 0,0)?
Should be possible, as long as they contain only SB extensions (not device drivers etc). They should survive a NEW, but disappear when you kill the MB job.


User avatar
ql_freak
Super Gold Card
Posts: 581
Joined: Sun Jan 18, 2015 1:29 am

Re: QPC2 MultiBASIC: Load SBASIC machine code function resident

Post by ql_freak »

Hi Jan,

unfortunately here not :-( I LRESPR it and after a NEW it is gone. Have not tested it on Minerva's (UQLX or sQlux) MultiBASICs, but as far as I remember extensions loaded with LRERPR are resident in a Minerva MultiBASIC job.

Here the source:

Code: Select all

; Simplified test example to get the first parameter of a SB function
; (which must be a string) without CA.GTSTR and returns length of string

BP_INIT     equ     $110    ;Vector BP.INIT to define MC PROC/FNs
BV_VVBAS    equ     $28     ;SB variables value area
BV_CHRIX    equ     $11a    ;Vector BV.CHRIX
BV_RIP      equ     $58     ;BASIC storage for RI stack

NTVVAPTR    equ     4       ;offset of ptr to value in VVA in nametable

QSTR        equ     1       ;QDOS STRing
QINT        equ     3       ;QDOS INteger (WORD, i.e. short in C!)
SVBITCLR    equ     $DFFF   ;andi.w SVBITCLR,sr switches to User mode

            bra     addSBext

strlen      move.l  NTVVAPTR(a6,a3.l),d7 ;d7 should now point to the string
                                    ;parameter in the variables value area, i.e.
                                    ;d7(a6,BV_VVBAS.l) - which is not possible
                                    ;in 68K assmbler(!) - should point to the
                                    ;value of the string parameter
;So we must switch into Supervisor mode to calculate a (relative to a6) pointer
;to the value:

            trap    #0              ;switch to Supervisor mode
            move.l  BV_VVBAS(a6),a3 ;0(a6,a3.l) should now point to BV.VVBAS
            adda.l  d7,a3           ;0(a6,a3.l) should now point to the passed
                                    ;string parameter (in the VVA)
            andi.w  #SVBITCLR,sr    ;switch back to User mode

            move.w  0(a6,a3.l),d7   ;d7 should now hold string length

;Following code must be adjusted, copied from reflection.asm:
        move.w  BV_CHRIX,a0     ;We need space on RI stack
        moveq   #2,d0           ;2 bytes required for string length
        move.l  BV_RIP(a6),a1   ;Per says: Only QDOS needs this move
        jsr     (a0)

;Per says we must correct the stack pointer with:
        subq.l  #2,BV_RIP(a6)   ;this is where the real stack top is so
                                ;here's where we need to take the space

        move.l  BV_RIP(a6),a1   ;Restore RI stack pointer
        move.w  d7,0(a6,a1.l)   ;Push line no. (short int) ...

        ;subq.l  #2,a1           ;... on RI stack AND (don't forget!):
        moveq   #QINT,d4        ;... set return type (integer [short])
        moveq   #0,d0           ;No error
        rts                     ;Return to SuperBASIC

addSBext    lea     SBext,a1        ;Load Effective Adress of SB PROC/FNs
            move.w  BP_INIT,a2      ;We must call SB vector BP.INIT
            jmp     (a2)            ;DO IT!


SBext       dc.w 0,0                ;No PROCs, END of PROCedure definitions
            dc.w 1                  ;1 FuNction
            dc.w strlen-*
            dc.b 7,'STRLEN%'
            dc.w 0
Ciao Peter


http://peter-sulzer.bplaced.net
GERMAN! QL-Download page also available in English: GETLINE$() function, UNIX-like "ls" command, improved DIY-Toolkit function EDLINE$ - All with source. AND a good Python 3 Tutorial (German) for Win/UNIX :-)
User avatar
janbredenbeek
Super Gold Card
Posts: 711
Joined: Wed Jan 21, 2015 4:54 pm
Location: Hilversum, The Netherlands
Contact:

Re: QPC2 MultiBASIC: Load SBASIC machine code function resident

Post by janbredenbeek »

I see some issues in your code:
  • No check if the parameter is a string, or if there's even a parameter at all
  • The parameter to bv.chrix should be in d1, not d0. You're now requesting a random value, which might be negative and corrupt the system.
Also, it's not necessary to enter supervisor mode since the addresses are al relative to A6 and won't change in the same SB job.

Code: Select all

	move.l	4(a6,a3.l),d7	; offset to VVBAS of value
	add.l	bv_vvbas(a6),d7	; add VVBAS, address is still relative to a6!
	move.w	(a6,d7.l),d7	; now d7 holds length
I could not reproduce your symptom with NEW, a function added with lrespr remained in MB even after a NEW. Maybe it's because your wrong call to bv.chrix corrupted the system?


User avatar
ql_freak
Super Gold Card
Posts: 581
Joined: Sun Jan 18, 2015 1:29 am

Re: QPC2 MultiBASIC: Load SBASIC machine code function resident

Post by ql_freak »

janbredenbeek wrote: Sun Aug 17, 2025 11:55 am I see some issues in your code:
  • No check if the parameter is a string, or if there's even a parameter at all
I know, but this is just a function I have written (as simple as possible) to perhaps ask here, why I don't get the pointer to the string parameter (I had a lot of problem with that and didn't know if I will succeed) and it's primarily a benchmark, to test if fetching the parameter without CA.GETSTR will be faster (which it seems not).
  • The parameter to bv.chrix should be in d1, not d0. You're now requesting a random value, which might be negative and corrupt the system.
Oops, thank you.
Also, it's not necessary to enter supervisor mode since the addresses are al relative to A6 and won't change in the same SB job.

Code: Select all

	move.l	4(a6,a3.l),d7	; offset to VVBAS of value
	add.l	bv_vvbas(a6),d7	; add VVBAS, address is still relative to a6!
	move.w	(a6,d7.l),d7	; now d7 holds length
Thank you, I wasn't sure. In this case I will correct the code and run the benchmark (see here) again.
I could not reproduce your symptom with NEW, a function added with lrespr remained in MB even after a NEW. Maybe it's because your wrong call to bv.chrix corrupted the system?
Yes, this will most probably be the problem as I think I have already loaded SB extensions in QPC2s MultiBASICs which survived NEW.

EDIT (addendum):
I have now corrected strlen_asm and also the benchmark program (see here). Albeit the switch to supervisor mode has been removed, the result is the same. The version with LEN is faster than the one with STRLEN% (which does NOT use CA.GTSTR to fetch the string parameter).

Unfortunately albeit now using D1 for BV.CHRIX (see Jans corrections) if LRESPRed in a QPC2 MultiBASIC, the extension is gone after a NEW (LOAD) here. Jan does not have this problem. Here the corrected assembler code:

Code: Select all

;Simplified test example to get the first parameter of a SB function
;(which must be a string) without CA.GTSTR and returns length of string.
;NOTE: This is just for a benchmark, to test if fetching a string parameter
;      without using CA.GTSTRING is faster. Therefore absolutely no
;      parameter checking. Do NOT use this function in production code!
;NOTE 2: No '.' used for e.g. BV.VVBAS but underscore, as not all assemblers
;        support '.' in label names.

BP_INIT     equ     $110    ;Vector BP.INIT to define MC PROC/FNs
BV_VVBAS    equ     $28     ;SB variables value area
BV_CHRIX    equ     $11a    ;Vector BV.CHRIX
BV_RIP      equ     $58     ;BASIC storage for RI stack

NTVVAPTR    equ     4       ;offset of ptr to value in VVA in nametable

QSTR        equ     1       ;QDOS STRing
QINT        equ     3       ;QDOS INteger (WORD, i.e. short in C!)
SVBITCLR    equ     $DFFF   ;andi.w SVBITCLR,sr switches to User mode

            bra     addSBext

strlen      move.l  NTVVAPTR(a6,a3.l),d7 ;d7 should now point to the string
                                    ;parameter in the variables value area, i.e.
                                    ;d7(a6,BV_VVBAS.l) - which is not possible
                                    ;in 68K assmbler(!) - should point to the
                                    ;value of the string parameter

;Jan Breedenbeck says it's NOT necessary to switch to SuperVisor mode:
;So we must switch into Supervisor mode to calculate a (relative to a6) pointer
;to the value:

;            trap    #0              ;switch to Supervisor mode
            move.l  BV_VVBAS(a6),a3 ;0(a6,a3.l) should now point to BV.VVBAS
            adda.l  d7,a3           ;0(a6,a3.l) should now point to the passed
                                    ;string parameter (in the VVA)
;            andi.w  #SVBITCLR,sr    ;switch back to User mode

            move.w  0(a6,a3.l),d7   ;d7 should now hold string length

            move.w  BV_CHRIX,a0     ;We need space on RI stack
            moveq   #2,d1           ;2 bytes required for string length
            move.l  BV_RIP(a6),a1   ;Per says: Only QDOS needs this move
            jsr     (a0)

;Per says we must correct the stack pointer with:
            subq.l  #2,BV_RIP(a6)   ;this is where the real stack top is so
                                    ;here's where we need to take the space

            move.l  BV_RIP(a6),a1   ;Restore RI stack pointer
            move.w  d7,0(a6,a1.l)   ;Push line no. (short int) ...

            ;subq.l  #2,a1           ;... on RI stack AND (don't forget!):
            moveq   #QINT,d4        ;... set return type (integer [short])
            moveq   #0,d0           ;No error
            rts                     ;Return to SuperBASIC

addSBext    lea     SBext,a1        ;Load Effective Adress of SB PROC/FNs
            move.w  BP_INIT,a2      ;We must call SB vector BP.INIT
            jmp     (a2)            ;DO IT!


SBext       dc.w 0,0                ;No PROCs, END of PROCedure definitions
            dc.w 1                  ;1 FuNction
            dc.w strlen-*
            dc.b 7,'STRLEN%'
            dc.w 0

;END OF FILE


http://peter-sulzer.bplaced.net
GERMAN! QL-Download page also available in English: GETLINE$() function, UNIX-like "ls" command, improved DIY-Toolkit function EDLINE$ - All with source. AND a good Python 3 Tutorial (German) for Win/UNIX :-)
User avatar
tofro
Font of All Knowledge
Posts: 3188
Joined: Sun Feb 13, 2011 10:53 pm
Location: SW Germany

Re: QPC2 MultiBASIC: Load SBASIC machine code function resident

Post by tofro »

I'm confused:
Why are you calling an (apparently) SBASIC job a "Mutibasic job"? It isn't.


ʎɐqǝ ɯoɹɟ ǝq oʇ ƃuᴉoƃ ʇou sᴉ pɹɐoqʎǝʞ ʇxǝu ʎɯ 'ɹɐǝp ɥO
User avatar
janbredenbeek
Super Gold Card
Posts: 711
Joined: Wed Jan 21, 2015 4:54 pm
Location: Hilversum, The Netherlands
Contact:

Re: QPC2 MultiBASIC: Load SBASIC machine code function resident

Post by janbredenbeek »

tofro wrote: Mon Aug 18, 2025 12:32 pm I'm confused:
Why are you calling an (apparently) SBASIC job a "Mutibasic job"? It isn't.
Ah, that's the difference!
A NEW in Minerva's MB clears out the name table except for machine code procedures and functions already loaded (and inherited).

A NEW in SBASIC clears out the whole name table, and machine code procedures and functions get copied from the main SBASIC as you use them. But if you've loaded them locally, they will simply go away!

So ql_freak was in fact testing SBASIC...


User avatar
RalfR
QL Wafer Drive
Posts: 1248
Joined: Fri Jun 15, 2018 8:58 pm

Re: QPC2 MultiBASIC: Load SBASIC machine code function resident

Post by RalfR »

So an SBASIC daughter job is not the right way for testing. Never knew, that this happens. Any remarks for that in any documentation?


7000 4E75
User avatar
tofro
Font of All Knowledge
Posts: 3188
Joined: Sun Feb 13, 2011 10:53 pm
Location: SW Germany

Re: QPC2 MultiBASIC: Load SBASIC machine code function resident

Post by tofro »

RalfR wrote: Mon Aug 18, 2025 5:01 pm So an SBASIC daughter job is not the right way for testing. Never knew, that this happens. Any remarks for that in any documentation?
Early SMSQ/E manuals (the QXL manual, for example) were pretty clear about this
Bildschirmfoto 2025-08-18 um 18.47.52.png
This was just some random manual I had lying around. I'm sure that later manuals also point this out clearly.


ʎɐqǝ ɯoɹɟ ǝq oʇ ƃuᴉoƃ ʇou sᴉ pɹɐoqʎǝʞ ʇxǝu ʎɯ 'ɹɐǝp ɥO
User avatar
RalfR
QL Wafer Drive
Posts: 1248
Joined: Fri Jun 15, 2018 8:58 pm

Re: QPC2 MultiBASIC: Load SBASIC machine code function resident

Post by RalfR »

Yes, but "NEW" removing all in a daughter job is not stated!


7000 4E75
Post Reply