A86: Interrupts & the "Down-Left" bug


[Prev][Next][Index][Thread]

A86: Interrupts & the "Down-Left" bug



I've made some research on what causes the famous down-left freeze
bug (or is it a feature!?) and how to get around it when programming.
I hope some programmers find this information useful - the routines
below is at least used in the upcoming Sqrxz 1.0.

So - how do you do to avoid the freeze bug? I've found three major
ways to solve the problem:

 * Disable Interrupts (chicken way :) )
 * set 2,(iy+$12)
 * Creating your own interrupt handler which ends with a RETI, not a JP
$38

Disabling interrupts
--------------------
In most games, this method is enough to avoid the problems. There are
two
disadvantages doing it this way though. Those are

 - keypresses must be read through ports
 - you can't create your own interrupt handlers

The first disadvantage is not much of a problem, most of the time. And
the second disadvantage is usually not a problem either. But sometimes
you want stuff to go on in the background (a timer for example) and
then disabling interrupts is not a good way to prevent the down-left
bug.

SET 2,(IY+$12)
--------------
When disassembling the ROM I found that if the second bit at (IY+$12)
is set, there will be no call to the routine that check port 1 for
keypresses. That routine lies at $01A1, and if that routine is not
called,
the down-left bug is gone. This is also a very simple way to get around
the problem, but it still has the disadvantage that you have to read
keypresses through ports. And most of the time when method 1 (Disabling
interrupts) isn't enough, it usually means you want to make your
own interrupt handler, and then method 3 is best suited for it (see
below).

If you use this method, you MUST reset the flag with RES 2,(IY+$12)
before
the program terminates - else the calc freezes.

Creating your own interrupthandler
----------------------------------
This is the most complex method, which I had some problems with first.
Creating an interrupt handler is not much of a problem really. This
is easily done with the following code:

 ld hl,$8E00
 ld de,$8E01
 ld (hl),$8F
 ld bc,256
 ldir
 ld hl,inthandler
 ld de,$8F8F
 ld bc,intend-inthandler
 ldir
 ld a,$8E
 ld i,a
 im 2

This code stores a vector table at $8E00-$8F00 and the interrupt handler
at $8F8F. Since most of RAM page 1 is free to use (????), this seems to
work without problems. The interrupt handler could then look something
like

inthandler:
 ex af,af'
 exx
 <do something>
 ex af,af'
 exx
 jp $38
intend:

This method is used in Sqrxz 0.9. The problem is that each interrupt
ends with a jump to the default interrupt at $38 - which has the
down-left bug. So, I tried to replace jp $38 with EI \ RETI (which
is how the default interrupt handler ends) but that didn't work -
The calc crashed :( To find out what was wrong, I had to play around
with the default interrupt handler. Anyway, this is how a "homemade"
interrupt handler should look like, that doesn't use the buggy handler
in the ROM AND still uses ROM routines for reading keys (without having
the down-left bug):

inthandler:
 ex af,af'
 exx
 <do something>
 in a,(3)
 rra
  push af       ; This three lines are not needed
  call nc,$01A1 ; if you only read keys through ports
  pop af
 ld a,9
 adc a,0
 out (3),a
 ld a,$0B
 out (3),a
 ex af,af'
 exx
 ei
 reti
intend:

It's necessary to send $09 $0B to port 3 when ON is _NOT_ pressed and
when
ON is pressed, you have to send $0A $0B to port 3 - else the calc
crashes.

This method works very well - it allows the user to create his own
interrupt
handler and still use the ROM routines to convert port 1 bit code to
scancodes
without having the annoying down-left bug.

-- 
Jimmy Mårdell                "The nice thing about standards is that 
mailto:mja@algonet.se         there are so many of them to choose from."
http://www.algonet.se/~mja    
IRC: Yarin                   "Sanity? I'm sure I have it on tape
somewhere!"


Follow-Ups: