Konami Games Idle loops

This is an archive of a topic from NESdev BBS, taken in mid-October 2019 before a server upgrade.
View original topic
Konami Games Idle loops
by on (#46736)
These Konami games all use the exact same idle loop.

Akumajou Densetsu (Castlevania III: Dracula's Curse)
Akumajou Special - Boku Dracula Kun
F-1 Sensation
Ganbare Goemon Gaiden: Kieta Ougon Kiseru
Gyruss
Kings of the Beach
Mission: Impossible
Moai-Kun
Monster in My Pocket
Parodius Da!
Snake's Revenge
Teenage Mutant Ninja Turtles II
Tiny Toon Adventures
Tiny Toon Adventures 2: Trouble in Wackyland

Other Konami games do not use the same idle loop as these games. (This list may be incomplete)

The idle loop is: (a and b are just variable names for zeropage addresses)
Code:
INC $a
CLC
LDA $a
ADC $b
STA $a
JMP back


It's almost like a code signature for certain Konami games.

by on (#46737)
Yeah, it's probably a random number generation. I've tried to hack that loop so that $a is always equal to $00 (or something olike that) in one of the Castlevania games and all "unimportant" candles were small hearts, never money bags (or was it the other way arround ? I don't remember exactly so this may have been wrong).

Older Konami games uses another loop using a lockup table (Castlevania), modern games uses a more complex variant of it (Mardara, Lagrange Point) and as far I know Bucky o'Hare and Gradius uses just a "jmp here" loop with no RNG.

by on (#46738)
There are also several other simple variants, they change some of the operations slightly:

Life Force
LDA A
CLC
ADC B
STA B
JMP

Castlevania II: Simon's Quest
LDA A
CLC
ADC B
STA A
JMP

Double Dribble
LDA A
ADC B
STA A
INC A
JMP

Jarin-Ko Chie
LDA A
SEC
ADC B
STA A
JMP

Goonies II, Dragon Scroll, Ganbare Goemon! Karakuri Douchuu
LDA A
SEC
ADC B
STA B
JMP

Super C, Ski or Die
INC A
LDA A
ADC B
STA A
JMP

Contra, blades of steel
LDA A
ADC B
STA B
JMP

Contra Force, Gradius 2
LDA A
ADC B
STA A
JMP

I'm writing code to skip these loops, trying to express their behavior as a function of number of times run. Some of them reduce nicely, such as the common one in the first post, which reduces to A+=N*(B+1). Anything that sets or clears carry gives room for multiplication.
But the ones which do not set or clear carry are tricky, I don't think there's any way to get their final value without running each iteration.

by on (#46739)
to include the carry, simply perform 16 (or higher) bit arithmetic and add the high byte to the low byte afterwards. IE:

Code:
LDA a
ADC b
STA a


Could be unrolled with:
Code:
unsigned sum = a+(b*x); //x is # of iterations
sum += (sum>>8);
sum &= 0xFF;


EDIT

actually I guess you'd have to add all of the higher bytes together.

so something more like:
Code:
unsigned sum = a+(b*x);
sum += (sum>>8) + (sum>>16) + (sum>>24);
sum &= 0xFF;

by on (#46740)
Wow, that's disturbingly simple. Thanks.

by on (#46745)
This is for pocket NES ? If so this is cool, if you automatically skip the CPU emulation of simple loops waiting for VBlank, you can probably dratiscally increase the speed.
And I guess many games will run into idle loops like that instead of simple "jmp here" loops. Konami is just very different because the loop is not waiting for a NMI to happen, it just is the main programm and everything else is in NMI, and I don't know how the programmer could do something like that without having MANY headaches.

by on (#46749)
I just realized my above code is wrong. Right idea, but implemented wrong.

Since adding the carry (high bytes) can create more carry, you have to repeat it.

Revised:

Code:
sum = a+(b*x);
while(sum > 0xFF)
  sum = (sum & 0xFF) + (sum >> 8);

by on (#46750)
What about the output carry flag?

It also looks like some of these get very complex, especially with that INC operator, which increments with wraparound without setting carry.

by on (#46754)
This is fun. Like little 6502 logic puzzles.

I'll work on solutions for all listed loops. Results to be posted as soon as I finish (sould be later today).

EDIT:
Hrm -- when you factor in having to set carry out.. this gets a bit more complicated. Even the simple ones aren't so simple.

by on (#46784)
I think I'll just end up repeating some ARM assembly for some of these:
Code:
LDA A
ADC B
STA A
INC A

becomes:
Code:
adcs r0,r0,r1
add r0,r0,#0x01000000

and repeat it (making sure not to change the ARM carry flag)

by on (#46790)
That's probably the safest bet. I tried coming up with solutions for some of them, but those INC ones are tricky. Even some of the not so tricky ones are tricky with some edge cases (I don't think my previous solution of adding the high byte(s) would work -- it gets weird when you add 255+255 with carry)

=x