0 REM Compile program 10 origin = <start of area for machine-code> 20 file$ = "ABC" 30 PROCrun("I",<page for source files>) 40 PROCrun("M",<page for macro file> (optional)) 50 FOR pass = 0 TO 2 STEP 2 60 P% = origin 70 FOR files = 1 TO LENfile$ 80 PROCrun(MID$(file$,files,1),<page for source files>) 90 NEXT files 100 NEXT pass 110 PRINT '"Object code from &'';~or igin ''to &;~P% 120 END 130 140 DEFPROC run (name$,start) 150 PRINT name$; 160 OSCLI "LOAD SOURCE"+name$+" "+STR$~start 170 PAGE = start 180 GOSUB 0 190 ENDPROC
The files are assumed to be called SOURCEA, SOURCEB, ... i.e. the word 'SOURCE' followed by a single letter. The string 'file$' holds the letters which identify them and hence in this example it contains 'ABC'. Since single letters are used, the length of 'file$' gives the number of source programs. Note that in this example 'I' and 'M' should not be used for naming source files since they have their own special uses.
The program starts by reading in and assembling SOURCEI which is the initialisation file to be described later in this chapter. Then the macro file (if one exists) will be treated similarly; again this will be explained later. The main loop takes each of the source files in turn, loads it into the area you have defined as reserved for the source files, and then assembles it. For the first source file the object code starts at the value of the variable 'origin', and P% is used by the assembler as a pointer to the next free byte. Hence this allows subsequent source files to be assembled so that their code follows on directly after that produced by the previous one.
A typical source file would be of the following format:
0 REM SOURCEX 10 20[OPT pass 30 . (Assembler text) . . . 900 910] : RETURN
A typical memory map might look like this:
Note that if you are using a 6502 second processor then the screen mode selected will not make any difference.
This method has been designed for use with disc based systems, but can also be used on cassettes if the tape is rewound between the two passes. To remind you of this you should place a 'Rewind Tape' message, together with a 'dummy=GET' statement (to wait for a key to be pressed as an indication that the tape is in the correct position), between the two NEXT statements.
DEF PROCsave OSCLI("SAVE <filename>+ STR$~PAGE + " "+STR$~TOP) ENDPROC
Note: this routine will not work on BASIC I. See chapter 9 (BASIC 1, BASIC II and Electron BASIC) for a description of OSCLI and the equivalent BASIC I routine.
The routine should be inserted at the ends of all the source files, and called whenever you wish to save the source file that is in memory at the time.
Thus to SAVE the program all that is needed is to type 'PROCsave'. The reason this is so useful is that when editing large numbers of source files which all look alike, it is very easy to overwrite an existing file by typing the wrong name.
An alternative to this, which works on all versions of BASIC, is to type, in immediate mode, 'SAVE $(PAGE+6)'. This looks at the first line of the program to find the filename. So, each program should start with
0 REM <filename> 100 <program> 200 ....
10 REM Default Soft Keys 20 30 *KEY 0 |LLIST|N|M 40 *KEY 1 RUN |M 50 *KEY 2 LOAD"SOURCE 60 *KEY 3 CALLenter||M 70 *KEY 5 PPOCsave|M 80 *KEY 6 PROCfind(" 90 *KEY 7 MODE7|MPAGE=&6000|ML0AD "COMPILE"|M 100 *KEY 9 |L*CAT|M
Note that line 80 has a reference to PROCfind. This procedure is to be used from immediate mode to find all occurrences of strings in the current source module. (PROCfind is defined in section 12.6.) This procedure should be at the end of every source file.
In the simplest case, when just one source file references a given macro, the macro can be added to the end of that source file, and the file treated normally. Consider, however, what would happen if two source files both had a reference to a macro called 'FNfred', and this macro was put at the end of each of them. Since they would almost certainly be different sizes the definition of 'FNfred' would, in each case, start at different addresses. Moreover, when the first source file was assembled the address of the macro in this file would be stored for use by all later references to the macro. Thus, when the second source file tried to use the macro, no searching through would occur. Instead the assembler would jump straight to the address which was stored by the first file and be unlikely to find 'DEFFNfred' starting there.
To avoid this problem a macro file is set up containing all the macros referenced in any of the assembler source files. This is present in the memory all the time, though in a different area of memory to the other files. Each of the macros must be called before the source files are assembled, however, so that the addresses where their definitions may be found are available to the compiler. Otherwise the first time that the compiler comes across a reference to a macro, e.g. FNfred, it will start searching for 'DEFFNfred', starting at PAGE, look through to the end of the source file, not find the definition, conclude that the macro doesn't exist and report 'No such FN/PROC'. So, set PAGE to the bottom of the macro file and call each macro once.
Thus a typical macro source file looks like this:
0 REM Macro file 10 20 pass = -1 : REM Dummy compilation 30 A% = Fnmacro1(0,0) + FNmacro2(0,0,0,0).. 40 RETURN 50 60 DEF FNmacro(temp, no) 70 IF pass <0 THEN = TRUE 80[OPT pass 90 ...
Line 20 sets 'pass' to a value that the assembler will not use, so that the first time the macro is referenced it will not generate any code. Note that every macro in the file must have a test to see if it is being referenced for the first time (as at line 70).
Since the macro file needs to be resident in memory during the compilation a space will have to be assigned for it. This is usually between the top of the normal source files and the bottom of the COMPILE program.
0 REM SOURCEI 10 20 REM variables 30 ------------------------------------------------------ This is where all the variables that will be accessed by all the source files are defined. Note that all the variables are defined to be resident in memory one after the other. Thus to move the block of variables around in memory, all you have to do is to simply change the value of 'P%' in line 40. ------------------------------------------------------- 40 P% = <start of memory to put variables> 50[OPT2 \ Report any errors, but don't list 60.<first variable> EQUB 0 \ Reserves one byte 70.<second variable> EQUW 0 \ Reserves two bytes 80.<third variable> EQUS STRING$(20,CHR$(0)) 90 etc. . 200] ------------------------------------------------------- This section sets up any constants that may be used in the program. The use of constants in any program is very important as is explained in chapter 12 (Program structure). ----------------------------------------------------- 210 220 REM Constants 230 240 limit = 45 250 numberofshapes = 12 260 oswrch = &FFEE 270 etc. . . ------------------------------------------------------- This section reserves memory for the data tables. ------------------------------------------------------ 300 310 REM Main Memory Allocations 320 330 P%=<start of memory allocated for tables> 340[OPT2 350.jim 360 OPT FNspace(80) 370.fred 380 OPT FNspace(300) See section 12.5 390.lenqth for 'FNspace' 400 OPT FNspace(1OO) 410 etc . . --------------------------------------------------------------- This section fills the data tables. --------------------------------------------------------------- 600 610 REM fill data tables 620 630 FOR offset = 0 TO numberofshapes 640 READ offset?length, offset?width, offset?type 650 NEXT offset 660 670 DATA 20,12,3 ,36,24,1 ,etc. 680 etc . . 999 RETURN