From
Ivan Roshin
→
To
All
8 February 2002
Hello, All!
═══════════════════ call_asm.1 ══════════════════
(c) Ivan Roshchin, Moscow
Fido: 2:5020/689.53
ZXNet: 500:95/462.53
E-mail: asder_ffc@softhome.net
WWW: http://www.ivr.da.ru
Passing parameters
═══════════════════
when calling assembly procedures from a BASIC program
════════════════════════════ ════════════════════════════
("Radiomir. Your computer" 10/2001)
Introduction
────────
After a BASIC program is written, often
it turns out that it works prohibitively slowly. Yes
several ways to solve this problem. You can completely
rewrite the program in assembler, achieving
maximum performance - but this will have to cost
a lot of time and effort. You can use the BASIC compiler - this
will not require any significant effort, but the program
will not be as fast as one written in assembly language, besides
There may also be problems with insufficient memory during compilation
or when trying to run a compiled program. It is possible
finally, rewrite in assembler only those sections of the program
on which most of the time is spent. At the same time
performance will be only slightly lower compared to full
rewriting the program in assembler, and the amount of work will be
much less. It is this method of optimization in a number ofcases turns out to be the most preferable.
When using assembly procedures, the question arises
passing parameters. In standard BASIC there are means for this
very modest: it is only stipulated that the called procedure
can return one value in register pair BC, representing
which is an integer in the range 0-65535. Let's say, when
executing the command
....
1000 LET A = USR 40000
....
the assembler procedure located at the address will be called
40000, after which variable A will be assigned the value
register pair BC.
You can select a specific area for transferring parameters
memory, write values there before calling the procedure
passed variables using the POKE operator, and then
call - read return values using operator
PEEK. Let, say, you need to pass the scaling procedure
coordinates located at address 40004, variables X and Y
(integers, in the range 0-65535) through memory area 40000-40003 (with
Address 40000 contains the value X, in the format minor
byte/high byte; with 40002 - Y value, in the same format).
The procedure will change these values in a certain way, leaving
the result is in the same memory cells; after that you need to read
returned values and assign them to variables X1 and Y1.
.....
1000 POKE 40000,X-INT(X/256)*256: POKE 40001,INT(X/256)1010 POKE 40002,Y-INT(Y/256)*256: POKE 40003,INT(Y/256)
1020 RANDOMIZE USR 40004
1030 LET X1=PEEK(40000)+PEEK(40001)*256
1040 LET Y1=PEEK(40002)+PEEK(40003)*256
.....
As we can see, with this method of passing parameters
The volume of the BASIC program increases, time is wasted on transmission
values (sometimes quite significant - for example, when transmitting
arrays), you have to remember which address is which
the variable is located, and, finally, it simply may not be enough
memory.
A more promising way seems to be when
an assembler program directly works with variables
BASIC. Let's consider this method in more detail.
BASIC Variables
──────────────────
Let's first consider what variables there are in BASIC, where
and in what format they are stored.
BASIC program variables are located in the memory area
the beginning of which is addressed to the VARS system variable,
located at #5C4B=23627. Variables are strictly different
after each other, and after the last of them there is byte #80 - a sign
end of the variable area.
Although variable names are allowed in both uppercase and lowercase
Latin letters, as well as spaces and numbers (a number cannot be
the first character), in the storage area for variable names
are converted to lowercase letters and spaces are not stored (so
Thus, for the interpreter the variables Size X and sizex are one and the samethe same), the "$" sign for symbolic variables is also not stored.
Below are the different types of variables and the format:
their storage (format data given in [1], frankly speaking,
do not differ in completeness and accuracy, so a lot had to be
study through practical experiments).
════════════════════════ ════════════════════════
Best regards, Ivan Roshchin.
From
Ivan Roshin
→
To
All
8 February 2002
Hello, All!
═══════════════════ call_asm.2 ══════════════════
Numeric variable with a single letter name
- name (1 byte)
- value (5 bytes)
Numeric variable with a multi-character name
- name (the code of the first character is increased by #40, the last - by #80)
- value (5 bytes)
for-next loop variable
- name (1 byte), symbol code increased by #80
- value (5 bytes)
- limit (5 bytes)
- increment (5 bytes)
- number of the line in which the corresponding
FOR operator (2 bytes)
- the number of the operator next after FOR in this line (1 byte)
(Note: a variable with a single letter name defined
initially as usual, later, if it is used in
as a for-next loop variable, changes its format
storage.)
Numeric array
- name (1 byte), symbol code increased by #20
- array length in bytes (2 bytes)
- number of dimensions (1 byte)
- first dimension (2 bytes)
...........................
- last dimension (2 bytes)
- elements (5 bytes each), stored in the following order:
first the last index increases, then the penultimate one and
so on, for example (1,1), (1,2), (1,3), (2,1), (2,2), ...
Character variable
- name (1 byte), character code reduced by #20
- number of characters (2 bytes)
- contents
Character array
- name (1 byte), symbol code increased by #60- array length in bytes (2 bytes)
- number of dimensions (1 byte)
- first dimension (2 bytes)
...........................
- last dimension (2 bytes)
- elements (1 byte each); the storage procedure is the same as for
numeric arrays.
As we can see, by the value of the first byte of the variable name
You can immediately determine its type:
╔══════════════════════════╤═══ ═══════════════════════════════╗
║ Value range │ Variable type ║
║ first byte │ ║
╠══════════════════════════╪═══ ═══════════════════════════════╣
║ #41-#5A │ symbolic ║
╟──────────────────────────┼─── ───────────────────────────────╢
║ #61-#7A │ number (name - one letter) ║
╟──────────────────────────┼─── ───────────────────────────────╢
║ #81-#9A │ numeric array ║
╟──────────────────────────┼─── ───────────────────────────────╢
║ #A1-#BA │ number (name - several characters) ║
╟──────────────────────────┼─── ───────────────────────────────╢
║ #C1-#DA │ character array ║
╟──────────────────────────┼─── ───────────────────────────────╢
║ #E1-#FA │ loop variable ║
╚══════════════════════════╧═══ ═══════════════════════════════╝Table 1
I will provide some information about the five-byte representation form
numbers used to store variables. Integers in
range 0..65535 are represented as follows:
┌─────┬─────┬─────────────────── ───┬─────────────────────┬─────┐
│ 0 │ 0 │ low byte of a number │ high byte of a number │ 0 │
│ │ │ │ │ │
└─────┴─────┴─────────────────── ───┴─────────────────────┴─────┘
And integers in the range -65535..-1 - like this:
┌─────┬─────┬─────────────────── ───┬─────────────────────┬─────┐
│ 0 │ #FF │ low byte of the number │ high byte of the number │ 0 │
│ │ │(the number is represented in two's complement)│ │
└─────┴─────┴─────────────────── ───┴─────────────────────┴─────┘
Real numbers are stored in a more complex form. Information
You can learn about the format of their storage, for example, from [3].
I do not present them here, since working with such numbers is difficult.
assembly language is necessary only in very rare cases.
It is often possible to transform the problem being solved so that it works
only with integers, which makes calculations much faster.
Let's try now, armed with the knowledge we have gained,
"decrypt" the contents of the real variable area, dump
which is shown in Fig. 1.
┌───────────────────────┐
│pic_1.scr ││ │
│ │
│ │
│ │
│ │
│ │
└───────────────────────┘
Fig. 1
════════════════════════ ════════════════════════
Best regards, Ivan Roshchin.
From
Ivan Roshin
→
To
All
8 February 2002
Hello, All!
═══════════════════ call_asm.3 ══════════════════
The first byte of the variable area is #98. He belongs
range #81-#9A, therefore the first variable is numeric
array. As we remember, for a numeric array the name character code is
stored increased by #20. Let's define the name: #98-#20=#78, this is the code
character "x". The next two bytes contain the length of the array
(not counting the name byte and the two bytes of the length itself). After reading it
(#23=35) and moving forward the appropriate number of bytes, we
We can immediately move on to the next variable. But for now, do this
Let's not, but consider the structure of the array. In the next byte
2 is stored, which means the array is two-dimensional. According to the first dimension it
size is 2, according to the second - 3 (and in total, it means 6
elements). Next are the elements themselves. X(1,1)=4,
X(1,2)=0, subsequent elements are also zero.
The first byte of the next variable is #C1, therefore it is
a character array named A$... Well and so on; I hope with
you can figure out the rest of this variable area
on your own.
Procedure for determining the address of a variable
───────────────────────────────────────
To determine the location of a BASIC variable it is convenient
use the procedure below. Here are its inputs and
output parameters.
Input: DE points to variable name converted toin accordance with its type (i.e. the name must be in exactly this
form as it is stored in the variable area), ending
byte 0.
Output: if the variable is found, the Z flag is cleared, HL
points in the variable area to the first byte after the name
found variable, DE points to the next byte after 0
(which means if you store the names of the sought variables one after another,
then, first loading the address of the first name into DE and then
calling the procedure sequentially, we will sequentially
get the addresses of these variables). If the variable is not found -
the Z flag is set, DE remains unchanged.
The procedure, when running, changes the contents of register A.
VARS EQU #5C4B
FIND_VAR LD HL,(VARS)
PUSH BC
FIND_M0 PUSH DE
PUSH HL
;Comparing the name of the current variable with the name of the desired one:
FIND_M1 LD A,(DE)
INC DE
AND A
JR Z,FIND_YES ;names match
CP(HL)
INC HL
JR Z,FIND_M1
;The names do not match - we skip the current variable:
POP HL
POP DE
LD A,(HL)
INC HL
CP #80 ;Have you reached the end of the variable area?
JR Z,FIND_NO ;If yes, exit, flag Z is set.
CP#60
JR C,SKIP_M ;skip the character variable
CP#80
FIND_M4 LD BC,5JR C,SKIP_BC ;skip the numeric variable
;with a name of one letter
CP#A0
JR C,SKIP_M ;skip the numeric array
CP#C0
JR NC,FIND_M3
;Skip a numeric variable with a name of several characters:
FIND_M2 LD A,(HL)
INC HL
R.L.A.
JR NC,FIND_M2
JR FIND_M4
FIND_M3 CP #E0
LD C,18 ;B=0!
JR NC,SKIP_BC ;skip the loop variable
;Skipping a character variable or array:
SKIP_M LD C,(HL)
INC HL
LD B,(HL)
INC HL
SKIP_BC ADD HL,BC
JR FIND_M0
FIND_YES POP BC ;remove two now unnecessary numbers from the stack
POP B.C.
INC A ;A will become equal to 1, flag Z will be reset
FIND_NO POP BC
RET ;output
The BASIC interpreter performs a similar search every time
accessing any variable - here is one of the reasons
low speed of BASIC programs.
════════════════════════ ════════════════════════
Best regards, Ivan Roshchin.
From
Ivan Roshin
→
To
All
8 February 2002
Hello, All!
═══════════════════ call_asm.4 ══════════════════
Optimization example
──────────────────
Let's try to optimize for performance some
a simple BASIC program - for example, the one below.
10 LET N=0: INPUT A$
20 IF A$="" THEN GO TO 60
30 FOR I=1 TO LEN (A$)
40 IF A$(I)="X" THEN LET N=N+1
50 NEXT I
60 PRINT N
This program prompts the user for a string and
counts how many times the "X" character appears in it. Critical
in terms of performance, the area is obviously a loop in lines
30-50. Let's rewrite it in assembler. Let it be placed
Let's say from address 40000.
ORG 40000
LD DE,NAME_A ;look for variable A$
CALL FIND_VAR
;Now HL points to a 2-byte value defining
;length of string A$.
LD E,(HL)
INC HL
LD D,(HL)
INC HL
;DE is the length of the string, HL indicates the first character of the string.
LD BC,0 ;character counter "X"
M1 LD A,(HL)
CP "X"
JR NZ,M2
INC B.C.
M2 INC HL
DEC DE
LD A,D
OR E
JR NZ,M1
;BC - number of "X" characters in the line.
LD DE,NAME_N ;looking for variable N
CALL FIND_VAR
;Now HL points to the value of the variable N. Place it there
;contents of BC, represented in 5-byte form:LD(HL),0
INC HL
LD(HL),0
INC HL
LD(HL),C
INC HL
LD(HL),B
INC HL
LD(HL),0
RET ;output
NAME_A DB "a"-#20 ;variable name A$
DB 0
NAME_N DB "n" ;variable name N
DB 0
The result of the compilation will be written to the file "file1". After that
All that remains is to rewrite the original program:
5 CLEAR 39999: RANDOMIZE USR 15619: REM: LOAD "file1" CODE
10 LET N=0: INPUT A$
20 IF A$="" THEN GO TO 60
30 RANDOMIZE USR 40000
60 PRINT N
Let me remind you of the conditions that the called one must satisfy.
BASIC assembly procedure.
- maskable interrupts when running your procedure can
prohibited, but must be allowed before exiting. Please note
that when interrupts are disabled, the value does not increase
system timer - this may be important if you
use it, say, to calculate execution time
programs.
- the value of the register pair HL' when exiting the procedure should
be the same as at entry - equal to #2758. So what if
in the procedure you used HL' for some of your purposes,
before exiting, do not forget to enter the commands LD HL, #2758: EXX.
- the value of register pair IY should not change whenenabled interrupts, since it is used by the system
interrupt handling procedure. So if you need
use IY, disable interrupts first, and before
resolution, do not forget to restore the contents of IY (there
should be number #5C3A).
════════════════════════ ════════════════════════
Best regards, Ivan Roshchin.
From
Ivan Roshin
→
To
All
8 February 2002
Hello, All!
═══════════════════ call_asm.5 ══════════════════
Some useful tips
──────────────────────────
It is better to convert the values of numeric variables to
post-processing from a 5-byte form into a single or
double-byte, depending on the range: this way they can be
faster to process. If you need to work with arrays, and in
there is enough free space in memory, it is worth copying them to
addresses divisible by 256: this will speed up access to elements
such an array (read more about how to quickly work with
arrays, you can read in [4]).
Creating New Variables
─────────────────────────
It may be that in the rewritten program fragment
a value is assigned to a previously unused
variable. In this case, there is no information about it in the area
variables, and therefore we need to add it there. For this
must be allocated at the end of the variable area (between the end
last variable and byte #80) memory area required
length and enter the name and value of the new variable into it.
Memory area is allocated using the procedure
MAKE_ROOM, located in ROM at address #1655. When she calls
the HL register pair must contain the address from which
you need to allocate space, and in BC - the length of the allocated area.
The address of the allocated memory area is easy to determine iftake into account that directly behind the variable area there is
area of editable program lines, the beginning address of which
stored in the E_LINE system variable. Therefore, for
to obtain the desired address, just take the value E_LINE and
decrease it by 1.
Let's look at an example. Let there be a program like this:
10 INPUT A: INPUT B
20 LET C=A+B
30 PRINT C
and we need to rewrite line 20 in assembler. It is known that
variables A, B and C are integers and their values belong to the range
0..65535. In this case, the assembly text will be like this:
ORG 40000
MAKE_ROOM EQU #1655
E_LINE EQU 23641
LD DE,NAME_A
CALL FIND_VAR
;Now HL points to the value of variable A,
;DE points to NAME_B.
INC HL
INC HL
LD C,(HL)
INC HL
LD B,(HL)
;BC is the value of variable A.
CALL FIND_VAR
INC HL
INC HL
LD E,(HL)
INC HL
LD D,(HL)
;DE is the value of variable B.
EX DE,HL
ADD HL,BC ;calculated C
PUSH HL ;remembered on the stack
;Allocate space for variable C.
LD HL,(E_LINE)
DEC HL; where we select
PUSH HL
LD BC,6 ;how much we allocate
CALL MAKE_ROOM
POP HLLD (HL),"c" ;variable name
INC HL
POP DE ;popped from stack
LD (HL),0 ;variable value
INC HL ; and write it down
LD (HL),0 ;in 5-byte
INC HL ;form
LD(HL),E
INC HL
LD(HL),D
INC HL
LD(HL),0
RET ;output
NAME_A DB "a" ;variable name A
DB 0
NAME_B DB "b" ;variable name B
DB 0
Literature
──────────
1. "Basic ZX Spectrum". Moscow, VA PRINT, 1993.
2. "Secrets of ROM". "ZX-Review" 10/1991.
3. "ZX Spectrum personal computer. Programming
in machine codes and assembly language." Moscow, "Inforcom",
1993.
4. I. Roshchin. "Optimization using the example of intro "Start". "Radiomir.
Your computer" 7-10/2001.
════════════════════════ ════════════════════════
Best regards, Ivan Roshchin.
From
Ivan Roshin
→
To
All
8 February 2002
Hello, All!
═══════════════════ call_asm.1 ══════════════════
(c) Ivan Roshchin, Moscow
Fido: 2:5020/689.53
ZXNet: 500:95/462.53
E-mail: asder_ffc@softhome.net
WWW: http://www.ivr.da.ru
Passing parameters
═══════════════════
when calling assembly procedures from a BASIC program
════════════════════════════ ════════════════════════════
("Radiomir. Your computer" 10/2001)
Introduction
────────
After a BASIC program is written, often
it turns out that it works prohibitively slowly. Yes
several ways to solve this problem. You can completely
rewrite the program in assembler, achieving
maximum performance - but this will have to cost
a lot of time and effort. You can use the BASIC compiler - this
will not require any significant effort, but the program
will not be as fast as one written in assembly language, besides
There may also be problems with insufficient memory during compilation
or when trying to run a compiled program. It is possible
finally, rewrite in assembler only those sections of the program
on which most of the time is spent. At the same time
performance will be only slightly lower compared to full
rewriting the program in assembler, and the amount of work will be
much less. It is this method of optimization in a number ofcases turns out to be the most preferable.
When using assembly procedures, the question arises
passing parameters. In standard BASIC there are means for this
very modest: it is only stipulated that the called procedure
can return one value in register pair BC, representing
which is an integer in the range 0-65535. Let's say, when
executing the command
....
1000 LET A = USR 40000
....
the assembler procedure located at the address will be called
40000, after which variable A will be assigned the value
register pair BC.
You can select a specific area for transferring parameters
memory, write values there before calling the procedure
passed variables using the POKE operator, and then
call - read return values using operator
PEEK. Let, say, you need to pass the scaling procedure
coordinates located at address 40004, variables X and Y
(integers, in the range 0-65535) through memory area 40000-40003 (with
Address 40000 contains the value X, in the format minor
byte/high byte; with 40002 - Y value, in the same format).
The procedure will change these values in a certain way, leaving
the result is in the same memory cells; after that you need to read
returned values and assign them to variables X1 and Y1.
.....
1000 POKE 40000,X-INT(X/256)*256: POKE 40001,INT(X/256)1010 POKE 40002,Y-INT(Y/256)*256: POKE 40003,INT(Y/256)
1020 RANDOMIZE USR 40004
1030 LET X1=PEEK(40000)+PEEK(40001)*256
1040 LET Y1=PEEK(40002)+PEEK(40003)*256
.....
As we can see, with this method of passing parameters
The volume of the BASIC program increases, time is wasted on transmission
values (sometimes quite significant - for example, when transmitting
arrays), you have to remember which address is which
the variable is located, and, finally, it simply may not be enough
memory.
A more promising way seems to be when
an assembler program directly works with variables
BASIC. Let's consider this method in more detail.
BASIC Variables
──────────────────
Let's first consider what variables there are in BASIC, where
and in what format they are stored.
BASIC program variables are located in the memory area
the beginning of which is addressed to the VARS system variable,
located at #5C4B=23627. Variables are strictly different
after each other, and after the last of them there is byte #80 - a sign
end of the variable area.
Although variable names are allowed in both uppercase and lowercase
Latin letters, as well as spaces and numbers (a number cannot be
the first character), in the storage area for variable names
are converted to lowercase letters and spaces are not stored (so
Thus, for the interpreter the variables Size X and sizex are one and the samethe same), the "$" sign for symbolic variables is also not stored.
Below are the different types of variables and the format:
their storage (format data given in [1], frankly speaking,
do not differ in completeness and accuracy, so a lot had to be
study through practical experiments).
════════════════════════ ════════════════════════
Best regards, Ivan Roshchin.
From
Ivan Roshin
→
To
All
8 February 2002
Hello, All!
═══════════════════ call_asm.3 ══════════════════
The first byte of the variable area is #98. He belongs
range #81-#9A, therefore the first variable is numeric
array. As we remember, for a numeric array the name character code is
stored increased by #20. Let's define the name: #98-#20=#78, this is the code
character "x". The next two bytes contain the length of the array
(not counting the name byte and the two bytes of the length itself). After reading it
(#23=35) and moving forward the appropriate number of bytes, we
We can immediately move on to the next variable. But for now do this
Let's not, but consider the structure of the array. In the next byte
2 is stored, which means the array is two-dimensional. According to the first dimension it
size is 2, according to the second - 3 (and in total, it means 6
elements). Next are the elements themselves. X(1,1)=4,
X(1,2)=0, subsequent elements are also zero.
The first byte of the next variable is #C1, therefore it is
a character array named A$... And so on; I hope with
you can figure out the rest of this variable area
on your own.
Procedure for determining the address of a variable
───────────────────────────────────────
To determine the location of a BASIC variable it is convenient
use the procedure below. Here are its inputs and
output parameters.
Input: DE points to variable name converted toin accordance with its type (i.e. the name must be in exactly this
form as it is stored in the variable area), ending
byte 0.
Output: if the variable is found, the Z flag is cleared, HL
points in the variable area to the first byte after the name
found variable, DE points to the next byte after 0
(which means if you store the names of the sought variables one after another,
then, first loading the address of the first name into DE and then
calling the procedure sequentially, we will sequentially
get the addresses of these variables). If the variable is not found -
the Z flag is set, DE remains unchanged.
The procedure, when running, changes the contents of register A.
VARS EQU #5C4B
FIND_VAR LD HL,(VARS)
PUSH BC
FIND_M0 PUSH DE
PUSH HL
;Comparing the name of the current variable with the name of the desired one:
FIND_M1 LD A,(DE)
INC DE
AND A
JR Z,FIND_YES ;names match
CP(HL)
INC HL
JR Z,FIND_M1
;The names do not match - we skip the current variable:
POP HL
POP DE
LD A,(HL)
INC HL
CP #80 ;Have you reached the end of the variable area?
JR Z,FIND_NO ;If yes, exit, flag Z is set.
CP#60
JR C,SKIP_M ;skip the character variable
CP#80
FIND_M4 LD BC,5JR C,SKIP_BC ;skip the numeric variable
;with a name of one letter
CP#A0
JR C,SKIP_M ;skip the numeric array
CP#C0
JR NC,FIND_M3
;Skip a numeric variable with a name of several characters:
FIND_M2 LD A,(HL)
INC HL
R.L.A.
JR NC,FIND_M2
JR FIND_M4
FIND_M3 CP #E0
LD C,18 ;B=0!
JR NC,SKIP_BC ;skip the loop variable
;Skipping a character variable or array:
SKIP_M LD C,(HL)
INC HL
LD B,(HL)
INC HL
SKIP_BC ADD HL,BC
JR FIND_M0
FIND_YES POP BC ;remove two now unnecessary numbers from the stack
POP B.C.
INC A ;A will become equal to 1, flag Z will be reset
FIND_NO POP BC
RET ;output
The BASIC interpreter performs a similar search every time
accessing any variable - here is one of the reasons
low speed of BASIC programs.
════════════════════════ ════════════════════════
Best regards, Ivan Roshchin.
From
Ivan Roshin
→
To
All
8 February 2002
Hello, All!
═══════════════════ call_asm.4 ══════════════════
Optimization example
──────────────────
Let's try to optimize for performance some
a simple BASIC program - for example, the one below.
10 LET N=0: INPUT A$
20 IF A$="" THEN GO TO 60
30 FOR I=1 TO LEN (A$)
40 IF A$(I)="X" THEN LET N=N+1
50 NEXT I
60 PRINT N
This program prompts the user for a string and
counts how many times the "X" character appears in it. Critical
in terms of performance, the area is obviously a loop in lines
30-50. Let's rewrite it in assembler. Let it be placed
Let's say from address 40000.
ORG 40000
LD DE,NAME_A ;look for variable A$
CALL FIND_VAR
;Now HL points to a 2-byte value defining
;length of string A$.
LD E,(HL)
INC HL
LD D,(HL)
INC HL
;DE is the length of the string, HL indicates the first character of the string.
LD BC,0 ;character counter "X"
M1 LD A,(HL)
CP "X"
JR NZ,M2
INC B.C.
M2 INC HL
DEC DE
LD A,D
OR E
JR NZ,M1
;BC - number of "X" characters in the line.
LD DE,NAME_N ;looking for variable N
CALL FIND_VAR
;Now HL points to the value of the variable N. Place it there
;contents of BC, represented in 5-byte form:LD(HL),0
INC HL
LD(HL),0
INC HL
LD(HL),C
INC HL
LD(HL),B
INC HL
LD(HL),0
RET ;output
NAME_A DB "a"-#20 ;variable name A$
DB 0
NAME_N DB "n" ;variable name N
DB 0
The result of the compilation will be written to the file "file1". After that
All that remains is to rewrite the original program:
5 CLEAR 39999: RANDOMIZE USR 15619: REM: LOAD "file1" CODE
10 LET N=0: INPUT A$
20 IF A$="" THEN GO TO 60
30 RANDOMIZE USR 40000
60 PRINT N
Let me remind you of the conditions that the called one must satisfy.
BASIC assembly procedure.
- maskable interrupts when running your procedure can
prohibited, but must be allowed before exiting. Please note
that when interrupts are disabled, the value does not increase
system timer - this may be important if you
use it, say, to calculate execution time
programs.
- the value of the register pair HL' when exiting the procedure should
be the same as at entry - equal to #2758. So what if
in the procedure you used HL' for some of your purposes,
before exiting, do not forget to enter the commands LD HL, #2758: EXX.
- the value of register pair IY should not change whenenabled interrupts, since it is used by the system
interrupt handling procedure. So if you need
use IY, disable interrupts first, and before
resolution, do not forget to restore the contents of IY (there
should be number #5C3A).
════════════════════════ ════════════════════════
Best regards, Ivan Roshchin.
From
Ivan Roshin
→
To
All
8 February 2002
Hello, All!
═══════════════════ call_asm.5 ══════════════════
Some useful tips
──────────────────────────
It is better to convert the values of numeric variables to
post-processing from a 5-byte form into a single or
double-byte, depending on the range: this way they can be
faster to process. If you need to work with arrays, and in
there is enough free space in memory, it is worth copying them to
addresses divisible by 256: this will speed up access to elements
such an array (read more about how to quickly work with
arrays, you can read in [4]).
Creating New Variables
─────────────────────────
It may be that in the rewritten program fragment
a value is assigned to a previously unused
variable. In this case, there is no information about it in the area
variables, and therefore we need to add it there. For this
must be allocated at the end of the variable area (between the end
last variable and byte #80) memory area required
length and enter the name and value of the new variable into it.
Memory area is allocated using the procedure
MAKE_ROOM, located in ROM at address #1655. When she calls
the HL register pair must contain the address from which
you need to allocate space, and in BC - the length of the allocated area.
The address of the allocated memory area is easy to determine iftake into account that directly behind the variable area there is
area of editable program lines, the beginning address of which
stored in the E_LINE system variable. Therefore, for
to obtain the desired address, just take the value E_LINE and
decrease it by 1.
Let's look at an example. Let there be a program like this:
10 INPUT A: INPUT B
20 LET C=A+B
30 PRINT C
and we need to rewrite line 20 in assembler. It is known that
variables A, B and C are integers and their values belong to the range
0..65535. In this case, the assembly text will be like this:
ORG 40000
MAKE_ROOM EQU #1655
E_LINE EQU 23641
LD DE,NAME_A
CALL FIND_VAR
;Now HL points to the value of variable A,
;DE points to NAME_B.
INC HL
INC HL
LD C,(HL)
INC HL
LD B,(HL)
;BC is the value of variable A.
CALL FIND_VAR
INC HL
INC HL
LD E,(HL)
INC HL
LD D,(HL)
;DE is the value of variable B.
EX DE,HL
ADD HL,BC ;calculated C
PUSH HL ;remembered on the stack
;Allocate space for variable C.
LD HL,(E_LINE)
DEC HL; where we select
PUSH HL
LD BC,6 ;how much we allocate
CALL MAKE_ROOM
POP HLLD (HL),"c" ;variable name
INC HL
POP DE ;popped from stack
LD (HL),0 ;variable value
INC HL ; and write it down
LD (HL),0 ;in 5-byte
INC HL ;form
LD(HL),E
INC HL
LD(HL),D
INC HL
LD(HL),0
RET ;output
NAME_A DB "a" ;variable name A
DB 0
NAME_B DB "b" ;variable name B
DB 0
Literature
──────────
1. "Basic ZX Spectrum". Moscow, VA PRINT, 1993.
2. "Secrets of ROM". "ZX-Review" 10/1991.
3. "ZX Spectrum personal computer. Programming
in machine codes and assembly language." Moscow, "Inforcom",
1993.
4. I. Roshchin. "Optimization using the example of intro "Start". "Radiomir.
Your computer" 7-10/2001.
════════════════════════ ════════════════════════
Best regards, Ivan Roshchin.