CODING
Interesting text from Ivan Roshchin for code-
ditch Wishes, suggestions, objections,
send to my address: 2:5020/689.53
November 20, 98 Alex Letaev
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
(c) Ivan Roshchin, Moscow, 11/13/1998
Fido : 2:5020/689.53
E-mail: asder_ffc@softhome.net
+---------------------------------+
| Undocumented feature |
| Z80 processor |
+---------------------------------+
Permission is granted to freely distribute this
articles provided that no amendments are made
changes and preservation of my copyright.
1. Where it all started
----------------------
I once wrote the next version of the program
BestView (v2.4), and used it here
this fragment:
....
EI
CALL SUBR1
HALT
....
SUBR1 LD A,R
PUSH AF
D.I.
....
POP AF
DI
RET RO
EI
RET
In this fragment there is a call to the procedure
ry SUBR1, which during its operation
disables interrupts, and when exiting, restores
restores their previous mode of operation.Checking whether they are allowed or not
interrupt when calling a procedure, and restore
The interrupt mode changes as follows:
in the following way:
-the command LD A,R enters the flag P/V sos-
interruption trigger IFF2 waiting;
-register pair AF is stored on the stack
(PUSH AF);
-interrupts (DI) are disabled;
-actually, those functions are performed for
cat and the SUBR1 procedure was intended;
-AF contents are restored (POP AF);
-interrupts are disabled (DI);
-if the P/V flag is cleared, exit occurs
from a procedure with disabled interrupts
(RET RO);
-otherwise exit occurs with allowed
interrupts (EI: RET).
I began to notice that when working this
BestView fragment freezes - not always, and
not even too often, but very rarely
cases. But it still wasn't very good
nice. The program did not seem to contain
no errors, at least from the first
I didn’t notice anything suspicious in my gaze
till. All that remained was to resort to more
strong means...
2. The situation is starting to clear up
--------------------------------
After another freeze, I inserted the number
th disk and confidently pressed the MAGIC button.
Then I loaded the debugger "STS 6.2 +@" (not
in vain I remade it - now with his
power after downloading the @-file, you can restore
update the contents of processor registers to
the moment the program is reset to disk). Pressing
a couple of keys - and now I see where
The program froze.
....
EI
CALL SUBR1
HALT <------------ here!
....
A typical case is that interrupts are disabled, and
The processor has stopped executing the program
on the HALT command. But why are the interruptions
are prohibited - unclear. After all, in front of you-
they were resolved by the call of procedure SUBR1
by the EI team, and after finishing the work
SUBR1 they should also be allowed -
SUBR1 procedure should not affect
influence on their operating mode.
I'm tracing SUBR1. Everything is going as expected - and
at input and at output interruptions remain -
Xia permitted. I repeat the trace:
once, twice...tenth. So far, so good.
Or maybe the fact is that in SUBR1
is something going on with the stack? And because of this
sometimes the co-
held AF? We should check...
3. Freezes: take two
--------------------------
Well, I remade the program. Now
I'll know exactly what's going on:
SUBR1 LD A,R
PUSH AF
DI
PUSH HL
PUSH AF
POP HL
LD (WR_НН1),HL
POP HL
....
POP AF
PUSH HL
PUSH AF
POP HL
LD (WR_НН2),HL
POP HL
DI
RET RO
EI
RET
WR_НН1 DW 0
WR_НН2 DW 0
AF content is now remembered not only
on the stack, but also in the variable WR_НН1 (for
control), and when exiting the procedure -
The value popped from the stack is stored in
WR_НН2. If the procedure works correctly,
WR_НН1 and WR_НН2 must match, and the flag
P/V to be set.
I’m launching... BestView has been working for a minute now
melts normally... I look through it from her
with the power of the same file, when viewing it
Why did it freeze last time... Well,
again! And it’s not surprising, because I’m the reason
didn't eliminate it. Okay, we'll figure it out.
I press MAGIC again, load "STS 6.2 +@"
andI immediately check the values of WR_НН1 and
WR_НН2. Both there and there #5908 is recorded. Know-
the values coincide - therefore, when working
those with a stack of errors did not occur. But if in re-
The flag registry contains #08 - which means
the P/V flag is reset, and when calling the procedure
SUBR1 interrupts were disabled.
But this is completely impossible! After all, in
The program costs EI: CALL SUBR1! Probably
it's just that the Spectrum overheated, and that's why
glitches. Nothing smarter than me this day
I never came up with it.
4. False trail
--------------
The next day I found a possible explanation
oblivion to the mysterious prohibition of interrupts.
Let’s say after the EI command, but before execution
An interrupt occurred during the command LD A,R.
As is known, the procedure for its processing is
must end with EI:RET commands (because
that at the beginning of processing occurs automatically
tically disable interrupts). If
the interrupt handler simply terminates
RET command, then the interrupts will remain recorded
resolved.
Of course, the likelihood that an interruption
will happen between teams EI and LD
A,Rvery small, but freezes also occur
they go very rarely. So this is one more time
confirmed my hypothesis.
However, it remained unclear why
it would be the interrupt handler ending
with the RET command, not EI:RET. I decided to check
wondering if this is really the point, and
for this purpose I added the HALT command after EI
(see below). If the interrupt handler
actually terminates incorrectly, then
after the added HALT interruption is always
may be prohibited, and, accordingly,
BestView will always freeze.
....
EI
HALT <------ added command
CALL SUBR1
HALT
....
SUBR1 LD A,R
PUSH AF
D.I.
....
POP AFD.I.
RET RO
EI
RET
I compile, run... Completely unexpected
This result is completely freezing
stopped! I wanted to leave it like that, but
nevertheless, I decided to figure out why it was so half-
is expected.
5. "Program simplification" technique
------------------------------
When you can't find an error using normal means
succeeds, I delete everything from the program
possible, but so that the error remains
las. As a result, when what remains of the program
about a dozen lines, the error is immediately noticeable. Yes
I did this time too:
ORG #6000
EI
M1 CALL SUBR1
JR M1
SUBR1 LD A,R
DI
JP PO,M2
EI
RET
M2 LD A,4
OUT(254),A
RET
Here is a program, only 19 bytes. Allow
interruptions occur, and in an endless loop
procedure SUBR1 is called. This procedure
sets green border if
interrupts were disabled when entering it, and
does not change border color if interrupted
allowed. Thus, if it happens
spontaneous disabling of interrupts,
it will be immediately noticeable.
I run it - yes, the border changes its color to
green. The reason for such strange behavior
program remains unknown. Maybe
be, the processing procedure is to blame
type 1 interrupts? Adding to the program
several commands that set IM mode
2 with a handler consisting of only two
commands: EI:RET.
ORG #6000
LD HL,#8000
LD (HL),#81
LD DE,#8001
LD ВС,#100
LDIR
LD A,#80
LD I,A
IM 2
EI
M1 CALL SUBR1
JR M1
SUBR1 LD A,R
DI
JP РО,M2
EI
RET
M2 LD A,4
OUT (254),A
RET
ORG #8181
EIRET
I run it - the same result! Although when
trace of both this and the previous program
in the debugger the border remains black. Attention
careful study of the program leads to
guess: maybe the command LD A,R
sometimes sets the P/V bit as if
then interrupts are disabled, while on
Are they actually allowed?
6. Is there a bug in the processor?
-------------------------------
I'm changing the program again. Now interrupt-
tions will not be prohibited at all (removed
DI command). If when executing the command
LD A,R bit P/V will become 0, at some point
The next time the border will turn green (for this
delay is provided):
ORG #6000
LD HL,#8000
LD (HL),#81
LD DE,#8001
LD Sun,#100
LDIR
LD A,#80
LD I,AIM 2
EI
M1 CALL SUBR1
JR M1
SUBR1 LD A,R
RET РЕ
LD A,4
OUT (254),A
LD HL,0
LD DE,0
LD ВС,#600
LDIR ;WAIT
XOR A
OUT (254),A
RET
ORG #8181
EI
RET
Запускаю... И что я вижу?Верхняя часть
border-а мигает зеленым цветом:
Это говорит о том, что, во-первых, команда
LD A,R Region Commercial Counter-
Picture of Р/V, и во-вторых - this
Remove the snowflake from the snow, а
не когда угодно (действительно, тогда бы
border border on black-
вольных местах).
То, что верхняя часть border-а мигает, а
If you want to be on the sidelines, back
Thanks for the rest. По-виимому, ко-
Let LD A,R snowflake snowflakes
тогда, когдаthe interrupt pulse comes
during its execution, and this happens further
but not always - an interruption can occur
and while another command is running.
7. Final confirmation
------------------------------
Let's check this fact. Let the handler pre-
jerking determines where it was
program interrupted. If it was interrupted
exactly after the command LD A,R, let the border
will turn yellow for a while:
ORG #6000
LD HL,#8000
LD (HL),#81
LD DE,#8001
LD Sun,#100
LDIR
LD A,#80
LD I,A
IM 2
EI
M1 CALL SUBR1
JR M1
SUBR1 LD A,R
ВР1 RET РЕ
LD A,4
OUT (254),A
LD HL,0
LD DE,0
LD ВС,#600
LDIR ;WAIT
XOR A
OUT (254),A
RET
ORG #8181
EXX
EX AF,AF'
POP HL
PUSH HL
LD DE,ВР1
AND A
SBC HL,DE
JR NZ,NE_ВР1
LD A,6
OUT(254),A
LD HL,0
LD DE,0
LD Sun,#600
LDIR ;WAIT
NE_BP1 EXX
EX AF,AF'
EI
RET
If the LD A,R command does not work correctly
is due to the fact that during its execution
the impulse of interruption comes, then we will see
how the top part of the border will blink then
green, then yellow. But if the connection
between these two events exists, then
we need to see how the top
border blinks yellow, and the bottom
some are green, and they should blink
completely synchronized.
I launch it and see exactly what I expect.
laged. Indeed, such a connection exists
howls:
Other snowflake snowflakes and ра-
Do you want LD A,R? Эта команда помещает
If you want Р/V you can get a smile
IFF2. При разрешешеных прерываният тот
pages 1, а когда приходит импульс
прерывания, он ввтотически сбрсыается
At 0, you'll see the rest of the game
прерывания. This is the cover of the picture-
Pages Other Brand Website Personal Blog-
The price of the snowflake (т.е.
Small LD A,R). И, видимо, уже сброшенный
Price IFF2 in the range Р/V (дей-
snowflake, snowflake snowflakes,
прерывания в втот моменяalready prohibited).
All of the above also applies to the LD team
A.I. The information provided has been verified
on the original Z80 processor from ZILOG
and on the domestic analogue KR1858VM1.
8. What does this lead to and what to do?
------------------------------------
Using the LD A,R and LD A,I commands for op-
determining the state of the interrupt trigger,
generally speaking, it is used in many pro-
grams (and even in TR-DOS ROM). Here you go
explanation of a certain number of strange dependencies
saniy. It seems as if the probability of coming
interrupt pulse precisely during execution
The opinion of the LD A,R team is small. But first of all
Thus, the probability increases due to the fact that
that this command can be executed in
program more than once (and for freezing
a single incorrect execution is enough
opinion), and, secondly, if the program contains up to
this was encountered by the HALT command, i.e. syn-
intermittent chronicity may happen
so that the command LD A,R will be every time
be performed at the time when the most
probably another interruption (it happened in
BestView).
So this method is not reliable. But how
be? It turns out that it is possible with 100% accuracy
determineinterrupt trigger state
according to the following simple rule:
-execute the command LD A,R;
-if the P/V flag = 1, it means that interrupts are in
are actually allowed;
-if flag P/V = 0 - or interrupts in sa-
are prohibited in our case, or are they permitted,
but the LD A,R command set incorrectly
flag. To eliminate uncertainty,
execute the command LD A,R again. If and
now the P/V flag = 0 - that means interruptions
prohibited (in fact, it cannot be
so that during execution of the second
command LD A,R interruption occurred -
1/50 passes between two interrupts
seconds, and between two commands LD A,R -
much less time). If the flag P/V
= 1 - means interrupts are enabled.
Here is the relevant program fragment:
SUBR1 LD A,R
JP PE,M1
LD A,R
M1 PUSH AF
D.I.
....
POPA.F.
D.I.
RET RO
EI
RET
9. How can this be used?
------------------------------
Using the LD A,R command it is convenient to perform
testing the processor to recognize
execution of the program under the emulator. Emu-
The lator executes Z80 commands sequentially
but, one after another, and the command LD A,R always-
Yes, it will be correct to set the P/V flag.
But in the real Z80 this is not the case.
Here is the simplest procedure for testing
processor, which returns to the battery
tor 1, if it is running on a real Z80,
and 0 otherwise. She's trying 65536
read register R once when enabled
interrupts, and if at least one
once the P/V flag is set to 0, it is done
conclusion that the procedure works on real
Z80.
TESTZ80 EI
LD Sun,0
M1 LD A,R
JP RO,QUITINC Sun
LD A,B
OR C
JR NZ,M1
RET
QUIT LD A,1
RET
If the emulator is recognized, you can either
stop program execution (specifically
different protection), or disable some
areas of the program that may be incorrect
work under an emulator (for example, instead of
direct work with VG93 use point
input #3D13, etc.).
10. STS 6.2 fix
-----------------------
In the well-known STS debugger, the definition of co-
waiting for the interrupt trigger also occurs
dit using the LD A,R command. Because of this
Tracing may not work correctly
programs. When tracing, STS runs
each command (except for control transmission commands)
management) with the help of a resident, and after
after its execution, remembers the contents
CPU register pressure and status
interrupt trigger. This is where it's possible
errors.Let's say interrupts are enabled and traces
The following simple program is created:
#8000 NOP
#8001 JR #8000
Having stopped tracing after some time,
name (with the Indicate option disabled, one
a minute is enough), we will see that
interrupts were disabled. If only
a real program was traced, such
disabling interrupts could have an impact
for the entire course of its further implementation
and even lead to freezing (if
the trace would have encountered the HALT command).
It is quite obvious that the STS needs to be updated
corrections. Here's how to do it for ver-
this 6.2:
First you need to start STS and load
file "stsб.2 <С>", in which there will be
corrections are made.
Then you need to find the free 14 bytes -
their purpose will be explained below. You can
use user function buffer (with
addresses #FEЗ7). But in the version of STS that
I'm using it, this buffer is occupied by pro-
disassembly procedure with assembly marks
Blair ZX ASM, so I decided to cutsome text messages:
'Block' -> 'Bl.' (2 bytes saved)
'Save' -> 'S.' (----/----- 2 --/--)
'Load' -> 'L.' (----/----- 2 --/--)
' DEFB' -> ' ' (----/----- 4 --/--)
'FileName' -> 'Name' (----/----- 4 --/--)
To do this, from the address #EB24 you need to enter the following
following byte sequence:
#EB24: AE 46 72 6F ED 54 EF 46
#EB2C: 69 6C E5 53 65 63 74 6F
#EB34: F2 53 AE 4C AE 53 74 6F
#EB3C: 70 20 69 E6 42 61 6E EB
#EB44: 51 75 69 F4 54 72 61 63
#EB4C: E5 53 74 61 72 F4 44 69
#EB54: 73 61 73 ED A0 46 69 6C
#EB5C: E5 42 41 53 49 C3 20 44
#EB64: 4F D3
At address #E702 we replace the value #0A with
#0E to print the file name correctly
(since instead of the line FileName there remains
just Name).
So, now 14 is free from address #EB66
byte. Let's see where in STS the definition occurs.
division of interrupt trigger state:
#DFFE: LD(#5BA1),SP
LD SP,#5BA1
PUSH Sun
PUSH AF
LD A,R
D.I.
LD BC,#7FFD
LD A,#1F
OUT (C),A
LD B,#BF
LD A,#00
OUT (C),A
JP#E028
Let's replace the commands LD A,R: DI with NOP, and
mandu JP #E028 - to JP #EB66. From the address
#EB66 let's place the following fragment:
#EB66: LD A,R
JP PO,#EB6E +
NOP |
+- JR #EB70 |
| LD A,R <---+
+>DI
JP#E028
Please note - this fragment in any
case during its work increases the re-
gist R by the same amount (by 7). De-
The point is that more will be done next
one command LD A,R, perthis time it's needed
already to determine the value of register R, and
the received information will be corrected
values, because register R value increased
is executed with each command executed, and
you need to find out its value at the time of completion
execution of the traced command. Here
what it looks like:
#DCA2: LD A,#5A
LD HL,#FEFЧ
SLA (HL)
R.L.A.
ADD A,(HL)
RRCA
LD(HL),A
RET
Constant #5A at address #DCA3 should be followed by
change to #53, i.e. reduce by 7 - after all
an additional
a fragment that increments the R register by 7, and
we need to compensate for this change.
After this, all that remains is to record the changes
saved file to disk.
Share your thoughts about the article