Modularity with plain SuperBasic

Anything QL Software or Programming Related.
Post Reply
User avatar
polka
Trump Card
Posts: 196
Joined: Mon Mar 07, 2011 11:43 am

Modularity with plain SuperBasic

Post by polka »

To complement this post viewtopic.php?t=4287&start=16, I decided to open this new thread to discuss some updates of my methods to program plain SuperBasic without line numbers. Basing the new developments on the same example of my modular generic BOOT with only these two small numbered files :

Code: Select all

DEV$="FLP1_" : MODE 4 : PROG_USE DEV$ : DATA_USE DEV$

32741 MRUN mdv1_TOOB

32742 DEFine FuNction aln(f$,f,d)
32743 input_file$  = dev$ & f$ & "_txt"
32744 output_file$ = dev$ & f$ & "_bas"
32745 OPEN_IN#5,input_file$:OPEN_OVER#6,output_file$
32746 l = f
32747 REPeat number
32748    INPUT#5,l$ : PRINT#6,IDEC$(l,5,0);" ";l$
32749    IF EOF(#5) THEN EXIT number : ELSE l=l+d
32750 END REPeat number
32751 CLOSE#6:CLOSE#5:RETurn l
32752 END DEFine aln

32753 DEFine FuNction mwoln(f$,ff,d)
32754 ll=aln(f$,ff,d): ff$ = dev$ & f$ & "_bas"
32755 MERGE ff$ : RETurn ll
32756 END DEFine mwoln

32757 DEFine PROCedure BYE
32758 PAPER#3,0:INK#3,4:CSIZE#3,0,0:CLS#3:AT#3,0,7
32759 PRINT#3,' F1',,'F2',,'F3',,'F4',,'F5':RESTORE OK
32760 FOR i = 5 TO 69 STEP 16
32761    READ p,q,p$,Q$:PAPER#3,p:INK#3,q
32762    AT#3,1,i:PRINT#3,p$:AT#3,2,i:PRINT#3,Q$
32763 END FOR i
32764 p = CODE(INKEY$(-1))/4
32765 IF p<58 OR p>62 : GO TO 32764
32766 CLS#0:CLS:CLS#2:GO TO p+OK-53
32767 END DEFine BYE

PRINT aln("MANIFEST",1,1)
MRUN mdv1_MANIFEST_bas

Code: Select all

DLINE 1 to KO
BYE
The BOOT file has one PROcedure called BYE and one FuNtion called ALN (Add_Line_Numbers).
After loading these, BOOT will immediately execute two commands (statements without line numbers) : a call to ALN to add line numbers to a file called mdv1_MANIFEST_txt that will output a file called mdv1_MANIFEST_bas and MRUN (Merge and Run) it.

This is my first idea of the MANIFEST file mdv1_MANIFEST_txt :

Code: Select all

LRESPR 'mdv1_ptr_gen'
LRESPR 'mdv1_MacMouse11'
LRESPR 'mdv1_wman'
rem LRESPR 'mdv1_sys_hot_rext'
LRESPR 'mdv1_Qptr'
rem LRESPR 'mdv1_sys_Qpac2'
rem LRESPR 'mdv1_fileinfo2_bin'

window#0,512,256,0,0:paper#0,0;:ink#0,0;: cls#0
CLS#0:WINDOW#0,388,42,110,0:PAPER#0,2;:INK#0,7:BORDER#0,1,7
WINDOW#1,94,225,14,0:PAPER#1,0;:INK#1,7:BORDER#1,1,7
WINDOW#2,388,182,110,43:PAPER#2,0;:INK#2,4:BORDER#2,1,7
OPEN#3,'scr_512x31a0x225':PAPER#3,0;:INK#3,4

CFO=32601
R = ALN("CFORMS",CFO,1)
TMO = R+1
R = ALN("TOP_MENU",TMO,1)
PMO = R+1
R = aln("PSION_MENU",PMO,1)
LMO = R+1
R = ALN("LANG_MENU",LMO,1)
CMO = R+1
R = ALN("C68_MENU",CMO,1)

MERGE mdv1_C68_MENU_bas
MERGE mdv1_LANG_MENU_bas
MERGE mdv1_PSION_MENU_bas
MERGE mdv1_TOP_MENU_bas
MERGE mdv1_CFORMS_bas

INITFORM
OK = TMO : KO = CFO-1
The statements of MANIFEST (all with line numbers outside PROCedure or FuNction) will execute and add line numbers to five files : TOP_MENU PSION_MENU LANG_MENU C68_MENU and CFORMS, and after that, MERGE them to the BOOT program.

Finally, one numbered statement outside PROcedure or FuNction :

Code: Select all

32741 MRUN mdv1_TOOB
will be executed, deleting the MANIFEST statements no longer useful (In fact, this statement has been automatically relocated at line 32701 and statement 32741 was overwritten).


May the FORTH be with you !
POLKa
User avatar
polka
Trump Card
Posts: 196
Joined: Mon Mar 07, 2011 11:43 am

Re: Modularity with plain SuperBasic

Post by polka »

Indeed, it needs also the five following files, that it will merge with the BOOT program :

CFORMS_txt

Code: Select all

DATA 18,4,0,2,0,2,2,0,7,7,12
DATA 12,2,"3K"," -=3072",5,18,2
DATA 12,2,"5K"," -=5120",5,18,3
DATA 12,2,"7K"," -=7168",5,18,4
DATA 12,2,"9K"," -=9216",5,18,1
DATA 24,2,"VERBOSE"," -v",7,1,6
DATA 24,2,"LACONIC","",7,1,5
DATA 41,2,"ANSI"," -unproto",9,5,8
DATA 41,2,"K&R ","",9,5,7
DATA 53,2,"16 bits"," -Qshort",13,7,10
DATA 53,2,"32 bits","",13,7,9
DATA 30,7,"","   flp1_mydir_",12,14,4
DATA 30,8,"","   myprog_c",15,11,1
DATA 30,4,""," -Iflp1_myINCs_",14,9,2
DATA 30,5,""," -Lflp1_myLIBs_",11,13,3
DATA 10,12,"Floating point & Maths         flp1_LIB_LIBM_a"," -lm",16,12,15
DATA 10,13,"Dynamic allocations       flp1_LIB_LIBMALLOC_a"," -lmalloc",17,15,16
DATA 10,14,"debug support              flp1_LIB_LIBDEBUG_a"," -ldebug",18,16,17
DATA 10,15,"Semaphores and tasking       flp1_LIB_LIBSEM_a"," -lsem",1,17,18
DATA 0,1,0,0,1,0,0,1,0,1,1,1,1,1,0,0,0,0,11
DATA 1,1,0,4,"COMPILE AND LINK OPTIONS :"
DATA 4,2,0,4,"Stack :      Mode :           Norm :       Int :"
DATA 0,3,0,7,"----------------------------------------------------------------"
DATA 7,4,0,4,"My Include Directory :"
DATA 7,5,0,4,"My Library Directory :"
DATA 0,6,0,7,"----------------------------------------------------------------"
DATA 7,7,0,4,"My Project Directory :"
DATA 7,8,0,4,"My Current File Name :"
DATA 0,9,0,7,"----------------------------------------------------------------"
DATA 1,10,0,4,"Standard Libraries to scan :"
DATA 10,11,0,7,"Standard C LIBRARY             flp1_LIB_LIBC_a"
DEFine PROCedure INITFORM
o=32601:RESTORE o:READ m:DIM f(m):READ n:DIM r$(n,32)
DIM CC(7):FOR i=0 TO 7:READ CC(i):NEXT i:READ cp
RESTORE o+m+1:FOR i=1 TO m:READ f(i):NEXT i:READ MMM
FOR i=1 TO m:READFLD(o+i):IF m$="":r$(jt)=n$
END DEFine 
DEFine PROCedure EDITFORM
o=32601:CLS#2:RESTORE o+m+2:p$=""
FOR i=1 TO MMM:READ x,y,jp,jt,m$:PAPER#2,jp:INK#2,jt:AT#2,y,x:PRINT#2,m$
c=cp:READFLD(c+o)
c=NEXTFLD(c):IF c<>cp:GO TO 32642
REPeat ScanKeyb
a=CODE(INKEY$(-1))
SELect ON a
ON a=208:c=PREVFLD(c)
ON a=216:c=NEXTFLD(c)
ON a=32:c=TOGGLE(c)
ON a=10:DISPFLD c,0:EXIT ScanKeyb
ON a=27:C68:bye
END SELect 
END REPeat ScanKeyb
FOR i=1 TO m
READFLD(o+i):IF m$="":n$=r$(jt)
IF ((m$<>"") OR ((jt>1) AND (jt<n))) AND (f(i)=1):p$=p$&n$
END FOR i
dat$=r$(n,4 TO LEN(r$(n))): REM p$ = p$ & " -tmp" & dat$
END DEFine
DEFine FuNction CCC$(opt)
EDITFORM:INK#2,4:CLS#2:nnn="_c"INSTR r$(1)
IF nnn=0:nnn=len(r$(1))+1:r$(1)=r$(1)&"_c"
IF opt=0:p$="-c "& dat$ & r$(1,4 TO nnn+1)&" "&p$
IF opt=1:p$="-o"&dat$&r$(1,4 TO nnn-1)&" "&dat$&"*_o "&p$
EXEC_W CC,#2,#2,#2;p$
PRINT#0," Done... Hit any key !";:return inkey$(-1)
END DEFine 
DEFine FuNction TOGGLE(i)
IF m$="" THEN 
EDITFLD(jt)
ELSE 
IF jt=i :f(i)=1-f(i)
IF jt<>i:f(i)=0:i=jt:f(i)=1:READFLD(i+o)
END IF 
DISPFLD i,1:RETurn i
END DEFine 
DEFine FuNction PREVFLD(i)
DISPFLD i,0:i=jp:i=FINDFLD(i):DISPFLD i,1:RETurn i
END DEFine 
DEFine FuNction NEXTFLD(i)
DISPFLD i,0:i=jn:i=FINDFLD(i):DISPFLD i,1:RETurn i
END DEFine 
DEFine PROCedure DISPFLD(i,s)
PAPER#2,CC(2*f(i)+s):INK#2,CC(4+2*f(i)+s)
AT#2,y,x:PRINT#2,m$;:IF m$="":PRINT#2,r$(jt,4 TO)
END DEFine 
DEFine PROCedure EDITFLD(ii)
PAPER#2,CC(2*f(i)):INK#2,CC(4+2*f(i))
AT#2,y,x:PRINT#2,FILL$(" ",29)
AT#2,y,x:INPUT#2,O$:r$(ii)=r$(ii,1 TO 3)&O$
END DEFine 
DEFine PROCedure READFLD(l)
RESTORE l:READ x:READ y:READ m$:READ n$:READ jn:READ jp:READ jt
END DEFine 
DEFine FuNction FINDFLD(i)
READFLD(i+o):IF (jt<>i)AND(f(i)=0):i=jt:GO TO 32695
RETurn i
END DEFine 
DEFine PROCedure C68
PAPER#2,0:INK#2,4:CLS#2:VIEW#2,dev$ &'C68_help'
END DEFine C68
MRUN mdv1_TOOB
TOP_MENU_txt

Code: Select all

DATA   208,7,'   PSION   ','   SUITE   '
DATA   208,7,'  PROLOG   ',' and FORTH '
DATA   208,7,'    C68    ','programming'
DATA     2,7,'   QJUMP   ','  Q R A M  '
DATA    80,7,'   SUPER   ','   BASIC   '
OK=PMO : PAPER#2,0:INK#2,4:CLS#2 : bye
OK=LMO : PAPER#2,0:INK#2,4:CLS#2 : bye
OK=CMO : PAPER#2,0:INK#2,4:CLS#2 : C68 : bye
EXEC DEV$ & 'QRAM' : bye
CLS:CLS#0:PRINT#0,'Say BYE to quit SUPER BASIC':STOP
PSION_MENU_txt

Code: Select all

DATA  80,7,'  M A I N  ','  M E N U  '
DATA   2,7,'   PSION   ','   QUILL   '
DATA   2,7,'   PSION   ','  ABACUS   '
DATA   2,7,'   PSION   ','  ARCHIVE  '
DATA   2,7,'   PSION   ','   EASEL   '
OK=TMO : bye : REM back to main menu
EXEC dev$ & 'quill'      : bye
EXEC dev$ & 'abacus'     : bye
EXEC dev$ & 'archive'    : bye
EXEC dev$ & 'easel'      : bye
LANG_MENU_txt

Code: Select all

DATA   2,7,'    QED    ',' Text EDIT '
DATA  80,7,'  M A I N  ','  M E N U  '
DATA   2,7,' Edimburgh ','   PROLOG  '
DATA   2,7,'ComputerOne','   FORTH   '
DATA   2,7,'DigitalPrec','Super FORTH'
EXEC dev$ &'qed':bye
OK=TMO : bye : REM back to main menu
EXEC dev$ &'prolog':bye
EXEC dev$ &'forth':bye
EXEC dev$ &'forth83_job':bye
C68_MENU_txt

Code: Select all

DATA   2,7,' C68   QED ',' Text EDIT '
DATA   2,7,' C68       ','      MAKE '
DATA  80,7,'  M A I N  ','  M E N U  '
DATA   2,7,' C68       ','   COMPILE '
DATA   2,7,' C68       ','      LINK '
EXEC QED:C68:bye
EXEC_W MAKE,#2,#2,#2;p$ & " -t " & Q$:C68:bye
OK=TMO : CLS#2 : bye : REM back to main menu
CLS#0:PRINT#0,"Compiling...";:PRINT#0,CCC$(0):C68:bye
CLS#0:PRINT#0,"Linking...";:PRINT#0,CCC$(1):C68:bye
As you can see, all the menu files have the same structure :
- five data lines defining the function keys (F1 to F5) labels and their INK and PAPER.
- five statements defining their behaviour when their function keys are pressed.
Evidently, for these statements to EXECute, the corresponding executable binary files must be available somewhere.

The C68_MENU is special, because it will first display the following general help file : mdv1_C68_HELP

and then depending on the task selected, display an editable CFORM to compose a command line for CC (I will have to change this interface to adapt to the latest issue of C68 and maybe re-think it entirely... but write it still in plain interpreted SuperBasic to keep it easily modifiable).


May the FORTH be with you !
POLKa
User avatar
polka
Trump Card
Posts: 196
Joined: Mon Mar 07, 2011 11:43 am

Re: Modularity with plain SuperBasic

Post by polka »

Some of you may have detected that I still use 2 GOTO statements in CFORMS, but it is because I simply cut CFORMS out of a previous "spaghetti" BOOT. Thus, I had to manually correct the GOTO destination lines. In these two occurences, the aim was to repeat a statement that was not yielding a convenient result ; this could have been avoided without the SSB labels by this plain Superbasic construct :

REPEAT query
IF <ok> THEN EXIT query
END REPEAT query


In the numbered BYE PROCedure I use another type of GOTO, that was called a "computed" GOTO, at times when the primitive Basics had no SELECT statements. In my BYE PROCedure the GOTO statement computes a line number based on the first line of a menu and the key code of the key pressed (F1 to F5). When it can be used instead of a more verbose select statement, I still prefer to do it this way.

However in general, when coding without line numbers and using DATA statements and/or computed GOTOs, you have to put some actual line numbers into variables and refer to them in RESTORE and READ statements and in computed GOTOs. As you can see, this was done by the MANIFEST program for TMO PMO LMO CMO and CFO by chaining the calls to FuNtion ALN.

To show you another opportunity to use the potential of plain SuperBasic in a manifest file, what about conditional management of the _txt and the _bas files ?

The question that I address here : as ALN outputs a _bas file that is always the last update, the _txt file doesn't need to be numbered again each time it is LOADed/MERGEd. So in this new version of the MANIFEST file, the calls to ALN will be inserted into an IF...THEN...ENDIF construct

mdv1_MANIFEST_txt

Code: Select all

LRESPR 'mdv1_ptr_gen'
LRESPR 'mdv1_MacMouse11'
LRESPR 'mdv1_wman'
rem LRESPR 'mdv1_sys_hot_rext'
LRESPR 'mdv1_Qptr'
rem LRESPR 'mdv1_sys_Qpac2'
rem LRESPR 'mdv1_fileinfo2_bin'

window#0,512,256,0,0:paper#0,0;:ink#0,0;: cls#0
CLS#0:WINDOW#0,388,42,110,0:PAPER#0,2;:INK#0,7:BORDER#0,1,7
WINDOW#1,94,225,14,0:PAPER#1,0;:INK#1,7:BORDER#1,1,7
WINDOW#2,388,182,110,43:PAPER#2,0;:INK#2,4:BORDER#2,1,7
OPEN#3,'scr_512x31a0x225':PAPER#3,0;:INK#3,4

CFO = 32601
IF CFO > 0 THEN
OPEN_OVER#4,mdv1_TOOB
PRINT#4,"CFO = "; : PRINT#4,CFO:print CFO
R = ALN("CFORMS",CFO,1)
TMO = R+1
PRINT#4,"TMO = "; : PRINT#4,TMO:print TMO
R = ALN("TOP_MENU",TMO,1)
PMO = R+1
PRINT#4,"PMO = "; : PRINT#4,PMO:print PMO
R = aln("PSION_MENU",PMO,1)
LMO = R+1
PRINT#4,"LMO = "; : PRINT#4,LMO:print LMO
R = ALN("LANG_MENU",LMO,1)
CMO = R+1
PRINT#4,"CMO = "; : print#4,CMO:print CMO
R = ALN("C68_MENU",CMO,1)
PRINT#4,"KO = CFO - 1 : OK = TMO"
PRINT#4,"DLINE 1 TO KO"
PRINT#4,"INITFORM : BYE"
CLOSE#4
END IF

MERGE mdv1_C68_MENU_bas
MERGE mdv1_LANG_MENU_bas
MERGE mdv1_PSION_MENU_bas
MERGE mdv1_TOP_MENU_bas
MERGE mdv1_CFORMS_bas
But then, the TMO PMO LMO CMO and CFO line numbers must have been saved somewhere. Why not in the TOOB file, that will be MRUN after all the MERGE of MANIFEST are done ?

This is why inside the THEN...ENDIF alternative a channel #4 will be OPEN_OVER to edit TOOB this way :

mdv1_TOOB

Code: Select all

CFO = 32601
TMO = 32702
PMO = 32712
LMO = 32722
CMO = 32732
KO = CFO - 1 : OK = TMO
DLINE 1 TO KO
INITFORM : BYE
TOOB will then change each time you give - in the MANIFEST file - CFO a non-zero value. But if you give it a zero value (you have only one line to change in the mdv1_MANIFEST_txt file), it will skip all the ALN statements and use the _bas files he already has. And it will also use the last TOOB he has - because it also kept it.

So, instead of calling ALN each time you boot, if you change to zero the value of CFO, it will only MERGE the _bas files you already have numbered and MRUN the TOOB file edited the last time CFO was given a non zero value.

BYE ! POLKa


May the FORTH be with you !
POLKa
User avatar
polka
Trump Card
Posts: 196
Joined: Mon Mar 07, 2011 11:43 am

Re: Modularity with plain SuperBasic

Post by polka »

UU : ultimate update !

The demonstration of my methods to program QL SuperBasic without line numbers was successfully tested on : Daniele's Qemulator with TK2 and JSrom and uQLx on PIzeroW with TK2 and Minerva198. I have to confess however that on these two configurations the FuNction MWOLN (Merge WithOut Line Numbers) never worked : I got the messages "not implemented" or "not yet implemented" when MWOLN tried to execute MERGE inside the FuNction. However, MERGE and also MRUN, LOAD and LRUN are ok when used as commands (without line numbers) or in numbered statements outside any PROCedure of FuNction.

So I erased the MWOLN FuNction from my BOOT file and took the opportunity to modify the ALN (Add_Line_Numbers) FuNction to address two irritating issues.

First issue : In my former version of ALN, the blank lines in the _txt files got numbers too but when loaded the _bas file simply dismissed them and then, ran OK. In my new version the blank lines are dismissed by the ALN function itself.

Second issue : when editing a _txt file outside of the QL environment, you may get CR-LF at the end of lines instead of simple LF as needed by the QL. So, my new version detects CR characters in the _txt file and deletes them in the _bas file.

New versions of Add_Line_Numbers and Remove_Line_numbers :

Code: Select all

DEFine FuNction add_line_numbers(f$,first,interval)
input_file$  = dev$ & f$ & "_txt"
output_file$ = dev$ & f$ & "_bas"
OPEN_IN#5,input_file$
OPEN_OVER#6,output_file$
l = first : i = interval
REPeat number
   INPUT#5,l$ : k = LEN(l$) : IF k > 1 THEN
      IF CODE(l$(k))=13 THEN k = k-1
      IF k > 0 THEN PRINT#6,IDEC$(l,5,0);" ";l$( 1 TO k )
      ELSE i = 0 : END IF
   IF EOF(#5) THEN EXIT number : ELSE l = l+i : i = interval
END REPeat number
CLOSE#6 : CLOSE#5 : RETurn l
END DEFine add_line_numbers

DEFine FuNction remove_line_numbers(f$,column,suffix)
input_file$  = f$
output_file$ = f$(1 to (len(f$)-suffix)) & "_txt"
OPEN_IN#5,input_file$
OPEN_OVER#6,output_file$
l = 1
REPeat number
   INPUT#5,l$ : PRINT#6,l$(column to)
   IF EOF(#5) THEN EXIT number : ELSE l=l+1
END REPeat number
CLOSE#6 : CLOSE#5 : RETurn l
END DEFine remove_line_numbers
(Merge_WithOut_Line_Numbers) was discarded.

New version of the BOOT file -> with the new version of ALN.

Code: Select all

DEV$="FLP1_" : MODE 4 : PROG_USE DEV$ : DATA_USE DEV$

32741 MRUN mdv1_TOOB

32742 DEFine FuNction aln(f$,f,d)
32743 input_file$  = dev$ & f$ & "_txt"
32744 output_file$ = dev$ & f$ & "_bas"
32745 OPEN_IN#5,input_file$:
OPEN_OVER#6,output_file$
32746 l = f : i = d
32747 REPeat number
32748    INPUT#5,l$ : k = LEN(l$) : IF k > 1 THEN
32749    IF CODE(l$(k)) = 13 THEN k = k - 1
32750    IF k > 0 THEN PRINT#6,IDEC$(l,5,0);" ";l$(1 TO k)
32751    ELSE i = 0 : ENDIF
32752    IF EOF(#5) THEN EXIT number : ELSE l = l+i : i = d
32753 END REPeat number
32754 CLOSE#6 : CLOSE#5 : RETurn l
32755 END DEFine aln

32757 DEFine PROCedure BYE
32758 PAPER#3,0:INK#3,4:CSIZE#3,0,0:CLS#3:AT#3,0,7
32759 PRINT#3,' F1',,'F2',,'F3',,'F4',,'F5':RESTORE OK
32760 FOR i = 5 TO 69 STEP 16
32761    READ p,q,p$,Q$:PAPER#3,p:INK#3,q
32762    AT#3,1,i:PRINT#3,p$:AT#3,2,i:PRINT#3,Q$
32763 END FOR i
32764 p = CODE(INKEY$(-1))/4
32765 IF p<58 OR p>62 : GO TO 32764
32766 CLS#0:CLS:CLS#2:GO TO p+OK-53
32767 END DEFine BYE

PRINT aln("MANIFEST1",1,1)
MRUN mdv1_MANIFEST1_bas
Everything else is unchanged.

*To conclude : scanning the relatively recent thread "Coding SuperBasic", I discovered that coding SuperBasic without line numbers was done already for a long time, but examining SSB, I wondered if I could not program a simpler tool : with ALN, a 15 line function entirely coded in SuperBasic, I find I reached this goal : giving the means to develop programs as bundles of independant SuperBasic modules coded as unnumbered _txt files ; and with the help of a MANIFEST file, ALN them to become _bas files and chain-MERGE them. The issues of GOTO and DATA...RESTORE...READ should be solved with some SuperBasic programming disciplin fostering maximum use of PROCedures and FuNctions.

( a FORTH programmer can program FORTH in any language : was a sarcastic saying : just replace FORTH by FORTRAN )

BYE ! POLKa


May the FORTH be with you !
POLKa
Post Reply