Close

Stack Overflow!

A project log for Project 72 - Korg DW-6000 wave memory expansion

An attempt to reverse engineer and modify Korg DW-6000s firmware in order to expand its wave memory.

mateusz.kolanskimateusz.kolanski 11/07/2018 at 20:270 Comments

No, I'm not browsing SO looking for help. I'm experiencing it right now :) After I found the definitive places where I'm going to inject my code, I noticed that my .BANKSWITCH 'thingy' must become a callable subroutine. Blah blah blah... Wait a minute, I think I know why it fails. BRB

[10 minutes later]

Hi there. I started writing this post because - for some strange reason - my debugger decided to crash. Not once, not twice, but all the time. I didn't quite expect this kind of behaviour, but when I loaded vanilla ROM, it was rock stable again. OK, quick look into GIT and I quickly figured out that the only change was my dreadful subroutine I created yesterday. I needed a callable sub to be able to, well, call it from different places. The biggest limitation of the previous one was the arbitral jump into the place it should go back plus the fact, that it completely relied on values in registers. This is the old one:

MOV	A, D		; bank number (0-7) stored in D, copy to A
SLL	A		; store in high nibble
SLL	A
SLL	A
SLL	A
;OFFIW 	82H, 40H	; check if $2682 bit 5 is set
;ORI	A, 40H		; yes, we have 'carry'
MOV	B, A		; store in B
DI			; enter critical section
MOV	A, PB		; get current PB
ANI	A, 0FH		; wipe high nibble
ORA	A, B		; join together
MOV	PB, A		; speak to hardware
XRI	PC, 04H		; enable latch (PC2)
NOP			; wait
XRI	PC, 04H		; disable latch
EI			; enable interrupts
JMP	.11E1H		; go back (label is my guess:)

The jump back was actually spot-on (I figured it out after some more cumbersome code analysis), but like I said it didn't fit at all in the other place. This is what I ended up with:

LDAW	81H		; load word from $2681 to A
SLR 	A		; shift right, now we have our value in b4-b5
OFFIW	82H, 40H	; check if bit 6 @ $2682 is set
ORI	A, 40H		; it is, so let's do the same with our data
ANI	A, 70H		; clean-up
MOV	B, A		; store in B
DI			; enter critical section
MOV	A, PB		; get current PB
ANI	A, 0FH		; wipe high nibble
ORA	A, B		; join together
MOV	PB, A		; speak to hardware
XRI	PC, 04H		; enable latch (PC2)
NOP			; wait
XRI	PC, 04H		; disable latch
EI			; enable interrupts
RET

First off, I'm using absolute addresses instead of values from registers. They ain't gonna change, so I can hardcode them. And since the value I'm 'extracting' is already in bits b4-b5 and finally I'm writing it back to the upper half of PB, only one shift is necessary. Furthermore, if we store the high bit already on the 'right' position, all we need to do is to check if it's set and copy its state into our value. Neat. So far so good. But then MAME crashed a few times. I managed to go in step mode into my routine and then I saw that the value of the vector register (V) is not 0x26 but 0x27. I was pretty sure, that the context will be right, but it turned out that I was wrong. Maybe that's why it crashed, huh? OK, let's modify it one more time:

PUSH    V
MVI     V, 27H
...
POP     V

That should do the trick. I was only concerned about the stack size, cause it's pretty tiny, but I decided to give it another go instead. Nope, still the same. I set a breakpoint on my new code again and saw that each call of my sub causes the bottommost part of the memory (where the stack lives) to fill with some repeating pattern. Yup, stack overflow.

That was the moment I decided to write a post, to share a laugh and maybe let my brain cool down a little bit. But after a few lines it struck me where the problem was.

I have rewritten this code, because I needed to run it every time new patch is loaded. I found a suitable place inside of a piece of code that sends the patch data to KLM-654 (DWGS board). After each chunk of data, KLM-654 sends an ACK signal to say that it finished processing the previous one. It generates an interrupt and then the magic happens. After having sent the whole patch, the code branches and this is where my code is being called. I'm not going to show you the whole ISR, but here's something I have clearly missed:

; ******************************************************************************
; * INTERRUPT HANDLER FOR INT1 / INT1 (ACK from KLM-654)                       *
; ******************************************************************************
.ISR_INT1_2:	PUSH	EA	
		PUSH	V	
		PUSH	D	
		PUSH	H	
		DI	
		...
.0A5AH:		POP	H	
		POP	D	
		POP	V	
		POP	EA	
		EI	
		RETI	

Got it? I was calling my code from an ISR which - surprise, surprise - disables and re-enables interrupts. Go back to my code now. See? Enabling interrupts in the middle of an interrupt handler is a bad idea. OK, getting back to work.

Discussions