Down-Left Bug

z80 » Intermediate

Written by Jimmy Mårdell

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 majorways to solve the problem:

Disabling interrupts

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

  1. Keypresses must be read through ports
  2. 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.

Setting Flags

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 (Your Handler). 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 Interrupt Handler

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

	. . .
	. . .	;interrupt code
	. . .

	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

	. . .
	. . .	;interrupt code
	. . .

	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,$00
	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 (Key Port) bit code to scancodes without having the annoying Down-Left Bug.


More from z80 » Intermediate
All the Flags // Debugging // Down-Left Bug // _GetKey Codes // Logical Operators // Memory, ROM, RAM, and Safe Areas // Miscellaneous Instructions // PC and SP // Random Numbers // TI's ROM Calls // Restart Commands // Simulated 16-bit Addition // The Stack // Tables and Arrays // Text Display // Variables