AY-Track: Muzic theme from game "FAIRLIGHT"
__________________________________________
(C) Ivan Roshchin
__________________________________________
Undocumented feature
CPU Z80
Allowed free
This article provided no kakihlibo make changes and save my
copyright.
1. How it all began
I wrote once the next version of the program BestView (v2.4)
and used it Here's a snippet:
....
EI
CALL SUBR1
HALT
....
SUBR1 LD A, R
PUSH AF
DI
....
POP AF
DI
RET PO
EI
RET
In the snippet is a procedure call SUBR1, which at the time
of his work disables the interrupts, while output recovers the
previous regime of their work.
Verifying that are allowed or disallowed
interruption in the procedure call, and restore interrupt mode
is as follows:
- The team LD A, R enters into a flag P / V coc
Toyan trigger interrupt IFF2;
- Register pair AF is stored in the degree
Ke (PUSH AF);
- Do not Interrupt (DI);
- Are satisfied, in fact, the functions for
which was designed and procedure
SUBR1;
- The contents of AF recovered (POP
AF);
- Interrupts are prohibited (DI);
- If the flag P / V reset is exited
of the procedure prohibited by interrupting
mi (RET PO);
- Or exits to the allowed
Interrupt (EI: RET).
I began to notice that when running this
fragment BestView hangs - not always, and
not even too often, but in very rare
cases. But still, it was not very
nice. The program, like, did not contain
no errors, at least at first
See anything suspicious, I did not notice. Had only to resort
to more a powerful tool ...
2. The situation begins to brighten
After another hang-up, I put
a blank disk and confidently pressed the MAGIC.
Then loaded the debugger "STS 6.2 + @" (not
I reworked it for nothing - now using it after downloading @
file, you can restore the contents of CPU registers on the
instant relief to disk). Pressure a pair of keys - and now I
see where in the program crash happened.
....
EI
CALL SUBR1
HALT <------------ here!
....
A typical case - Interrupt prohibited
and the processor has stopped execution of the program
on the command HALT. But why break proved to be forbidden - it
is not clear. After all, before calling the procedure SUBR1
they were allowed Team EI, and after work
SUBR1 they too should be allowed SUBR1-procedure should not
affect on the mode of their work.
They trace SUBR1. Everything goes as expected
- And at the entrance and exit interrupts are allowed. Repeat
trace: once, twice ... tenth. Everything is going fine.
And maybe the fact that SUBR1
something happens with the stack? And because of this
sometimes incorrectly restored the contents of AF? We ought to
check out ...
3. Hanging: double second
Well, rewrote the program. Now there
I'll know exactly what it was:
SUBR1 LD A, R
PUSH AF
DI
PUSH HL
PUSH AF
POP HL
LD (WR_HH1), HL
POP HL
....
POP AF
PUSH HL
PUSH AF
POP HL
LD (WR_HH2), HL
POP HL
DI
RET PO
EI
RET
WR_HH1 DW 0
WR_HH2 DW 0
The contents of AF now remembered not
only in the stack, but in variable WR_HH1
(Control), and at the exit from the procedure
- Removal from the stack value is stored in
WR_HH2. If the procedure works correctly,
WR_HH1 and WR_HH2 must match, and the flag
P / V to be installed.
Run ... For a moment BestView works fine ... Look through
with it the same file, while viewing that it hung the last time
... Well, again! And no wonder, because the reason I
not eliminated. Okay, we understand.
Again, press MAGIC, load "STS 6.2
+ @ ", And immediately check the value and WR_HH1
WR_HH2. And there, and there is written # 5908. Values are
the same - hence, when working with a stack of errors was not.
But if the flags register contains # 08 - hence the flag P / V
is reset, and the procedure call SUBR1 interrupts were
prohibited.
But it's absolutely impossible! Indeed, in
program is EI: CALL SUBR1! Probably
Spectrum just overheated and because such
glitches. Nothing is more intelligent I am in this day
never came up.
4. False scent
The next day I found a possible
explanation for the mysterious Prohibition interrupts. Suppose,
after the team EI, but before command LD A, R, there was an
interruption. As is known, the procedure for its treatment must
end teams EI: RET (because in the beginning of treatment there
is an automatic ban on interrupts). If the interrupt handler
completes the team just RET, then the interrupt remain
prohibited.
Of course, the likelihood that an interrupt will occur
precisely between the teams and EI LD A, R is very small, but
you and lag are very rare. So it's an extra just confirmed my
hypothesis.
Nevertheless, it was unclear why would this hook ended with
command RET, but not EI: RET. I decided to check whether this
is the whole thing, and for this added after the command HALT EI
(See below). If the interrupt handler did not completed
successfully, then after adding HALT'a interrupts are always
will be prohibited and, accordingly, BestView will always hang.
....
EI
HALT <------ add commands
CALL SUBR1
HALT
....
SUBR1 LD A, R
PUSH AF
DI
....
POP AF
DI
RET PO
EI
RET
Compile, run ... An unexpected result - hangs completely
stopped! Like everything and leave, but
he decided to find out why this happens.
5. Acceptance of "simplification program
When finding an error by conventional means
can not, I delete all of the programs that
possible, but that the error in this case remained. As a
result, when the program is with a dozen lines, the error is
noticeable immediately. So I entered and this time:
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
That such a program, a total of 19 bytes.
Allowed interruption and in an infinite
loop procedure is called SUBR1. This procedure sets the green
border, if at the entrance to her termination was prohibited,
and does not change the color of the border, if the interruption
allowed. Thus, if an
spontaneous ban interrupt
it will be immediately noticeable.
Run - yes, border changes color
to green. The reason for this strange behavior of the program
remains unknown. Perhaps this fault interrupt handling routine
one of the first kind? Add to Playing multiple teams, setting
IM 2 mode with a handler, which consists only two commands: EI:
RET.
ORG # 6000
LD HL, # 8000
LD (HL), # 81
LD DE, # 8001
LD BC, # 100
LDIR
LD A, # 80
LD I, A
IM 2
EI
M1 CALL SUBR1
JR M1
SUBR1 LD A, R
DI
JP PO, M2
EI
RET
M2 LD A, 4
OUT (254), A
RET
ORG # 8181
EI
RET
Run - the same result! Although the
tracing, and this and the previous program
in the debugger border is black. Careful examination of the
program leads to hypothesis: maybe the team LD A, R
sometimes sets a bit P / V as if the interruption is prohibited
while on Indeed, they allowed?
6. Is the error in the processor?
Once again change the program. Now do not interrupt will be
banned (removed command DI). If when you run LD A, R-bit P / V
becomes equal to 0, for some time border is green (for This
provides for the delay):
ORG # 6000
LD HL, # 8000
LD (HL), # 81
LD DE, # 8001
LD BC, # 100
LDIR
LD A, # 80
LD I, A
IM 2
EI
M1 CALL SUBR1
JR M1
SUBR1 LD A, R
RET PE
LD A, 4
OUT (254), A
LD HL, 0
LD DE, 0
LD BC, # 600
LDIR; WAIT
XOR A
OUT (254), A
RET
ORG # 8181
EI
RET
Run ... And what I see? Upper part
border'a flashes green:
This suggests that, firstly, the team LD A, R do sometimes
incorrectly sets bit P / V, and secondly - what
this is happening at the moment of arrival interrupt, but not
when you want (indeed, then it would blink green border in a
completely random locations).
The fact that the upper part border'a flashes
and not always painted in green,
also receives an explanation. Apparently, the team LD A, R is
not working only when the impulse to interrupt comes during its
execution, as it happens not always - an interrupt can occur
during the execution of another team.
7. Final confirmation
We verify this fact. Let processor
interrupt determines where in a
interrupted program. If it was interrupted
just after the command LD A, R, let the curb
for some time to yellow:
ORG # 6000
LD HL, # 8000
LD (HL), # 81
LD DE, # 8001
LD BC, # 100
LDIR
LD A, # 80
LD I, A
IM 2
EI
M1 CALL SUBR1
JR M1
SUBR1 LD A, R
BP1 RET PE
LD A, 4
OUT (254), A
LD HL, 0
LD DE, 0
LD BC, # 600
LDIR; WAIT
XOR A
OUT (254), A
RET
ORG # 8181
EXX
EX AF, AF '
POP HL
PUSH HL
LD DE, BP1
SBC HL, DE
JR NZ, NE_BP1
LD A, 6
OUT (254), A
LD HL, 0
LD DE, 0
LD BC, # 600
LDIR; WAIT
NE_BP1 EXX
EX AF, AF '
EI
RET
If incorrect operation command LD A, R
not connected with the fact that during its execution of the
pulse interrupt, then we see how the top will border'a
flashes then green, then yellow. But
if the relationship between these two events there, then we
should see how top border'a flashing yellow and the lower part
- the green, and they should blink completely in sync.
Run - and I see exactly what and
expected. Indeed, such a relationship
exist:
But as can be related interruptions and
teamwork LD A, R? This team puts in a flag P / V content
trigger interrupt IFF2. When interrupt enable this trigger is
1, and when it comes to interrupt the momentum, it is
automatically reset to 0 to eliminate reprocessing
interruption. However, query processing interrupt begins during
the last execution carried out by the bar team (Ie, commands LD
A, R). And, apparently, already thrown trigger IFF2 copied to
the flag P / V (indeed, in terms of CPU, interrupt at this
point is already prohibited).
All of the above applies to the command LD
A, I. This information has been verified
on the original Z80 CPU ZILOG firm
and domestic counterparts KR1858VM1.
8. What does it lead and what to do?
Application commands LD A, R and LD A, I for
determine the status of interrupt trigger,
generally used in many
programs (and even in the ROM TR-DOS). Here you
and an explanation of a number of strange lockups. It seems as
if the probability of the pulse interrupt it at the time of the
command LD A, R is small. But, first, the probability increases
due to that this command can be executed in
program more than once (and for hovering
enough only incorrect execution), and secondly - if the program
to This team met HALT, that is,
synchronization with interrupts, it may happen that the command
LD A, R will always be executed at a time when most likely the
next interrupt (or was BestView).
Thus, this method is unreliable. But how do
be? It turns out that with 100% accuracy
determine the status of interrupt trigger
by the following simple rule:
- Execute the command LD A, R;
- If the flag P / V = 1 - means Interrupt
in fact authorized;
- If the flag P / V = 0 - any interruption in
actually prohibited, or permitted,
but the team LD A, R incorrectly planted a flag.
To resolve this uncertainty, again
run the command LD A, R. If now
Flag P / V = 0 - hence, interrupts are prohibited (in fact,
may not be so and run-time second-team LD
A, R has occurred - between the two
preemption is 1 / 50 seconds, and between the two teams LD A, R
- much less time). If the flag P / V = 1 - mean
interrupts are enabled.
Here's the relevant code fragment:
SUBR1 LD A, R
JP PE, M1
LD A, R
M1 PUSH AF
DI
....
POP AF
DI
RET PO
EI
RET
9. How do I use it?
With a team of LD A, R is convenient to carry out testing of
the processor to recognize the program under the emulator. Z80
emulator executes instructions sequentially, one after another,
and the team LD A, R will always be properly installed
Flag P / V. A real-Z80 is not.
Here is a simple procedure for testing the processor, which
returns the battery 1, if it is running on a real Z80, and 0
otherwise. She tries to 65536 times read register R for
interrupt enable, and if this even though least once a flag P /
V set to 0 - it is concluded that the procedure works on
Real-Z80.
TESTZ80
EI
LD BC, 0
M1 LD A, R
JP PO, QUIT
INC BC
LD A, B
OR C
JR NZ, M1
RET
QUIT LD A, 1
RET
If recognized by the emulator, you can either
terminate the program (a kind of protection), or disable some
program segments that can be wrong
work under the emulator (for example, instead of
direct work with the use point VG93
Input # 3D13, etc.).
10. Correction STS 6.2
In a debugger STS definition
status of trigger interrupt also occurs with the command LD A,
R. Because of this, may not hold tracing program. When tracing
STS runs each command (except for command transfer control)
with the help of residents and after its implementation stores
the contents of processor registers and trigger status
interrupts. That's where mistakes are possible.
Assume interrupts are enabled and traced a simple program:
# 8000 NOP
# 8001 JR # 8000
Stopping the trace after a
time (if this option is disabled Indicate one minute is
enough), we see that interrupts were prohibited. If
traced to a real program, such a ban could have an interrupt
impact on the entire course of its further implementation and
even lead to a crash (if when tracing has got to command HALT).
It is obvious that the STS should be
correction. Here's how
for version 6.2:
You must first download and run the STS
file "sts6.2 ", which will be made and corrections.
Then you need to find a free 14-byte
- Their function will be explained below. You can use the
clipboard user function (With address # FE37). But in the
version of the STS, I use this buffer is occupied by procedure
of disassembling an assembly with labels ZX ASM, so I decided
to cut Some text messages:
'Block' -> 'Bl.' (Save 2 bytes)
'Save' -> 'S.' (----/----- 2 --/--)
'Load' -> 'L.' (----/----- 2 --/--)
'DEFB' -> '' (----/----- 4 --/--)
'FileName' -> 'Name' (----/----- 4 --/--)
For this purpose, the address must be entered # EB24
the following sequence of bytes:
# 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 44 69 F4
# EB54: 73 61 73 ED A0 46 69 6C1
# EB5C: E5 42 41 53 49 C3 20 44
# EB64: 4F D3
At # E702 changes the value of # 0A
at # 0E, to correctly print the name
File (FileName as string instead of just left Name).
So now with the address # EB66 free 14
bytes. See where the STS is to determine the state trigger
interrupts:
# DFFE: LD (# 5BA1), SP
LD SP, # 5BA1
PUSH BC
PUSH AF
LD A, R
DI
LD BC, # 7FFD
LD A, # 1F
OUT (C), A
LD B, # BF
LD A, # 00
OUT (C), A
JP # E028
Replace command LD A, R: DI on NOP, but
team JP # E028 - to JP # EB66. C address
# EB66 put this snippet:
# EB66: LD A, R
JP PO, # EB6E
NOP
JR # EB70
LD A, R <
> DI
JP # E028
Please note - this piece in any case, their work enhances
Register R by the same amount (7).
The fact is that more will be done yet
one team LD A, R, this time the desired
is to determine the value of the register R, and
correction will be made resulting
values, because R register value increases with each command,
and need to know its value at the end of the implementation
team traced. Here it looks like:
# DCA2: LD A, # 5A
LD HL, # FEF4
SLA (HL)
RLA
ADD A, (HL)
RRCA
LD (HL), A
RET
Constant # 5A at # DCA3 should
replaced by # 53, ie, reduced by 7 -
since the program was added an additional fragment, which
increases the register R at 7, and must compensate for this
change.
After that you only write
modified file to disk.