Автор: Stewart Nicholls
Год: 1983
Издатели: Your Computer
Языки:
Английский
Формат:
TZX лента
Требования:
ZX Spectrum 16K
Ссылки:
Страница на ZXArt
Страница на World Of Spectrum
Страница на Spectrum Computing
Скриншоты:
Год: 1983
Издатели: Your Computer
Языки:
Формат:
Требования:
Ссылки:
Скриншоты:
Spectrum MC/Basic
Sorting Variables
If you find that setting the parameters
of a machine-code routine is a bit of a
drag, Stewart Nicholls' program could
give you the best of both worlds.
Do you get tired of having to set parameters of a machine-
code routine by poking numerous addresses with the neces-
sary values? If you do, the following machine-code routine
is the answer: it makes poking completely unnecessary.
The problem is avoided by setting up the values using
Basic variables and then searching the variables area to
find their value and storing them in the spare bytes of the
printer buffer.
To demonstrate the technique, I have attached the search
program to a routine which prints a message anywhere on the
screen using characters of any height or width, in any ink
colour.
The x and y variables are used for the start of the mes-
sage; since these are the co-ordinates of the top-left
corner of the first character of the message the usual
Spectrum plot positions apply. That is, 0,0 is the bottom
left-hand corner of the screen. For the height of the cha-
racters, h is used. Normal height is 1, 22 will be full
screen. Width of characters is 2. Normal width is 1, 32 is
full screen for one character.
The string containing the message to be printed will be
a$. The only limitations on the message are that it can
only contain characters from code 32 - space - to code 127
- copyright - and that the number of characters multiplied
by width of character should not exceed 23 with the plot
position at the left-hand edge of screen. If this is ex-
ceeded the program will not crash but the message will wrap
round the screen overprinting as it goes.
The program is fully mug-trapped and any wrong parameter
of x and y will be corrected before plotting the string.
The machine code to find the variables x, y, h, w and a$ is
shown in listing 1. [Listing 1 was a code loader, "large"
on the TZX. This loaded (and tested) the machine code de-
scribed, which is on the TZX as "largecode".]
If you read through Chapter 24 of the Sinclair Manual,
you will see that there are six types of variable, namely:
a number whose name is one letter - type (i); a number
whose name is two or more letters - type (ii); an array of
numbers - type (iii); FOR-NEXT loop control - type (iv);
string variable - type (v); an array of characters - type
(vi).
Each of these variables is uniquely identified by its
first byte.
Variable type i = 96d + (letter code - 96d)
Variable type ii = 160d + (first letter code - 96d)
Variable type iii = 128d + (letter code - 96d)
Variable type iv = 224d + (letter code - 96d)
Variable type v = 64d + (letter code - 96d)
Variable type vi = 192d + (letter code - 96d)
The variables we will be searching for will be types (i)
and (v).
If we take, for example, the variable x which has a cha-
racter code of 120, then using the formula (i) given, its
unique first byte is 120. So in order to find variable x we
must search through the variables for this unique code,
which will then point to the start of the x variable infor-
mation. This is not just a simple case of running through
the variables one byte at a time using a CPIR instruction,
as this may find the code 120 held in a string or an array
of characters. So we must find the start of each variable,
check if it is the one we want, and if not, then jump to
the start of the next variable. This means that we must
identify the type of variable found before we know how far
to jump to the next one. This is not as difficult a task as
it might first appear because of the way in which the
Spectrum stores the variable parameters.
With three of the variables, namely array of numbers,
array of character and strings, the length of the variable
is held in the two bytes following the unique byte code.
What is more helpful, bit 5 of the unique code is zero and
the remaining three variables have bit 5 set at one.
The length of the three remaining variables can be calcu-
lated by this method:
Type (i) length = 6 bytes including unique code.
Type (ii) length = number of letters of variable + 5 bytes.
Type (iv) length = 19 bytes including unique code.
We now have the information to enable us to jump over each
type of variable, and the checks must be made in the follo-
wing order: first, check if the code is 120; if yes then
return from routine; check bit 5 and if zero jump by
value in next two bytes + 1 : GOTO 1
Check bit 6 and if zero check following bytes for
BIT 7 = 1
that is the last character of the variable, then jump six
bytes and Goto 1. Check but 7 and if zero then jump 6 bytes
and Goto 1. Now it must be a FOR/NEXT loop so jump 19 bytes
and Goto 1.
The mnemonics of the machine-code to do this are shown in
the Find subroutine. This uses address 23728 to hold the
unique code of the variable for which we are searching. The
HL register holds the address being checked, and the accu-
mulator is loaded with the unique code from address 23728.
The subroutine only takes 46 bytes to check, jump and
locate the start address of any variable.
Now that we have found the start address of our variable
x we must find its value. You will see that the value for
whole numbers is held in the third and fourth bytes follo-
wing the unique code. In our case we are only interested in
numbers from 0 to 255 for x. So we can ignore the high byte
and store the low byte, that is the third byte. This
applies to all our variables x, y, h and w.
With this in mind a subroutine, Setup, can be assembled
to move along three bytes from the address held in HL and
then store the value held in this address in the printer
buffer area of memory. So we now have the means of finding
and storing values of x, y, h and w in addresses 23296/7/8
and 9.
We can now move to the slightly more complicated string
parameters. In our case we need to find a$, unique code 65.
The same Find routine can be used to find the start of the
variable. Once found we then move to the next two bytes to
find the LEN of the string as follows:
INC HL
LD E,(HL)
INC HL
LD D,(HL)
This will put the length of the string into the DE regis-
ter, and, as we can safely limit the length of the string
to 255 characters we only need to store the number in low
byte:
LD (23300),DE
The number in address 23301 will be overwritten with the
first character in the string:
PUSH DE Get number of characters into BC
POP BC
INC HL Set HL at start of characters
LD DE,23301
LDIR Transfer information
The above will transfer the string characters from the
variables to the printer buffer starting at address 23301.
With this routine we now have x, y, h, w, length of
string, and characters in string stored consecutively in
the printer buffer, and can go straight into the routine to
plot the string based on these parameters.
The plot routine finds the start of the eight bytes for
each character in the character and, for each byte, a
Rotate Left instruction is carried out either plotting or
unplotting depending whether Carry is set or not. Again use
is made of the two unused bytes in the Systems Variables
23728/9 to hold and update the x,y plot positions for each
character.
If the y plot position goes below zero, then it is reset
to 175 to give a wrap around effect and likewise if the x
position goes above 255 it is reset to zero. So now we have
a machine-code program than can be called from Basic with
no POKEs in sight - for example:
10 LET x=0: LET y=0: LET h=8: LET w=4:
LET a$="Finished": INK 6: RANDOMIZE USR 32393
This saves 13 POKEs including LEN a$. Listing 1 ["large"]
shows the Basic program to set up the machine code above
RAMTOP. On a 16K Spectrum this is immediately before the
user-defined graphics and occupies 255 bytes. Once the
machine-code is entered the Basic can be NEWed and the code
saved in the usual way.
There are a couple of points to watch with this program.
First, ensude that all the variables are defined before a
call is made to the routine as failure to find a variable
will crash the program. Second, do not use x, y, h or w as
a control variable in a FOR-NEXT loop because the Spectrum
will then delete the simple variable and use the FOR-NEXT
loop to hold further values of x, y, h and w. This can be
demonstrated by the simple Basic program:
10 FOR a=1 TO 10
20 PRINT a;
30 NEXT a
40 LET a=3
50 NEXT a
This program will produce a continuous loop resetting a to
3 and jumping back into the FOR-NEXT loop showing that the
variable - a - is line 40 is part of the FOR-NEXT loop
control variable. If this was not the case the Spectrum
would give an error report:
1 NEXT without FOR, 50:1
You can have a small Basic subroutine to work out the
width of characters and the start plot position to give
information printed centrally on any line; such a program
is given in listing 2 with sample printout. [This is called
"Plot" on the TZX, with one line added to auto-load the
machine code.]
We can now tackle a machine-code program to print out all
the variables used after a program has been run. Note that
this may not be all the variables in the listing as when a
program is run certain subroutines may not have been called
and so variables held in those routines will not have been
placed into the variables area.
This program - listing 3 - could be of use in debugging
Basic programs. Listing 3 is in the form of a machine-code
dump which should be held in DATA statements as listing 1,
and poked into a memory position of your choice. There are
no Jump or Call commands to within the program. I find it
handy to have two versions of the program, one stored above
RAMTOP and another in a line 1 REM statement containing 331
zeros and called using
RANDOMIZE USR 23760
The machine-code is then held in the Basic area of RAM and
can be merged with any program - as long as the program
does not have a line 1, which will be overwritten. One
point to note when saving the Basic REM statement on tape
is to use CLEAR to erase all variables used in the machine-
code loader, otherwise these will be saved and merged along
with the REM statement. This is good practice on any pro-
gram which does not require the variables to be saved.
[This code, saved as a CODE file ready to be loaded any-
where in memory, including into a REM statement should you
want to, is on the TZX as "variables".]
Listing 4 is a demonstration program setting up varia-
bles, and shows a screen copy of the machine-code output
which lists these variables under their variable types.
Notice how the Spectrum converts all variables to lower
case and also that variables a, h, l, o, w, x and z have
been listed under the FOR-NEXT loop only and not under
number. [This is on the TZX as "Basicvars".]
Listing 5 shows the machine-code mnemonics for assembly
into a REM statement, for anyone who wishes to check
through the program to see how it works. The first section
from addresses 5CD0 to 5CE5 sets up the border, paper and
ink colours; from 5Ce6 to 5D46 prints the heading. The
remainder of the program checks the variables one by one
finding the type of variable, locating the next print posi-
tion in that variables column, updating the print position
and printing the variable.
The section from 5D94 to 5DBE is interesting in that it
checks for a column reaching the bottom of the screen and
if it does then waits for a key press before scrolling 21
lines, leaving the heading on the screen and printing the
next variable in position. A return to Basic is only made
when all the variables have been listed.
[ As listing 5 provided nothing more than a bare assembly
dump of the code (with a typo, to boot), I haven't copied
it. Listings 1 and 2, however, were accompanied by (unnum-
bered) hex listings with labels (which are referred to in
the article). The order was not quite rational, but sorted
by address, these listings were as follows: ]
Start address for 16K machine code 32235
ORG 32235
Subroutine to find variables
FIND LD HL, (23627)
START: LD A, (23728)
CP (HL)
RET Z
BIT 5, (HL)
JR NZ, NEXT
INC HL
LD E, (HL)
INC HL
LD D, (HL)
ADD HL, DE
INC HL
JR START
NEXT: BIT 6, (HL)
JR NZ, NEXT1
LOOP5: INC HL
LD A, (HL)
BIT 7, A
JR Z, START
LOOP6: LD DE, 6
ADD HL, DE
JR START
NEXT1: BIT 7, (HL)
JR Z, LOOP6
LD DE, 19
ADD HL, DE
JR START
Subroutine to set parameters of variable to be found
SETUP LD (23728), A
CALL FIND
INC HL
INC HL
INC HL
LD A, (HL)
LD (BC), A
RET
Start of machine-code routine
RUN LD BC, 23296
LD A, 120
CALL SETUP
INC BC
LD A, 121
CALL SETUP
INC BC
LD A, 104
CALL SETUP
INC BC
LD A, 119
CALL SETUP
LD A, 65
LD (23728), A
CALL FIND
INC HL
LD E, (HL)
INC HL
LD D, (HL)
LD (23300), DE
PUSH DE
POP BC
INC HL
LD DE, 23301
LDIR
Routine to plot the string
LD HL, (23296)
XOR A
LD A, H
SBC A, 176
JR C, YES
LD H, A
LD (23296), HL
YES: LD (23728), HL
LD HL, 23301
RUN1: PUSH HL
LD A, (HL)
LD H, 0
LD L, A
ADD HL, HL
ADD HL, HL
ADD HL, HL
LD DE, 15360
ADD HL, DE
LD B, 8
LOOP4: PUSH BC
LD BC, (23297)
LOOP3: LD A, (HL)
PUSH HL
PUSH BC
LD B, 8
LOOP2: PUSH BC
RLA
PUSH AF
JP C, PLOT
LD HL, (23299)
LD A, (23728)
ADD A, L
LD (23728), A
JP 32527
PLOT: LD BC, (23298)
LOOP1: PUSH BC
LD BC, (23728)
PUSH BC
CALL 22E5H
POP BC
INC C
LD (23728), BC
POP BC
DJNZ LOOP1
END: POP AF
POP BC
DJNZ LOOP2
LD A, (23296)
LD HL, 23728
LD (HL), A
INC HL
XOR A
LD A, (HL)
SBC A, 176
JR C, OK
LD (HL), A
JR CONT
OK: LD A, (HL)
CP 0
JR NZ, OK1
LD (HL), 176
OK1: DEC (HL)
CONT: POP BC
POP HL
DJNZ LOOP3
INC HL
POP BC
DJNZ LOOP4
LD A, (23299)
ADD A, A
ADD A, A
ADD A, A
LD L, A
LD A, (23728)
ADD A, L
LD (23296), A
LD (23728), A
LD A, (23297)
LD (23729), A
POP HL
INC HL
LD A, (23300)
DEC A
RET Z
LD (23300), A
JP RUN1
Sorting Variables
If you find that setting the parameters
of a machine-code routine is a bit of a
drag, Stewart Nicholls' program could
give you the best of both worlds.
Do you get tired of having to set parameters of a machine-
code routine by poking numerous addresses with the neces-
sary values? If you do, the following machine-code routine
is the answer: it makes poking completely unnecessary.
The problem is avoided by setting up the values using
Basic variables and then searching the variables area to
find their value and storing them in the spare bytes of the
printer buffer.
To demonstrate the technique, I have attached the search
program to a routine which prints a message anywhere on the
screen using characters of any height or width, in any ink
colour.
The x and y variables are used for the start of the mes-
sage; since these are the co-ordinates of the top-left
corner of the first character of the message the usual
Spectrum plot positions apply. That is, 0,0 is the bottom
left-hand corner of the screen. For the height of the cha-
racters, h is used. Normal height is 1, 22 will be full
screen. Width of characters is 2. Normal width is 1, 32 is
full screen for one character.
The string containing the message to be printed will be
a$. The only limitations on the message are that it can
only contain characters from code 32 - space - to code 127
- copyright - and that the number of characters multiplied
by width of character should not exceed 23 with the plot
position at the left-hand edge of screen. If this is ex-
ceeded the program will not crash but the message will wrap
round the screen overprinting as it goes.
The program is fully mug-trapped and any wrong parameter
of x and y will be corrected before plotting the string.
The machine code to find the variables x, y, h, w and a$ is
shown in listing 1. [Listing 1 was a code loader, "large"
on the TZX. This loaded (and tested) the machine code de-
scribed, which is on the TZX as "largecode".]
If you read through Chapter 24 of the Sinclair Manual,
you will see that there are six types of variable, namely:
a number whose name is one letter - type (i); a number
whose name is two or more letters - type (ii); an array of
numbers - type (iii); FOR-NEXT loop control - type (iv);
string variable - type (v); an array of characters - type
(vi).
Each of these variables is uniquely identified by its
first byte.
Variable type i = 96d + (letter code - 96d)
Variable type ii = 160d + (first letter code - 96d)
Variable type iii = 128d + (letter code - 96d)
Variable type iv = 224d + (letter code - 96d)
Variable type v = 64d + (letter code - 96d)
Variable type vi = 192d + (letter code - 96d)
The variables we will be searching for will be types (i)
and (v).
If we take, for example, the variable x which has a cha-
racter code of 120, then using the formula (i) given, its
unique first byte is 120. So in order to find variable x we
must search through the variables for this unique code,
which will then point to the start of the x variable infor-
mation. This is not just a simple case of running through
the variables one byte at a time using a CPIR instruction,
as this may find the code 120 held in a string or an array
of characters. So we must find the start of each variable,
check if it is the one we want, and if not, then jump to
the start of the next variable. This means that we must
identify the type of variable found before we know how far
to jump to the next one. This is not as difficult a task as
it might first appear because of the way in which the
Spectrum stores the variable parameters.
With three of the variables, namely array of numbers,
array of character and strings, the length of the variable
is held in the two bytes following the unique byte code.
What is more helpful, bit 5 of the unique code is zero and
the remaining three variables have bit 5 set at one.
The length of the three remaining variables can be calcu-
lated by this method:
Type (i) length = 6 bytes including unique code.
Type (ii) length = number of letters of variable + 5 bytes.
Type (iv) length = 19 bytes including unique code.
We now have the information to enable us to jump over each
type of variable, and the checks must be made in the follo-
wing order: first, check if the code is 120; if yes then
return from routine; check bit 5 and if zero jump by
value in next two bytes + 1 : GOTO 1
Check bit 6 and if zero check following bytes for
BIT 7 = 1
that is the last character of the variable, then jump six
bytes and Goto 1. Check but 7 and if zero then jump 6 bytes
and Goto 1. Now it must be a FOR/NEXT loop so jump 19 bytes
and Goto 1.
The mnemonics of the machine-code to do this are shown in
the Find subroutine. This uses address 23728 to hold the
unique code of the variable for which we are searching. The
HL register holds the address being checked, and the accu-
mulator is loaded with the unique code from address 23728.
The subroutine only takes 46 bytes to check, jump and
locate the start address of any variable.
Now that we have found the start address of our variable
x we must find its value. You will see that the value for
whole numbers is held in the third and fourth bytes follo-
wing the unique code. In our case we are only interested in
numbers from 0 to 255 for x. So we can ignore the high byte
and store the low byte, that is the third byte. This
applies to all our variables x, y, h and w.
With this in mind a subroutine, Setup, can be assembled
to move along three bytes from the address held in HL and
then store the value held in this address in the printer
buffer area of memory. So we now have the means of finding
and storing values of x, y, h and w in addresses 23296/7/8
and 9.
We can now move to the slightly more complicated string
parameters. In our case we need to find a$, unique code 65.
The same Find routine can be used to find the start of the
variable. Once found we then move to the next two bytes to
find the LEN of the string as follows:
INC HL
LD E,(HL)
INC HL
LD D,(HL)
This will put the length of the string into the DE regis-
ter, and, as we can safely limit the length of the string
to 255 characters we only need to store the number in low
byte:
LD (23300),DE
The number in address 23301 will be overwritten with the
first character in the string:
PUSH DE Get number of characters into BC
POP BC
INC HL Set HL at start of characters
LD DE,23301
LDIR Transfer information
The above will transfer the string characters from the
variables to the printer buffer starting at address 23301.
With this routine we now have x, y, h, w, length of
string, and characters in string stored consecutively in
the printer buffer, and can go straight into the routine to
plot the string based on these parameters.
The plot routine finds the start of the eight bytes for
each character in the character and, for each byte, a
Rotate Left instruction is carried out either plotting or
unplotting depending whether Carry is set or not. Again use
is made of the two unused bytes in the Systems Variables
23728/9 to hold and update the x,y plot positions for each
character.
If the y plot position goes below zero, then it is reset
to 175 to give a wrap around effect and likewise if the x
position goes above 255 it is reset to zero. So now we have
a machine-code program than can be called from Basic with
no POKEs in sight - for example:
10 LET x=0: LET y=0: LET h=8: LET w=4:
LET a$="Finished": INK 6: RANDOMIZE USR 32393
This saves 13 POKEs including LEN a$. Listing 1 ["large"]
shows the Basic program to set up the machine code above
RAMTOP. On a 16K Spectrum this is immediately before the
user-defined graphics and occupies 255 bytes. Once the
machine-code is entered the Basic can be NEWed and the code
saved in the usual way.
There are a couple of points to watch with this program.
First, ensude that all the variables are defined before a
call is made to the routine as failure to find a variable
will crash the program. Second, do not use x, y, h or w as
a control variable in a FOR-NEXT loop because the Spectrum
will then delete the simple variable and use the FOR-NEXT
loop to hold further values of x, y, h and w. This can be
demonstrated by the simple Basic program:
10 FOR a=1 TO 10
20 PRINT a;
30 NEXT a
40 LET a=3
50 NEXT a
This program will produce a continuous loop resetting a to
3 and jumping back into the FOR-NEXT loop showing that the
variable - a - is line 40 is part of the FOR-NEXT loop
control variable. If this was not the case the Spectrum
would give an error report:
1 NEXT without FOR, 50:1
You can have a small Basic subroutine to work out the
width of characters and the start plot position to give
information printed centrally on any line; such a program
is given in listing 2 with sample printout. [This is called
"Plot" on the TZX, with one line added to auto-load the
machine code.]
We can now tackle a machine-code program to print out all
the variables used after a program has been run. Note that
this may not be all the variables in the listing as when a
program is run certain subroutines may not have been called
and so variables held in those routines will not have been
placed into the variables area.
This program - listing 3 - could be of use in debugging
Basic programs. Listing 3 is in the form of a machine-code
dump which should be held in DATA statements as listing 1,
and poked into a memory position of your choice. There are
no Jump or Call commands to within the program. I find it
handy to have two versions of the program, one stored above
RAMTOP and another in a line 1 REM statement containing 331
zeros and called using
RANDOMIZE USR 23760
The machine-code is then held in the Basic area of RAM and
can be merged with any program - as long as the program
does not have a line 1, which will be overwritten. One
point to note when saving the Basic REM statement on tape
is to use CLEAR to erase all variables used in the machine-
code loader, otherwise these will be saved and merged along
with the REM statement. This is good practice on any pro-
gram which does not require the variables to be saved.
[This code, saved as a CODE file ready to be loaded any-
where in memory, including into a REM statement should you
want to, is on the TZX as "variables".]
Listing 4 is a demonstration program setting up varia-
bles, and shows a screen copy of the machine-code output
which lists these variables under their variable types.
Notice how the Spectrum converts all variables to lower
case and also that variables a, h, l, o, w, x and z have
been listed under the FOR-NEXT loop only and not under
number. [This is on the TZX as "Basicvars".]
Listing 5 shows the machine-code mnemonics for assembly
into a REM statement, for anyone who wishes to check
through the program to see how it works. The first section
from addresses 5CD0 to 5CE5 sets up the border, paper and
ink colours; from 5Ce6 to 5D46 prints the heading. The
remainder of the program checks the variables one by one
finding the type of variable, locating the next print posi-
tion in that variables column, updating the print position
and printing the variable.
The section from 5D94 to 5DBE is interesting in that it
checks for a column reaching the bottom of the screen and
if it does then waits for a key press before scrolling 21
lines, leaving the heading on the screen and printing the
next variable in position. A return to Basic is only made
when all the variables have been listed.
[ As listing 5 provided nothing more than a bare assembly
dump of the code (with a typo, to boot), I haven't copied
it. Listings 1 and 2, however, were accompanied by (unnum-
bered) hex listings with labels (which are referred to in
the article). The order was not quite rational, but sorted
by address, these listings were as follows: ]
Start address for 16K machine code 32235
ORG 32235
Subroutine to find variables
FIND LD HL, (23627)
START: LD A, (23728)
CP (HL)
RET Z
BIT 5, (HL)
JR NZ, NEXT
INC HL
LD E, (HL)
INC HL
LD D, (HL)
ADD HL, DE
INC HL
JR START
NEXT: BIT 6, (HL)
JR NZ, NEXT1
LOOP5: INC HL
LD A, (HL)
BIT 7, A
JR Z, START
LOOP6: LD DE, 6
ADD HL, DE
JR START
NEXT1: BIT 7, (HL)
JR Z, LOOP6
LD DE, 19
ADD HL, DE
JR START
Subroutine to set parameters of variable to be found
SETUP LD (23728), A
CALL FIND
INC HL
INC HL
INC HL
LD A, (HL)
LD (BC), A
RET
Start of machine-code routine
RUN LD BC, 23296
LD A, 120
CALL SETUP
INC BC
LD A, 121
CALL SETUP
INC BC
LD A, 104
CALL SETUP
INC BC
LD A, 119
CALL SETUP
LD A, 65
LD (23728), A
CALL FIND
INC HL
LD E, (HL)
INC HL
LD D, (HL)
LD (23300), DE
PUSH DE
POP BC
INC HL
LD DE, 23301
LDIR
Routine to plot the string
LD HL, (23296)
XOR A
LD A, H
SBC A, 176
JR C, YES
LD H, A
LD (23296), HL
YES: LD (23728), HL
LD HL, 23301
RUN1: PUSH HL
LD A, (HL)
LD H, 0
LD L, A
ADD HL, HL
ADD HL, HL
ADD HL, HL
LD DE, 15360
ADD HL, DE
LD B, 8
LOOP4: PUSH BC
LD BC, (23297)
LOOP3: LD A, (HL)
PUSH HL
PUSH BC
LD B, 8
LOOP2: PUSH BC
RLA
PUSH AF
JP C, PLOT
LD HL, (23299)
LD A, (23728)
ADD A, L
LD (23728), A
JP 32527
PLOT: LD BC, (23298)
LOOP1: PUSH BC
LD BC, (23728)
PUSH BC
CALL 22E5H
POP BC
INC C
LD (23728), BC
POP BC
DJNZ LOOP1
END: POP AF
POP BC
DJNZ LOOP2
LD A, (23296)
LD HL, 23728
LD (HL), A
INC HL
XOR A
LD A, (HL)
SBC A, 176
JR C, OK
LD (HL), A
JR CONT
OK: LD A, (HL)
CP 0
JR NZ, OK1
LD (HL), 176
OK1: DEC (HL)
CONT: POP BC
POP HL
DJNZ LOOP3
INC HL
POP BC
DJNZ LOOP4
LD A, (23299)
ADD A, A
ADD A, A
ADD A, A
LD L, A
LD A, (23728)
ADD A, L
LD (23296), A
LD (23728), A
LD A, (23297)
LD (23729), A
POP HL
INC HL
LD A, (23300)
DEC A
RET Z
LD (23300), A
JP RUN1