|クラッシュ/フリーズ||重要 (ゲーム)||解決済み||修正済み||2015-04-20 06:34||2015-05-12 06:06|
|詳 細||It would seem that there is a bug in the core emulation of the ZX Spectrum whereby interrupt routines which take longer than 1 frame (69888 T-states in the case of the PAL 50Hz ZX Spectrum) to complete cause the emulator to go into an infinite interrupt service routine loop which is never broken out of until a reset of the emulated machine.|
I've successfully reproduced this bug on MESS 0.154 and MESS 0.159 self-compiled 64-bit binaries on Mac OS X 10.7.5.
I've tried my best to follow and understand the source of MESS to see if I can help to fix the issue but I can't figure out how many T-states the /INT pin on the Z80 is pulled low for in the emulated Spectrum.
According to p. 226 of Chris Smith's excellent book "The ZX Spectrum ULA : How to design a microcomputer" the ULA pulls the /INT pin low for exactly 32 T-states. My thinking is that the MESS emulation of the Spectrum is either not pulling the /INT pin low for long enough or holding it low for too long, but I can't figure out which from the source.
I've checked the source code of three popular ZX Spectrum emulators (Fuse, Zero and Unreal Speccy Portable) and in all three the /INT pin of the Z80 is pulled low for exactly 32 T-states when generating an interrupt.
On a totally separate note, I noticed in the ZX Spectrum video driver that the FLASH rate is set at 25fps. This is incorrect, the actual rate should be 16fps. This is also detailed in Chris Smith's book.
|再現手順||I first noticed this bug when playing Chuckie Egg 2. If you can imagine the room layout as a 10x12 matrix then the final room (bottom right corner of the 10x12 grid) (room 120) contains a red room with a huge green monster on a skateboard with the text "A MONSTERS WORK IS NEVER DONE". If you enter that room then the emulator will go into an infinite interrupt service routine loop due to the fact that execution of the IM 2 service routine responsible for erasing the previous sprite of the huge green monster and drawing the new sprite of the huge green monster takes longer than a single frame to complete. Code execution never breaks out of the service routine to return to the main game loop. Every dedicated Spectrum emulator that I've ever tried renders this room without problems.|
You don't need to play through the entire game to get to room 120 to test this bug. If you have the original A&F release of the game then the room number you're in is held in location 41982 ($a3fe), if you have the Pick 'n Choose re-release then the room number you're in is held in location 42002 ($a412). Simply start a new game, drop into the debugger, set either 41982 or 42002 (dependent on which version of the game you're running) to a value of 119 ($77) and then continue execution. This will put Harry in room 119 so that when you walk to the right into the next room you'll actually walk into room 120 and the infinite interrupt service routine loop bug will occur. The game will hang, constantly drawing and erasing the huge green monster sprite.
|追加情報||I wrote some test Z80 code to demonstrate this :-|
start: di ; disable ints
ld sp,$ff00 ; set up a stack
ld hl,$8100 ; set up a table of 257 x $80 for IM 2 vectors @ $8100
loop: ld (hl),c
ld (hl),c ; set the 257th byte
ld (hl),195 ; JP instruction to $8080
ld de,isr ; address of ISR into DE
ld (hl),e ; low byte of ISR address to $8081
ld (hl),d ; high byte of ISR address to $8082
ld i,a ; set high byte of IM 2 vector to $81
im 2 ; 8 T States - switch into IM 2 mode
ei ; 4 T States - enable interrupts
main_loop: halt ; wait for interrupt (PC--, T States += 4)
; there are 69888 T States in a 50Hz PAL ZX Spectrum frame
; it costs 19 T States to respond to an IM 2 interrupt and a further 10 T States to JP to first instruction of that ISR
; 19 T States = 7 to fetch lower bits from the interrupting device + 6 to save PC + 6 to obtain jump address
; if BC == $420 then this ISR takes 69803 T States (excluding servicing the int and the JP instruction)
; if BC == $421 then this ISR takes 69869 T States (excluding servicing the int and the JP instruction)
; total T States if BC == $420 = 69803+19+10 = 69832 (assuming interrupt occurs at T states = 0)
; total T States if BC == $421 = 69869+19+10 = 69898 (assuming interrupt occurs at T states = 0)
isr: push af ; 11 T States
push de ; 11
push bc ; 11
push hl ; 11
ld bc,$420 ; 10 -- try changing this to $421 to cause MESS emulator to go into an infinite interrupts loop
isr_loop: nop ; 4
nop ; 4
nop ; 4
nop ; 4
nop ; 4
nop ; 4
nop ; 4
nop ; 4
nop ; 4
nop ; 4
dec bc ; 6
ld a,b ; 4
or c ; 4
jr nz,isr_loop ; 12 if Z != 0, 7 if Z == 0
pop hl ; 10
pop bc ; 10
pop de ; 10
pop af ; 10
ei ; 4
reti ; 14 (if BC==$421 then T States will be 69884 BEFORE this instruction is executed)
You may need to execute this code a few times to get to a position whereby the interrupt occurs at exactly T-states=0, as it's entirely dependent on what the T-state count was when execution of the program begins which determines what the T-state count will be when the interrupt occurs. If T-state count == 0 when the interrupt occurs and the BC loop is $420 then the routine will take exactly 69832 T-states to complete and return to the main loop. However if you change the BC loop counter to $421 then the routine takes longer than a single frame to complete (69898 T-states) and the emulator will go in to an infinite interrupt service routine loop. All tests were done using an emulated PAL 50Hz ZX Spectrum (69888 T-states per frame). Any figure higher than $420 in the BC loop will cause a hang in the emulated Spectrum.
|添付ファイル||mess_0154_ce2_bug.png (2015-04-20 06:34) |