Conversions: 4K ROM to 8K ROM and 8K ROM to 4K ROM
One of the big questions raised by Sinclair users is how to convert programs from the 4K ROM to the 8K ROM and vice versa. This article will serve as a comprehensive guide to such attempts. Let’s begin by a review of each of the commands and functions of both ROMs. It should be noted in the interim that there is no guarantee that all programs can be converted from one ROM to the other. It should also be noted that, barring an extremely complex software scheme, there is no way to load a 4K program from tape into an 8K machine or 8K programs on tape into a 4K machine.
Expressions
An expression is a series of values, strung together with operands, for example, 3-4+8/AMD. Expression evaluation is carried out by applying operators to their operand(s). For example, applying the + operator to the operands 4 and 26 yields the value of 30. Expressions are evaluated, operator by operator, according to the priorities of the operators they contain. The priorities for operators are shown in Figure 1.
As you can see, the only difference between the ROMs is the priority of the / (division) operator. In theory, this could cause problems. In practice, however, one can usually be assured that an expression specified on one machine, can be transferred as is, without ill effects. If you do find this difference to be a problem, you can always use parentheses to alter the order of evaluation.
8K ROM 4K ROM Operator priority priority Substrings 12 — All functions (except unary minus and NOT) 11 11 ** 10 10 Unary Minus 9 9 * 8 8 / 8 7 + 6 6 - (binary) 6 6 =,<,> 5 5 <=,>=,<> 5 — NOT 4 4 AND 3 3 OR 2 2
[Figure: Figure 1. Operator priorities on the 8K and 4K ROMs]
Functions
Many functions previously unavailable to the 4K ROM user are now available for owners with the 8K floating-point ROM. The primary problem with converting programs from 8K to 4K is that there is simply no way to work with floating-point numbers (that is, a number like 4.827736, as opposed to an integer like 6 or 327), on the 4K machine. Many of the new functions are “floating-point functions.” For example, there is no purpose in having a PI function on an integer-Basic machine, as the value 3 is not as useful as 3.14159265…. The following functions fall under this category of “floating-point only”:
ACS(x) Arc Cosine ASN(x) Arc Sine ATN(x) Arc Tangent COS(x) Cosine EXP(x) Natural logarithm (base e) PI The value 3.14159265 LN (x) Natural logarithm SIN(x) Sine SQR(x) Square root TAN(x) Tangent INT(x) Integer
Many other functions, however, are worth the conversion process. These are detailed below.
CHR$(x) / CODE(x$)
These functions perform the operation of converting a number to its corresponding character, and vice versa. They work identically on both ROMs, but it must be emphasized that the character sets on the two ROMs are different. For example, CHR$(20) yields a multiplication sign on a 4K ROM, but on an 8K ROM it produces an equals sign. You must not assume that a line such as:
3100 IF CODE(A$)=13 THEN GOTO 100
can be directly transferred to the other ROM. Most of the character set, primarily the most important and most commonly used sections, are the same on both ROMs. The characters for numbers and letters have the same codes, but this is not true of all characters. The primary rule is: If you are not sure, check it.
RND / RND(x)
The random number generation functions differ simply in that the 4K returns a random integer and the 8K a random floating point number. The 4K random number function, when called, spews up a random integer between 1 and x, x being the argument of the call, i.e., J=RND(x). The 8K function RND(no argument) returns an FP (floating-point) value between 0 and 1. It is clearly a fruitless venture to attempt to simulate the 8K function on a 4K machine, as you cannot have a floating point number in an integer Basic program. (A number between 0 and 1 must, obviously, be an FP number.)
It is, however, possible to obtain a random integer on an FP machine. To get a random number between 0 and x-1, use the expression INT(RND*x)+1. Compare the sample lines below:
4K Integer Basic
100 J+RND(100)
110 REM set J equal to a random
120 REM number between 1 and 100
8K FP Basic
100 J=INT(RND*100)+1
110 REM set J equal to a random
120 REM number between 1 and 100
INKEY$
The INKEY$ function is available only on the 8K ROM. Without a moderate amount of machine language programming, there is no way to simulate it on the 4K machine. If the thought of such an endeavor strikes you as an interesting project, I would suggest my article on the ZX80’s keyboard system (SYNC 1:2) as a good starting point.
LEN(x$)
This function is not available on the 4K machine, but it is easy to simulate by a simple subroutine. On a 4K machine you would add this subroutine at the end of your main program. Then to find the length, in bytes (characters) of a string you can set a few argument variables and call the subroutine. This process is illustrated in Figure 2. The Len subroutine is found in Figure 3.
9000 REM LEN
THIS SUBROUTINE FINDS
9010 REM THE LENGTH OF A STRING.
TO USE IT, SET Z$=x$; x$
9020 REM BEING THE STRING YOU
WANT TO FIND THE
9030 REM LENGTH OF. THE SUB-
ROUTINE WILL RETURN
9040 REM WITH THE LENGTH OF THE
STRING IN THE VARIABLE
9050 REM J. YOU SHOULD BE SURE
THAT YOUR PROGRAM
9060 REM WILL RUN PROPERLY IF
THESE VARIABLES (Z$,J)
ARE DESTROYED.
9070 LET J=0
9080 IF Z$="" THEN RETURN
9090 LET J=J+1
9100 LET Z$=TL$(Z$)
9110 GOTO 9080
[Figure: Figure 2.]
100 REM MAIN PROGRAM
110 LET H$="LKJHGTYL"
120 REM NOW CALL THE LEN ROUTINE
130 LET Z$=H$
140 GOSUB 9000
150 PRINT J
160 REM THIS PROGRAM WILL
PRINT
170 REM THE NUMBER 8, BECAUSE
H$
180 REM HAS 8 CHARACTERS IN IT.
190 STOP
[Figure: Figure 3.]
SGN(x)
The SGN(x) function is used to determine the sign (not sine) of a given number, its argument. If the value passed to the SGN(x) (often pronounced ‘signum’) function, is negative, the function returns a value of -1. If the value passed is 0, the function returns a zero. If the value passed is positive, that is, greater than zero, the signum function returns the value -1. For example, if you PRINT SGN(-237) the computer will dislay a -1 on the screen. Whereas, were you to PRINT SGN(237), the computer would place a 1 on the screen.
This is a useful function. It is not quite so useful, however, if you have to use a subroutine call to access it. To elucidate, you lose much of the value of being able to specify an expression as
100 LET X=SGN(RES23/2)+USR(AD)*
4+1
when you must express it as:
100 LET TEMP=RES23/2
110 GOSUB 9000
120 LET X=TEMP+USR(AD)*4+1
The listing of the routine to simulate SGN(x) is given in Figure 4. The calling procedure for the SGN(x) subroutine is found in Figure 5. You may, however, find it more useful (and easier) to find the signum, directly in your program, as opposed to calling it as a subroutine all the time.
9000 REM SGN
9010 REM THIS SUBROUTINE IS USED
9020 REM TO FIND THE SIGNUM OF
9030 REM A GIVEN NUMBER. TO USE
9040 REM IT, YOU PASS ITS ARGU-
9050 REM MENT IN THE VARIABLE
9060 REM TEMP. THE SUBROUTINE
9070 REM RETURNS THE SIGNUM OF
TEMP IN TEMP.
9080 IF TEMP=0 THEN GOTO 9200
9090 IF TEMP>0 THEN GOTO 9230
9100 REM HERE IF TEMP < 0
9110 TEMP=-1
9120 RETURN
9200 REM HERE IF TEMP=0
9210 RETURN
9230 REM HERE IF TEMP>0
9240 LET TEMP=1
9250 RETURN
[Figure: Figure 4.]
100 REM MAIN ROUTINE
|
|
500 LET TEMP=N ! You now want the SGN(N)
510 GOSUB 9000 !Call the subroutine
520 LET NEWVAL=TEMP !Get the SGN into NEWVAL
| !
| ! This sequence of code
| ! is equivalent to:
| !
| ! LET NEWVAL=SGN(TEMP)
| !
|
9000 REM SGN
|
|
[Figure: Figure 5.]
VAL(x$)
The VAL(x$) function is used to convert between a string containing the character representation of a number to an actual number. For example, PRINTing the VAL(“1234”) yields the number 1234, not the string “1234”. You cannot assign the result of using the VAL() function to a string, nor can you use a number as the argument. That is, X$=VAL(T$) is illegal, as is T=VAL(J). Once again, the 4K ROM falls short by not providing this useful function, but we can use the subroutine in Figure 6 to simulate it. The subroutine takes the argument T$ and returns the VAL of it in the variable TEMP.
9000 REM VAL
9010 REM
9020 REM THIS SUBROUTINE IS USED
9030 REM TO FIND THE VAL(T$). IT
9040 REM RETURNS THIS VALUE IN
9050 REM THE VARIABLE TEMP.
9060 LET TEMP=0
9070 IF T$="" THEN RETURN
9080 LET TEMP=TEMP+CODE(T$)-28
9090 LET T$=TL$(T$)
9100 LET TEMP=TEMP*10
9110 GOTO 9070
[Figure: Figure 6.]
STR$(x)
This function is the inverse of the VAL() function. It returns a string which corresponds to the characters which would appear on the screen if you were to PRINT the number. For example, LETting T$= STR$(23) would give the 2-character string “23”, not the number 23. This function is available on both the 4K and 8K ROMs, and works identically on both. Therefore, when translating from one ROM to the other, you can use exactly the same function without any problems.
USR(x)
The USR (pronounced ‘user’) function is used to call machine language subroutines in the computer. The 4K and 8K versions differ only in where they get the value to return to Basic. The 8K version, when your machine language subroutine RETurns, yields the 16-bit number held in the BC register-pair. The 4K version returns the value in the HL register-pair. (Do not worry if you do not understand this; this is directed to machine language programmers.)
ABS(x)
This function returns the absolute value of its argument.This function is available on each ROM and can be transferred freely between 8K and 4K ROM programs.
PEEK(x)
The PEEK function is used to find the value of an arbitrary byte of memory (i.e., byte x). It is available on both ROMs. The only difference between the two is that, whereas on the 4K ROM one might say:
J=PEEK(-13420)
on the 8K ROM one would say:
J=PEEK(46188).
The formulae for conversion between ROMs are:
4K to 8K:
If the address is negative, make it positive and add 32768.
8K to 4K:
If the address is greater than 32768, subtract 32768 and make it negative.
NOT X
This function is used to obtain the logical inverse of the value given as the argument. It is available on both ROMs. The only difference is that on the 4K ROM “True” is represented as -1. Whereas on the 8K ROM, “true” is represented by 1. Both ROMs use 0 to represent “false.”
X AND Y/X OR Y
AND is available on both ROMs. It does, however, work differently. On both ROMs a statement such as:
IF A=12 OR B>37 AND X$="Y" THEN
GOTO 1000
will execute identially on both ROMs. But a statement such as:
JY=P AND 2+Z
will create different results on the two ROMs.
On the 4K ROM AND (and OR) are binary. That is, each bit of the operands are ANDed (or ORed) together. On the 8K ROM the AND and OR operators work differently. The expression AN AND XZ will yield 0 if XZ is equal to 0. Otherwise, it will return AN. Thus,
47 and 227
returns 227. But,
0 and 227
returns zero. OR works in a similar way. X OR Y returns 1 if Y=0. Otherwise, it returns X. The general rule for conversion is: If the operators are used in a boolean expression (like an IF statement), they can be transferred directly. If not, you are probably in trouble.(Ed.—See Kitchen SYNC in this issue for further discussion of AND, OR, and NOT.)
In the next issue, I will continue the discussion of program conversion by giving tips for converting commands (e.g., PRINT and INPUT) from one ROM to the other. Until next time, same relativistic time period, same non-Euclidian universe.