TECH NICHE

GOING FORTH...

A long, long time ago, there was a little firm called CURRAH, and the people at CURRAH had a dream ... They dreamt that they could create a magic box that would give the mysterious ‘Spectrum User’ powers beyond imagination.

Unfortunately, CURRAH went bust before their dream became reality, but their black box that gives you FORTH, an Assembler and a debugging tool, all from within BASIC, is now available from Quadhouse Computers. At £19.50 Paul Gardner finds it hard to resist.

FIRST, A LITTLE HISTORY

Back in early 1984, MicroSource was conceived as an idea. It was originally to be a ROM based BASIC compiler and assembler package. It soon became apparent though, with the prices of ROM chips as they were, that the BASIC compiler would either have to be quite limited or expensive to produce. The next idea was to offer a Forth compiler and an assembler. That was possible because of the compact nature of Forth code. It was finally designed and tested but then CURRAH went into liquidation. MicroSource was not forgotten, however, and the rights to the package were bought by DK’tronics in mid 1984. Then it was forgotten. Now, more than a year after it was finally completed, Quadhouse Computers have negotiated the rights to distribute MicroSource, and at a much lower price than was originally intended.

THE PACKAGE

The final MicroSource package is a plug-in ROM based module that gives you access to Forth, an Assembler and a ‘Software Front Panel’ debugging tool. The box itself is similar to the CURRAH MicroSpeech package, but smaller. It is another ‘last in the line’ add-on for the Spectrum, so you will need a motherboard of sorts if you want to plug in any other products that don’t have a through connector.

The MicroSource circuit holds two chips, a 4K masked ROM and a small ULA. The ROM is switched in whenever a new variable is created. Because of this it is necessary to make sure that the Interface 1 variables are installed first if you have an Interface 1 attached — either cause an error or do a CAT of your microdrive.

THE ASSEMBLER

The assembler is a full Z80 assembler that supports macros with local labels, conditional assembly, powerful pseudo-ops, various number bases and an expression evaluator that allows you to import the contents of pre-defined BASIC variables into machine code. The assembler is invoked by a statement:

LET assemble=0

and the assembly routine then follows this statement as a list of REM ! lines eg:

  810 LET assemble=0
  820 REM ! org 3200
  830 REM !start ld a,2
  840 REM ! defm a$
  850 REM ! ret
  860 — (basic program continued) —

The contents of the variable a$ are imported onto the assembly routine during assembly and occupy the bytes reserved by the DEFM pseudo op.

Any labels used in the assembly routine are set up as BASIC variables and can be used directly in a BASIC program. So for the above routine, RANDOMISE USR start would call the routine.

The assembler outputs the machine code it produces either directly into memory, or to any opened channel if you have an Interface 1 fitted. Object code produced can be saved onto microdrive files or event sent over the network. For non microdrive owners the assembler will print a ‘map’ of the object code locations, so that you can save it to tape using the usual SAVE "name" CODE routine.

As your source code is assembled, the assembly listing produced can be output to screen, printer or again, to any opened channel. Listing 2 is an assembly listing of the routine contained in the demonstrated program.

THE DEMONSTRATION PROGRAM

Three subroutines in different languages achieve the same thing: turning lower case characters into upper case characters — a tedious example, maybe, but it shows how you can take a BASIC variable (Z$ here) and use it in three separate languages!

  10 REM demo program for micro source
  20 CLEAR 63000
  30 GO SUB 500: REM define forth words
  40 GO SUB 900: REM assemble m/c
  50 REM set up test string
  60 PRINT "Please wait"
  70 PRINT "  - setting up string"
  80 LET s$=""
  90 FOR i=32 TO 127
  95 LET s$=s$+CHR$ (i)
 100 NEXT i
 105 REM lengthen the string
 110 LET s$=s$+s$
 120 LET s$=s$+s$
 130 LET s$=s$+s$
 140 LET s$=s$+s$
 150 LET s$=s$+s$
 160 PRINT "Length of string is ";LEN s$;" chars"
 170 INPUT "Press enter to continue";n$: CLS
 180 LET z$=s$: PRINT "Basic start"
 190 GO SUB 400: REM basic routine
 200 PRINT "Basic end"
 210 INPUT "Press enter to continue";n$: CLS
 220 LET z$=s$: PRINT "Forth start"
 230 GO SUB 700: REM forth routine
 240 PRINT "Forth end"
 250 INPUT "Press enter to continue";n$
 260 LET z$=s$: PRINT "M/C start"
 270 GO SUB 1300: REM forth into m/c
 280 PRINT "M/C end"
 290 STOP
 300 REM subroutines in different languages
 310 REM to change a string to upper case
 320 REM
 400 REM Basic subroutine
 410 REM
 420 FOR i=1 TO LEN (z$)
 430 LET y$=z$(i)
 440 IF y$>="a" AND y$<="z" THEN LET y$=CHR$(CODE (y$)-32): LET z$(i)=y$
 450 NEXT i
 460 RETURN
 470 REM
 500 REM Forth definitions
 510 LET forth=0
 520 REM # % upcase

 530 REM #  OVER * SWAP
 540 REM #  DO
 550 REM #   I C@
 560 REM #   DUP DUP 96 >
 570 REM #   SWAP 128 < AND
 580 REM #   IF
 590 REM #    1101111B AND
 600 REM #    I C!
 610 REM #   ELSE
 620 REM #    DROP
 630 REM #   THEN
 640 REM #  LOOP :
 650 REM #
 660 RETURN
 670 REM
 700 REM actual run time call
 710 LET forth=1
 720 REM # COPY Z$ upcase
 730 RETURN
 740 REM
 900 REM m/c routine definition
 910 LET assemble=1
 920 REM !      list 3
 930 REM !      opt 2+4+16+32+64
 940 REM !      org  63000
 950 REM !mcr   push af
 960 REM !      push hl
 970 REM !save  push bc
 980 REM !      push bc
 990 REM !      pop  hl
1000 REM !      dec  hl
1010 REM !      ld   b,(hl)
1020 REM !      dec  hl
1030 REM !      ld   c,(hl)
1040 REM !      pop  hl
1050 REM !loop  ld   a,(hl)
1060 REM !      cp   97
1070 REM !      jp   m,notlo
1080 REM !      cp   123
1090 REM !      jp   p,notlo
1100 REM !      res  5,(hl)
1110 REM !notlo inc  hl
1120 REM !      dec  bl
1130 REM !      ld   a,b
1140 REM !      or   c
1150 REM !      jp   nz,loop
1160 REM !resto pop  hl
1170 REM !      pop  af
1180 REM !      ret
1190 RETURN : REM back to basic
1200 REM
1300 REM forth interface to m/c
1310 LET forth=0
1320 REM # COPY Z$ DROP GET mcr USR DROP
1330 RETURN : REM back to basic

The Assembly listing produced when MicroSource assembles lines 920 to 1180 of the first listing.

MicroSource CURRAH 1984

 920 0000
                  list 3
 930 0000                          opt 2+4+16+32+64
 940 F618                          org  63000
 950 F618 F5           mor         push af
 960 F619 E5                       push hl
 970 F61A C5           save        push bc
 980 F61B C5                       push bc
 990 F61C E1                       pop  hl
1000 F61D 2B                       dec  hl
1010 F61E 46                       ld   b,(hl)
1020 F61F 2B                       dec  hl
1030 F620 4E                       ld   c,(hl)
1040 F621 E1                       pop  hl
1050 F622 7E           loop        ld   a,(hl)
1060 F623 FE 61                    cp   97
1070 F625 FA 2F F6                 jp   m,notlo
1080 F628 FE 7B                    cp   123
1090 F62A F2 2F F6                 jp   p,notlo
1100 F62D CB AE                    res  5,(hl)
1110 F62F 23           notlo       inc  hl
1120 F630 0B                       dec  bl
1130 F631 78                       ld   a,b
1140 F632 B1                       or   c
1150 F633 C2 22 F6                 jp   nz,loop
1160 F636 E1           resto       pop  hl
1170 F637 F1                       pop  af
1180 F638 C9                       ret

   0 Total Errors

mor         F618
save        F61A
loop        F622
notlo       F62F
resto       F636

Start         Length
63000 (F618H)    33 (0021H)

The Forth vocabulary supported by MicroSource.

Forth Vocabulary: SWAP DUP ?DUP DROP OVER ROT PICK(*) RP(*) . ? U. H.(*) C.(*) TYPE ."(*) EMIT CR SP + - 1+ 1- 2* 2/ * / MOD /MOD NEG MAX MIN ABS AND OR(*) XOR(*) RL(*) RR(*) @ ! +! C@ C! ' CONST VAR = <> > < O< NOT >= <= I I' J >R R> RP(*) IF ELSE THEN DO LOOP +LOOP BEGIN UNTIL WHILE REPEAT ABORT(*) HERE ALLOT , C, H(*) FORGET EMPTY LAST(*) #(*) STACK(*) LINK(*) ( MOVE USR(*) GET(*) PUT(*) COPY(*) TOK(*)

Those words followed by (*) are different to the Forth ’79 standard in some way.

THE FORTH

The Forth is invoked from BASIC by the statement:

LET forth=0

after which the Forth system will execute or compile the following lines as Forth code. Like the assembler, the Forth lines in a program start with a REM statement:

  890 LET forth=1
  900 REM # 2 DUP + 32000 C!
  910 REM # % newword 100 10 DO I, LOOP ;
  920 REM # newword
  930 — (basic program continued) —

For technical reasons, any Forth definitions are started with a % (percent) sign instead of the usual : (colon).

Forth definitions are compiled into a dictionary which can be saved to tape or microdrive and later re-loaded and linked into the dictionary, although only if loaded back into the original memory locations.

The Forth language supported is a small subset of Forth ’79. Most of the omissions cover virtual memory, double length number handling and most of the useful commands for creating your own data and language structures. Some useful additions include the ability to pass variables between BASIC and Forth and the ability to pass parameters into, and then call, machine code routines. Listing 3 gives the full list of Forth words supported. Those marked with an asterisk differ from the standard in some way.

 A=00  F=SZ0H0PNC M=F618

BC=0000> FC AF 11 FF FF C3 CB 11
DE=0000> FC AF 11 FF FF C3 CB 11
HL=0000> FC AF 11 FF FF C3 CB 11
IX=0000> FC AF 11 FF FF C3 CB 11
IY=5C3A> FF CD 00 B1 F5 6B AD 00

   F558> 00 00 00 00 00 00 00 00
SP=F560> 00 00 00 00 00 00 00 00
   F568> 00 00 00 00 00 00 00 00
   F570> 00 00 00 00 00 00 00 00

   F600> 71 15 F6 21 17 9B 36 98
   F608> 03 00 05 00 00 49 71 44
   F610> 71 52 1C 76 1B 03 13 00
PC=F618> F5 E5 C5 C5 E1 2B 46 2B
   F620> 4E E1 7E FE 61 FA 2F F6
   F628> FE 7B F2 2F F6 CB AE 23
   F630> 0B 78 B1 C2 22 F6 E1 F1
   F638> C9 00 00 00 00 00 00 00
   F640> 00 00 00 00 00 00 00 00

The screendump of a typical Debug Front Panel display — in this instance, displaying the machine code routine, again derived from lines 920 to 1180 of Listing 1.

DEBUG

The debugging part of the package is a ‘software front panel’ that allows you to step through machine code routines while displaying the contents of all, or some of the Z80 registers and a section of memory. Listing 4 shows a screen dump of the form panel, as it views the machine code that was assembled from the demonstration program. Debug also allows you to display memory contents as screens full of hex bytes or ASCII characters. There are a number of commands that allow you to selectively step through machine code routines, with the option of following, or not following, subroutine calls as you wish.

For all the potential of Debug as a tool it is badly let down in two respects. It is very difficult to follow a machine code routine that is simply displayed as hex bytes. If only the current instructions were displayed as the Z80 assembly mnemonics it would be easier to use. The other problem concerns print-outs. I use an old Kempston printer interface and when Debug tries to send the display output to my printer the entire system crashes! However, I have been assured that Debug works perfectly with Interface 1 and any serial port printer, or the original Sinclair printer.

THE DEMONSTRATION PROGRAM

The simple demonstration has a main BASIC part that sets up a string of 3072 characters of mixed character codes. The program contains three ‘subroutines’, one in BASIC, one in Forth and one in assembly language. These routines are functionally identical in that they will convert the string in Z$ so that all the lower case letters are changed to upper case ones. Before any of the routines are called, the Forth lines need to be compiled and the assembly code needs to be assembled. After that, the test string is set up and each routine is used to convert a copy of the test string. The retrospective timings for the routines are some indication of the speed advantage to be gained by not using BASIC for the repetitive or slow sections of a program:

BUGS

It is unfortunate but there are some. Given the history of this product, we are unlikely to see a bug free ROM being released. Apart from the error in Debug which stops it printing out to a Kempston printer interface, there seems to be no serious bug in the Assembler-Debug part of the package. The most serious bug I have found is in the Forth. Contrary to the Forth ’79 standard, in this version you can use the word ." (dot-quote) outside word definitions but not inside word definitions! For you BASIC fans, that’s like having a version of BASIC that won’t let you PRINT text strings written as part of the program... For similar reasons you may as well not include comments in your Forth programs because if you’re not careful they cause some very spurious errors that you will never find.

One other ‘bug’ is that you can only transport the values and addresses of simple variables between the BASIC, Forth and assembly language. This means that you cannot easily get Forth to do processing on BASIC arrays. This is a real pity, as array processing is one of the most time-consuming elements of BASIC programming.

CONCLUSION

The assembler itself is worth the £19.50 that is being asked for the whole package. If you are aware of the bugs in the Forth then it is easy to evade or avoid them. The manual is very good and explains things in just the right amount of detail. The program listed in this article should show you some of the power the MicroSource offers you. I shall certainly buy this one! (High praise indeed — ED)

Search for more information