
**********************************************************************
files: Storing.TXT, Storing.TAP
       StoreMC1.BAS, StoreMC2.BAS, StoreMC3.BAS, REMbuild.BAS
**********************************************************************


** storing machine code in a BASIC program
   =======================================

Apart from putting machine code above RAMTOP, which means that it has to be POKEd or LOADed separately after the BASIC program; or putting it in a REM, which messes up LIST and is cumbersome for long chunks of m/c; there's another method, described in Toni Baker's "Mastering Machine Code", which provides the best of both worlds. This installs machine code between the end of BASIC and the beginning of VARS, so that it gets saved & loaded within each program file - as if it was in a REM; but is neater & easier than putting it in a REM, as it just uses DIM a$() to allocate the space and doesn't interfere with LISTing the program - as if it was above RAMTOP.

The method involves assigning DIM a$() at the start of VARS to hold the m/c and then resetting VARS to point beyond it. As LIST stops when it encounters a line number >= $40nn there's no garbage LISTed after the last line of BASIC, even though the m/c is effectively in the PROG area, because the variable identifying letter for a$() is encoded as 11000001 = $C1nn.

This version reclaims any previously used space before assigning the new space and doesn't need any BASIC to be typed other than "GO TO 9161". The DIM a$() is set as n-18 because there will be two prior numeric variables defined at the time and these, plus the dimension parameters of a$(), total 18 bytes. The POKEs for VARS, and any subsequent references to prior variables, wouldn't normally work, as the VARS pointer would be changing, so to resolve this the routine saves these values in the display file.

Note that the statement "LET o=FN p(FN p(23627)-2)" requires the length of the m/c segment to have been saved at the end of the m/c segment, so the assembler listing should finish with a statement of the form:
MCSIZE: defw MCSIZE-MCSTART+2
where MCSTART is any label at the start of the listing.

As a m/c routine appended to a BASIC program will move as the program is edited, any absolute addresses used in CALL, JP, etc. need to be adjusted each time the program is run. Refer to the "relocating machine code" section for a routine to do this.

StoreMC1.BAS [code]
   2 DEF FN p(a)=PEEK a+256*PEEK (a+1):
     DEF FN g(v)=v-FN h(v)*256: DEF FN h(v)=INT (v/256):

     REM INSTALL MACHINE CODE...    with GO TO 9161
     REM Shift down VARS to reclaim old CODE space
9161 CLEAR : LET o=FN p(FN p(23627)-2): LET ov=FN p(23627)-o-(o>0):
     POKE 16384,FN g(ov): POKE 16385,FN h(ov):
     POKE 23627,PEEK 16384: POKE 23628,PEEK 16385: POKE FN p(23627),128:
     REM Shift up VARS to assign new CODE space
9162 CLEAR : INPUT "New ";n: LET nv=FN p(23627)+n+1: DIM a$(n-6-7-5):
     POKE 16384,FN g(nv): POKE 16385,FN h(nv):
     POKE 16386,FN g(n):  POKE 16387,FN h(n):
     POKE 23627,PEEK 16384: POKE 23628,PEEK 16385:
     LET ml=PEEK 16386+256*PEEK 16387:
     REM Load m/c between PROG and VARS
9163 LET mc=FN p(23627)-ml: LOAD "m/c"CODE mc,ml:
     PRINT "m/c starts: ";mc'"NOT " AND PEEK (mc-2)<>13;"OK": STOP
[/code]

   =======================================

If machine code is to be embedded in a BASIC listing, the conventional approach is to define it in DATA statments and insert it at runtime with a READ-POKE loop, either into a REM or above RAMTOP, which wastes both time & space, particularly if the DATA is in 5-byte FP form.

Some alternative methods which avoid these problems are:
1) Pre-assembled code can be loaded & embedded as in the previous example.
2) The m/c can be defined in a hex DATA string and then pre-loaded into itself, which takes no extra space, although the DATA string is twice the size of that actually required for the code.
3) The assembled m/c can be entered directly into a DATA string or protected string variable of the exact size required. If using a string variable it would be concealed (from LIST) & protected (from CLEAR) as in the previous example.

   =======================================

Method 2:
In case there are any errors in the hex code, the m/c is first POKEd above RAMTOP and only loaded into the DATA string once it has all been converted successfully. This conversion routine would then be deleted before saving the main program.

StoreMC2.BAS [code]
   3 DEF FN p(a)=PEEK a+256*PEEK (a+1):
     REM Get a 16-bit value from RAM
   4 DEF FN h(b)=16*FN n(PEEK b)+FN n(PEEK (b+1)):
     DEF FN n(n)=n-CODE "0"-(7 AND n>=CODE "A")-(32 AND n>=CODE "a"):
     REM Convert a 2-byte hi-lo hex code to a 1-byte decimal
9000 DATA "....hex code..."
9001 REM conversion routine
9002 RESTORE 9000: LET mc=FN p(23639)+7: REM mc=start of hex code
9003 LET ml=FN p(mc-4)-4: REM ml=length of hex code between ""
9004 LET r=FN p(23730)+1: LET r1=1: REM r1=(RAMTOP)+1
9005 FOR a=mc TO mc+ml-2 STEP 2:
9006   LET p=FN h(a): IF p<0 OR p>255 THEN PRINT (a-mc+2)/2,p: STOP
9007   POKE r,p: LET r=r+1
9008 NEXT a
9009 FOR a=r1 TO r-1: POKE mc,PEEK a: LET mc=mc+1: NEXT a: STOP
[/code]

   =======================================

Method 3:
This little routine (plus the code to protect a$ under VARS, which I haven't included here) could be kept as a separate utility program, MERGEd into other programs when required and then deleted. Not a lot of use with emulators where it's so easy to assemble code anywhere, but it could have been useful for people using real Spectrums and not having an assembler, to incorporate short m/code routines into their programs without the space overhead of DATA statements.

StoreMC3.BAS [code]
   3 DEF FN p(a)=PEEK a+256*PEEK (a+1):
     REM Get a 16-bit value from RAM
   4 DEF FN n(n$)=CODE n$-CODE "0"-(7 AND n$>"@")-(32 AND n$>"_"):
     DEF FN h()=16*FN n(b$(1))+FN n(b$(2)):
     REM Convert a 2-byte hi-lo hex code to a 1-byte decimal
   5 DEF FN c()=FN p(23621):
     REM Get the current line#

9990 LET r=FN p(23730)+1:
     REM hex input routine; BS cancels last entry; ENTER stops
9991 LET b=0: DIM b$(2)

9992   LET k=CODE INKEY$: GO TO FN c()+NOT k
9993   LET k=CODE INKEY$: GO TO FN c()+SGN k
9994   IF k=12 THEN
         PRINT TAB c;: LET c=c-2: LET r=r-1: LET b=0: REM delete entry
9995   IF k>=48 THEN
         LET b$(b+1)=CHR$ k: LET b=NOT b:
         IF NOT b THEN
           LET c=33-PEEK 23688: PRINT b$;:
           POKE r,FN h(): LET r=r+1
9996   IF k<>13 THEN
         GO TO 9992

9997 RANDOMIZE p-r: CLEAR: DIM a$(FN p(23670)):
     LET r=FN p(23730)+1: LET v=FN p(23627)+6:
     FOR a=r TO r+FN p(23670)-1: POKE v,PEEK a: LET v=v+1: NEXT a: STOP:
     REM copy POKEd code to a$()
[/code]

Methods 2 & 3 could be combined so that the typed hex code is encoded into a visible DATA string in the main BASIC listing, rather than a concealed & protected string variable. In which case it would be best to enter it as the last line to avoid interfering with LIST.


**********************************************************************


** Inserting binary code into ZX Spin assembler
   ============================================

If you want raw data to be included in your assembler listing, then load it as binary somewhere in memory, select the area in the debugger, right-click "Edit Selection : Mark as Byte Data", then right-click "Copy to Clipboard" which will give a string of DEFB statements which can be pasted into the assembler listing.


**********************************************************************


** Embedding/Concealing static data in BASIC
   =========================================

Static data - such as UDGs, character sets, lookup tables, machine code - can be set up separately and embedded in the program so only one SAVE or LOAD is needed for the game, and there's no space taken up in the game itself with extra code to define and generate the data. Using UDGs as an example:
[code]
   3 RANDOMIZE FN p(23627)-168:
     POKE 23675,PEEK 23670: POKE 23676,PEEK 23671:
     DEF FN p(a)=PEEK a+256*PEEK (a+1)
.... program ....
9999 CLEAR : DIM u$(168): RANDOMIZE FN p(23641)-6:
     POKE 23627,PEEK 23670: POKE 23628,PEEK 23671:
     CLEAR : LOAD "infernoudg"CODE FN p(23627)-168: SAVE "GAME" LINE 3
[/code]
The UDGs are defined separately and saved as CODE; the game is saved with "GO TO 9999"; line 3 sets the UDG system variable to point to the UDG definitions. Using VAL "..." with all the numeric expressions would save 66 bytes as well. The embedded data is invisible and so doesn't mess up LIST.

Line 9999 can best be explained with a little diagram:
[code]
[b]after DIM u$(168)[/b]
 VARS                                       ELINE
(23627)                                    (23641)
  |                                          |
|$d5|$ab|$00|$01|$a8|$00|<-168 spaces->|$80|
|<-----u$() header----->|

[b]after LOAD[/b]
                    VARS    ELINE
                   (23627) (23641)
                        |   |
|$d5|<-168 UDG bytes->|$80|
[/code]


**********************************************************************


** Insert a REM of a given length into a BASIC program
   ===================================================

Here's a way to do it in BASIC (tested on +2A).

REMbuild.BAS [code]
   1 INPUT "Length ";rl: RANDOMIZE rl: CLEAR:
     DIM r$(FN p(23670)): LET rl=FN p(23670):
     LET VARS=FN p(23627): RANDOMIZE VARS+6+rl:
     POKE 23627,PEEK 23670: POKE 23628,PEEK 23671:
     INPUT "Line# ";rn: LET rv=VARS+4: LET rl=FN p(rv):
     RANDOMIZE rn: POKE VARS,PEEK 23671: POKE VARS+1,PEEK 23670:
     RANDOMIZE rl+2: POKE VARS+2,PEEK 23670: POKE VARS+3,PEEK 23671:
     POKE rv,234: POKE rv+1,CODE "<": POKE rv+rl,CODE ">": POKE rv+rl+1,13:
     DEF FN p(a)=PEEK a+256*PEEK (a+1)
[/code]

--

A REM line has a 4-byte header plus the REM token plus the text plus the trailing ENTER. A 1-dimension string array has a 6-byte header plus the text. Thus conveniently a REM line with 'n' bytes of text and a 1-dimension string array with 'n' bytes of text are exactly the same size. The routine works by creating a 1-dimension string array of the required size, shifting the VARS pointer forward to the next variable (rl) after the string, and then overlaying the string with the required delimiters for the REM line. Thus the REM will always appear as the last line in the program.

The two "POKE ... CODE ..." statements are just to make it easy to distinguish the ends of the REM text. They can be removed, but something sensible - such as CODE " " - needs to be POKEd at rv+1, otherwise it will still hold the hi-byte of the string length. All of the other POKEs are required.


**********************************************************************
