
**********************************************************************
files: TXT2EMU.TXT
       http://www.users.globalnet.co.uk/~jg27paw4/pourri/txt2emu0.asm
**********************************************************************


** TXT2EMU (load BASIC text into an emulator)
   ==========================================

This allows a BASIC program to be loaded from a text file in any emulator which has a "load binary" option. The features are:

;keywords should be in all CAPITAL letters
;variable names may use both CAPITAL and small letters
;spaces not inside quotes or after a REM will be discarded
;a BASIC line may be split across multiple text lines
;a BASIC line starts with zero or more spaces followed by a digit 0-9
;a text line with no line number will be appended to the previous line
;(or ignored if it's the first line)
;BASIC lines may be inserted/changed/deleted
;line numbers do not need to be in ascending order
;(they will be inserted in their correct numerical sequence regardless)

A syntax error drops out into the lower screen editor with a flashing error marker in the usual way. Correct the error in the lower screen before switching back to the upper screen (EDIT+Screen on the 128k) to resume (RUN again, but enter zero for the length, and it will restart the load from the next line). The program runs in the display file, so a BASIC program almost as big as the available memory can be loaded.

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

This is the required loader. Any statements after the RANDOMIZE USR will be ignored, as the USR call returns to the direct command prompt with "0 OK".

[code]
     REM this line must be the first line in the PROG area
   1 CLEAR 65535-32: INPUT "Length? (0 to Resume) ";l:
     LET s=65535-32-128-l: LET s=s-(s-FN p(23670) AND NOT l):
     IF FN p(23653)+40<s THEN
     INPUT (("Load at "+ STR$ s+" then ") AND l);"Press ENTER "; LINE a$:
     LOAD "TXT2Emu" CODE 16384:
     RANDOMIZE s: RANDOMIZE USR 16384
   2 DEF FN p(a)=PEEK a+256*PEEK(a+1)
[/code]

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

It uses the ROM LINE_SCAN routine for syntax checking, so IF.1 commands will be OK as long as it's run with IF.1 attached, as the syntax error will page in the shadow ROM to verify the statement.

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

The runner doesn't have to be line 1, just the first line of whatever's in memory beforehand, and whatever's being loaded shouldn't insert any lines before it, otherwise it would return a "Statement lost" error if the line being executed had moved. Using line 0 would avoid those problems.

If a variable name in CAPS matches a keyword or starts with a keyword (eg. LET LEN=VRS-PRG or LET OR1=OD1/2) then it will be treated as an error.

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

Got it working on the +2A/+3 as well. Turns out that the main parser is not only in a different ROM, it's at a different address as well.

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

I thought of a much simpler way of handling the stack conflict - I use a "roaming" stack. Prior to calling ROM0:$026B "insert line" I set OLDSP to (STKEND)+130+(length of tokenised line). The ROM routine will then switch the stack to that low address whenever it's referencing RAMpage 7 and switch back to the high address when it restores RAMpage 0. Thus as the program expands the temporary stack stays ahead of it all the time. It will eventually hit the BASIC text file, but it will only be overwriting lines which have already been processed, so that doesn't matter, and TXT2Emu was always intended to cater for destructive update anyway in order to make maximum use of the available memory.

So the 128 version now loads the full 25kb test program successfully even though the text input is loaded at 40027->65375. After conversion STKEND is at 48046, so about 26kb is the maximum (tokenised) BASIC program size which can be loaded on the 128k, but that's a limitation imposed by the paging as the BASIC program can't extend into the top 16k as it would be impossible to edit it because of the editor needing to reference RAMpage 7.

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

I've got it working with the extra features on the 128k as well, with the syntax error & resume & large file handling.

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

I didn't make it clear that now the RANDOMIZE USR 16384 statement must be the last one on the line. If there are any statements after it on that same line (like the DEF FN) then it won't work properly. I've re-checked it with the loader both on line 1 and line 0 and the text file loaded and edited afterwards OK. I've added a comment about this in all the files.

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

A slightly less slight change to the 48k version: In that it now handles the If.1/Microdrive commands as well. It turns out that the first time the Shadow ROM is paged, not only does it extend the system variables and add extra channel info (which I knew about), but it also rather unhelpfully trashes the machine stack - so I had to add an extra couple of dozen lines to work around that; these changes have extra indentation in the listing. The mod's a bit clunky; there might well be a neater way to do it.

The If.1 extended BASIC commands are not supported on the 128k; I don't think that the hardware's supported anyway; maybe in 48k mode?

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

Apart from the real programs which I used for general testing, here's the artificial constructs which I used for testing specific areas.

[code]
1000 REM Test Microdrive commands
1001 CAT 1: LOAD *"m";1;"name": SAVE *"m";1;"Squares": VERIFY *"m";1;"Squares": MERGE *"m";1;"Squares"
1002 ERASE "m";1;"Squares"
1003 FORMAT "m";1;"name"
1010 OPEN #4;"m";1;"Numbers": CLOSE #4
1020 FORMAT "n";2: SAVE *"n";2: LOAD *"n";1: VERIFY *"n";1
1030 MOVE #1 TO #2
2010 MOVE "m";1;"Numbers" TO #2: MOVE "m";1;"Numbers" TO "m";1;"Numbers2"
[/code]

[code]
1000 REM test 128 commands
1002 IF 1=2 THEN SPECTRUM
1004 IF 2=1 THEN PLAY "cdefgabC"
1006 SAVE ! "S": CAT !: ERASE ! "E": LOAD ! "L": MERGE ! "M"
1008 SAVE!"S": CAT !: ERASE!"E": LOAD!"L": MERGE!"M"
1010 STOP
[/code]

[code]
  10 Test all +3 commands
  11 BEEP 1,2: BORDER 3: BRIGHT 1
  12 CAT #1,"a:file": CAT #1,"a:file" EXP
  13 CIRCLE 1,2,3: CLEAR: CLEAR 45321: CLOSE #1: CLS
  14 CONTINUE: COPY: COPY EXP INVERSE
  15 COPY "f1" TO "f2": COPY "f?.*" TO "b:": COPY "a:" TO "b:"
  16 COPY "f1" TO SCREEN$: COPY "f1" TO LPRINT: COPY "f1" TO SPECTRUM FORMAT
  17 DATA 1,2,3: DEF FN a(b,c)=b+c: DIM a(1,2,3): DIM a$(3,2,1)
  18 DRAW 1,2: DRAW 3,2,1: ERASE "f?.*": ERASE "b:": FLASH 1
  19 FOR a=b TO c: FORMAT "a:": FORMAT LINE 75: FORMAT LPRINT "C";"E"
  20 GO SUB 1000: GO TO 2000: IF 1=2 THEN STOP: INK 4: INPUT #5;a$
  21 INVERSE 1: LET a=c: LIST #3;21: LLIST 11: LOAD "t:": LOAD "f1"
  22 LOAD "f1" DATA a(): LOAD "f2" DATA b$(): LOAD "f3" CODE 65000,535
  23 LOAD "f4" SCREEN$: LPRINT: MERGE "f5": MOVE "a1" TO "b2"
  24 MOVE "f1" TO "+P": NEW: NEXT a: OPEN #5,"S": OUT 12345,255
  25 OVER 1: PAPER 2: PAUSE 3: PLAY "ABCDE","abcde": PLOT INK 2;5,6
  26 POKE 54321,255: PRINT #3: RANDOMIZE 12345: READ a,b$: REM Remark
  27 RESTORE 17: RETURN: RUN 11: SAVE "t:": SAVE "f1" LINE 27
  28 SAVE "f1" DATA a(): SAVE "f2" DATA b$(): SAVE "f3" CODE 65000,535
  29 SAVE "f4" SCREEN$: SPECTRUM: STOP: VERIFY "f1"
  29 LET a=ABS b+ACS c: IF a=1 AND b=2 THEN PRINT ASN c+ATN d
  30 LET a=ATTR (b,c): POKE 12345,BIN 01010101: LET a$=CHR$ b
  31 PRINT CODE "a"+COS B+EXP c+FN a(b,c): LET b=IN 12345: LET a$=INKEY$
  32 PRINT INT a+LEN b$+LN c: IF NOT a OR PEEK b THEN PRINT c
  33 LET a=PI: LET b=POINT (5,6)+RND: LET c$=SCREEN$(7,8)
  34 LET a=SGN b+SIN C+SQR d: LET a$=STR$ 12345: PRINT TAN a+USR b
  35 LET a=USR "a": LET b=VAL "b": LET c$=VAL$ d$[/code]
[/code]

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

TXT2Emu works with SE BASIC (the v0.93e which comes with ZXSpin, anyway, so I would expect it to work with any other version). There are some differences in the SE ROM's token table - SE BASIC uses GOTO and GOSUB, for example, rather than GO TO and GO SUB - so text input files would have to comply with any such differences, as TXT2Emu uses the ROM's own parser for checking the command line syntax (although it's TXT2Emu which does the precursor tokenising, it uses the ROM's token table to determine the valid entries, anything else being passed to the parser as plain text).

As long as SET_MIN, MAKE_ROOM, E_LINE_NO, LINE_SCAN and MAIN_ADD are in the same places in the ROM, then I'd expect TXT2Emu to work with any 48k ROM variant. Similarly, as long as SET_MIN, MAKE_ROOM and E_LINE_NO - together with ProcessKeyPress, ExecuteCommandLine and ToggleScreen_KeyHandler are in the same places in the ROM, then I'd expect TXT2Emu to work with any 128k ROM variant as well (eg.it also works with the +3e). Oh, not forgetting the token table as well, of course, which is expected to start at $0095 in the BASIC ROM for all versions.

Those routines aren't addressed in any jump block / vector table, so for any non-working variants it would be necessary to search through the ROM's code to find out where they were.

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

I've re-tested it on 48k/+128k/+3 with small & large files with & without syntax errors (including If.1/Microdrive commands on 48k). There's one small change right at the start, in that there's a CHAN_OPEN call to ensure that 'K' is the current channel (if it wasn't then the error return wouldn't work properly).

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

Some of the advantages of using TXT2EMU are mentioned in the listing comments:

* A BASIC line may be split across multiple text lines, so the listing can be formatted for readability.

* BASIC lines may be inserted/changed/deleted, as it uses the ROM's editor to enter the BASIC, it can do anything that could be done if you were typing in the lines manually.

* Line numbers do not need to be in ascending order (although it might get a bit confusing for the programmer if they're not).

* Errors can be corrected interactively and loading resumed from the next line.

* As it's using the Spectrum's own ROM routines to check the BASIC it will accept any syntax which is valid for the host machine.

* Changes can be applied to an existing program already in memory.

* It's easy to alter TXT2EMU if you have a mind, as it's written in the Spectrum's own machine language and is only 500 lines or so (discounting comments).

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

I was looking at the encodings supported by BAS2TAP for entering graphics, print controls, etc. as plain text. Basically, there's only one needed, being {XX}, which allows any hex byte to be defined. All the others just duplicate what can be done with that one, but with some improvement in readability; eg. {(C)} instead of {7F} for the copyright symbol, or {PAPER 3} instead of {11}{03} for an embedded print control. There didn't seem much call for most of them, although {X} for the UDGs A-U seemed handy, so I've included those two in TXT2EMU.

So, TXT2Emu now supports (in CAPS):
{00}-{FF} for hex byte values $00-$FF
 {A}-{U} for UDGs A-U
The new bit is in lines 150-194.

For block graphics use {80}-{87} for the unshifted ones and {88}-{8F} for the shifted ones. If your eyesight's anything like mine then I should point out that the enclosing characters are braces {} and not parentheses (). If, for some wacky reason, you wanted the literal text for {00} or whatever, then just use the encodings for braces instead of the actual characters; eg. {7B}00{7D} will output {00}.

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

This enhancement also overcomes the problem with the 128k editor of not being able to insert command tokens in strings. For example, in:
[code]DEF FN j$(s,l)=CHR$ PEEK (s)+VAL$ (("FN j$("+STR$ (s+1)+","+STR$ (l-1)+")" AND l>1)+("""""" AND l=1))[/code]
replace the quoted FN with {A8} thus:
[code]DEF FN j$(s,l)=CHR$ PEEK (s)+VAL$ (("{A8} j$("+STR$ (s+1)+","+STR$ (l-1)+")" AND l>1)+("""""" AND l=1))[/code]
and after TXT2EMU has loaded it the function will work on the 128k models. (It PEEKs 'l' bytes from memory starting at 's' and returns them as a string.) This also allows the use of embedded print controls in 128k BASIC listings.

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

In TXT2Emu0.ASM replace line 195
[code]        jr   nz,NEXT_C       ;plain copy if after REM[/code]
with
[code]        dec  de
        jr   nz,NEXT_C       ;no copy if after REM
        inc  de[/code]
to truncate all REMs. The REM token will be preserved as a place holder but all the following text on the line will be discarded.

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

As TXT2Emu uses the Spectrum's own ROM routines for syntax checking & editing it works as though the lines were being typed in on a Spectrum, so lines can be entered in any order and it can be used to update existing files as well as loading new ones. Syntax errors can be corrected as the file is loading, and BASIC lines can contain multiple line breaks; input lines which don't start with a number are just appended to the previous line.

Download the ZIP file, load the TAP file, read the instructions at the top of the ASM listing, run it and follow the prompts. Don't use BASIC line 1 as that's where TXT2Emu's BASIC loader resides. It runs on any emulator which has a "load binary" option. The CODE loads into the display file, which is also used as the editor buffer, so don't panic when the screen gets grunged; just press ENTER after the OK prompt to clear it. Once a BASIC program has been loaded it can be saved in any format supported by the chosen emulator.

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

[list=a]
[*]Firstly, get the exact byte size of the BASIC text file. I usually right-click on the name in the file browser and select Properties.
[*]Load the TXT2Emu BASIC loader from the TAP file in any Spectrum emulator which has a "load binary" option.
[*]RUN the loader.
[*]At the prompt "Length? (0 to Resume) " enter the exact byte size of the BASIC text file then ENTER.
[*]At the prompt "Load at nnnnn then Press ENTER " use the emulator's "load binary" option to load the BASIC text file at address 'nnnnn' then ENTER.
[*]The TXT2Emu CODE will be loaded at the top of the screen which will then load the BASIC from the text file.
[/list]

If there's a syntax error in a line the program will switch to the BASIC editor and display the line with the flashing (or red) error cursor. Correct the error, ENTER, RUN, but enter zero at the "Length?" prompt and TXT2Emu will resume loading from where it left off. On the 128 models an error switches to the lower screen editor, in which case correct the error, ENTER, EDIT (to switch back to the upper screen), RUN, etc.

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

TXT2Emu certainly works with ZXSpin, as that's the emulator which I use most of the time and the one which I used to write it with. It also works with Fuse, SpecEmu, EightyOne. In fact, it must work with any standard Spectrum emulator which has a "load binary" option, as long as it's given the correct exact byte size of the input file and it's loaded where it says to load it. It won't work with BASin because that doesn't work like a normal Spectrum as it has its own editor overlaid on top, but BASin has several of its own import options anyway.

If TXT2Emu is given the wrong size then it will drop out to the editor with a garbage line when it gets to the end of valid BASIC, but everything before that will have loaded OK. If the file's not loaded at the given address then it will drop out to the editor straight away. If the loader gets overwritten then it will fail so don't load a program which uses line 1. This is why the loader is still there afterwards; it's supposed to be, otherwise it would fail with "N Statement lost" when it tried to return to BASIC, unless the line which overwrote it happened to be exactly the same size. Line 2 holds a DEF FN but that can be overwritten.

--

It's possible to create a single line "LINE 0" version so the loader will be preserved even if the input file uses line 1, thusly:
[list=a]
[*]Enter the DEF FN statement from line 2 at the start of line  1.
[*]Delete line 2.
[*]Enter as a direct command: POKE FN p(23635)+1,0
[*]Enter as a direct command: SAVE "TXT2Emu" LINE 0
[/list]
but this version will only work on 48k models, whereas the standard version will work with any model.


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


** TXT2EMU truncate REM enhancement
   ================================

In [url=http://www.users.globalnet.co.uk/~jg27paw4/pourri/txt2emu0.asm]TXT2Emu0.ASM[/url] replace line 195
[code]        jr   nz,NEXT_C       ;plain copy if after REM[/code]
with
[code]        dec  de
        jr   nz,NEXT_C       ;no copy if after REM
        inc  de[/code]
to truncate all REMs. The REM token will be preserved as a place holder but all the following text on the line will be discarded.


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