Frank N Stein

24

Comments

  • edited May 2011
    I've done this level 25 RZX to demonstrate the timing problem (and to ask if anyone can see how the route which I've taken might be shortened to avoid it). The main problem is caused by the long delays waiting for the ve-e-ery slo-o-ow yellow hazard on the bottom platform to get out of the way.

    I've tried four different emulators and the result is always the same. Hopefully we might be able to get an explanation of the author's comment that it can only be completed on a real Spectrum. I can't try that as I would never be able to get to level 25 without being able to save the game numerous times along the way.
  • edited May 2011
    Thank you. :)

    Yes its playable on the authors site although no download that I could see.
  • edited May 2011
    :)

    Had to do a 33577,0 to 'fix' the time but otherwise OK. Took a .z80 of it anyway right at the end. 27 looks difficult :-o

    FNS level 25 .z80 (with infy time)
  • edited May 2011
    That online version sent my processor fan into overdrive, so I didn't fancy trying that for very long.

    I checked that comment on YouTube about the countdown being inconsistent, and standing still on level 25 it takes 3:00 minutes, whereas moving about it takes 2:45. If the countdown was consistently 3:00 minutes then that extra 15 seconds would leave just enough time to finish the screen. Why would this vary on emulators but not (apparently) on a real Spectrum?
  • edited May 2011
    I can only assume its not emulating some form of ULA timing ( not my area :-? ) accurately. Would contended vs uncondended memory be an issue as the 'real' machine has it emulators might not emulate it accurately.
  • edited May 2011
    spider wrote: »
    Yes its playable on the authors site although no download that I could see.

    Here:

    http://www.colinstewart.co.uk/sites/default/files/flash/sna/FrankNStein.sna
    Creator of ZXDB, BIFROST/NIRVANA, ZX7/RCS, etc. I don't frequent this forum anymore, please look for me elsewhere.
  • edited May 2011
    I decided to plug in the Speccy & Multiface to check if level 25 really can be completed on a Spectrum. So I enter the infy lives POKE and make slow progress to level 13 when the game crashes and I remember that I hadn't entered the stack reset patch. So, back to level 1 again. Ho hum. I'll eventually get there, I expect, and will then report my observations; it might take the rest of the weekend though, as there's Dr Who plus Man Utd v Barcelona to watch tonight, and the Monaco GP tomorrow.

    Oh, I just remembered, I found a new little bug yesterday. On the Kong levels, if one of the springs on the penultimate platform appears beneath where the monster is standing then jumping on it will get Frank stuck between the monster's feet and the game has to be reloaded.
  • edited May 2011
    The .sna from the authors site, that also crashes eventually, I forget what level now stupidly I did not save it but it was about 30 something. I had only just started one screen and within a few seconds some of the nasties turned into keywords then the infamous Invalid Colour message.

    I used half a dozen pokes to get immunity. The .sna posted from the authors site is the 'bonus lives' version btw.
  • edited May 2011
    IMHO it doesn't count as a game bug (player still able to intentionally crash the game only after poking infinite lives) so I think this new patch is enough!
    The quick crash (hold left key) works for me without using any POKE - so I'd say it was a bug in the game! (Albeit minor compared to the other bugs)

    Does anybody know if the author still has the source code? If so, then the game should still crash under BASIC (probably with Error 4 "Out of Memory" as the GOSUB stack gets too big).

    I am fairly confident of today's emulators, so I'm sure the game will be just as unplayable on a 'real' machine.
  • edited May 2011
    I've just finished checking up to level 25 on a real Spectrum with my original tape. Points to note are: a) The end-of-level stack-reset patch as posted above is always needed otherwise it will crash eventually. b) Level 25 plays identically on an emulated or real Spectrum; ie. there's not enough time to finish it. The best that's possible is to collect all the parts but then there's no time left to reach the plunger.

    There's some side-effect in the code which causes the timer to count down slightly more quickly when Frank is moving than when he's stationary, so there's 10-15 seconds less time at the end than there should be and the level can't be completed as it is without an infy time POKE.
  • edited May 2011
    Interesting thread. Level 25 was obviously tested well by its author. Or not at all.
  • edited May 2011
    Did anybody check if level 25 can be completed using the snapshot version available from the author's site?

    http://www.colinstewart.co.uk/sites/default/files/flash/sna/FrankNStein.sna
    Creator of ZXDB, BIFROST/NIRVANA, ZX7/RCS, etc. I don't frequent this forum anymore, please look for me elsewhere.
  • edited May 2011
    I worked through it although I lost track of which version I was using. I had to use invulnerability though. I have snaps saved at levels 33 to 53 at the moment.
  • edited May 2011
    If a breakpoint is set at $B100 and the HL register set to the level number (not counting the intervening Kong levels, so for level 25 enter 13) then the game will continue at that level ...

    ... and the answers are "yes" I've now checked it and "no" it can't be completed - it plays just the same as the other versions.
    spider wrote: »
    I worked through it although I lost track of which version I was using. I had to use invulnerability though. I have snaps saved at levels 33 to 53 at the moment.

    Level 53? Are you sure you're not going around in circles? There's only supposed to be 50 levels!
  • edited May 2011
    It is a repeat of level 3, in that level 51 is level 1, if that makes sense :)

    I just meant that's as far as I got before I ran out of time (not game time) and had to go sort things out elsewhere...
  • edited May 2011
    The author Colin Stewart just sent me a very nice email regarding this game. I'm taking the liberty to reproduce his entire answer below assuming he won't mind, since there's nothing private or personal here:
    Hi Einar,
    Thanks for contacting me and for keeping my 25 year old game alive!
    I did make a new version fixing level 25 and the whole game has now been completed on a Nintendo DS.
    This was achieved by adding an extra electric-shock item, giving the player more time.

    I've attached the new version as requested, however you may want to apply the other patches mentioned on the forum as it is based on the original game image. Not sure which file type you wanted, so I've attached all 3 that I have.

    Let me know if you need anything else or any other information. Also let me know how things go.

    Take care,
    Colin.

    All 3 formats are now available here:
    FrankNStein2010.tzx
    FrankNStein2010.tap
    FRANK2010.Z80

    However, instead of uploading this version directly to WoS, I will first follow the author's request and also combine it with the latest bugfixes provided by Battle Bunny. Hold on, I will provide a final version in a few minutes!
    Creator of ZXDB, BIFROST/NIRVANA, ZX7/RCS, etc. I don't frequent this forum anymore, please look for me elsewhere.
  • edited May 2011
    I just finished to combine Colin Stewart's latest version with the bugfixes provided by Battle Bunny that seemed relevant, i.e the fixes provided in #29, #19, and #22. Many thanks to Jimmy for pointing out that the first bugfix wasn't enough.

    In this particular case, I decided to change the code block directly instead of including POKEs in the initial program, so this program would remain very small and thus provide more room for the stack trace. During my tests, the initial program size seemed to affect the crashes. Actually I didn't see any crash after all the fixes were applied, but I didn't play very far and I didn't want to take any chances.

    Finally I have documented everything and uploaded the fixed version in this thread. Please let me know if everything works correctly in this version for you!

    EDIT: I just found out that the bugfix proposed in #22 was crashing the game in specific cases. It looks like one of those JUMPs actually needed the return address left in the stack! So I just removed this particular fix and kept the others. The complete bugfix description has been corrected appropriately. If you have downloaded the bugfixed file prior to this post, please download it again!
    Creator of ZXDB, BIFROST/NIRVANA, ZX7/RCS, etc. I don't frequent this forum anymore, please look for me elsewhere.
  • edited May 2011
    Please correct the above entry (which I see you've now done). As pointed out at the bottom of #22 - "Nope, that's not the solution." - those changes don't help matters. In normal play only the "stack reset" patch in #29 is needed. The other "quick crash" patch in #19 is only needed to avoid that artificial situation; ie. the game can be played quite successfully (except for the level 25 timing problem) with the "stack reset" patch on its own (which is what I've been doing).

    Note also, as you suggested, that it's best to assemble the changes directly into memory rather than using a BASIC POKE routine as the latter expands the PROG space and reduces the free space between the calculator stack & machine stack and can cause new problems.
  • edited May 2011
    Yes, I assumed your sentence "Nope, that's not the solution" meant it was another step in the right direction but still not enough to solve it. However what you really meant was that this change would make things worse!

    It didn't take me long to realize my mistake and correct the bugfixed version, but thanks for pointing this out anyway.
    Creator of ZXDB, BIFROST/NIRVANA, ZX7/RCS, etc. I don't frequent this forum anymore, please look for me elsewhere.
  • edited May 2011
    Cannot say anything other than excellent 8)
  • edited May 2011
    That would be something I'd of come back to (checking 128 specifically +2A / +3 compatibility) at a later date. :)

    I might Alcatraz / Speedlock mine so its a bit quicker loading on a real 48 machine.
  • edited May 2011
    Spot the bug:
    200 GOTO con : REM this gets keyboard/joystick input and does GOTO 205
    205 IF key=0 THEN FOR i=1 TO 100: NEXT i: GOTO 260
    210 IF (key<>2 AND key<>3 AND key<>5 AND key>=0) THEN GOSUB 405: POKE 23675,164: POKE 23676,221: GOSUB 1000: GOTO 260
    220 IF (key<>1 AND key<>3 AND key<>4 AND y<31) THEN GOSUB 505: POKE 23675,78: POKE 23676,222: GOSUB 1100: GOTO 260
    230 IF (key<>1 AND key<>2 AND key<>5 AND p=2 AND dir=0) THEN LET y=y+1: GOSUB 1200: LET y=y-1: GOTO 260
    240 IF (key<>1 AND key<>2 AND key<>4 AND p=2 AND dir=1) THEN LET GOSUB 1200
    260 GOSUB 1400
    
    300 GOTO 200
    
    405 LET e=0: IF r<>1 THEN RETURN
    410 IF ATTR(x,y)>64 THEN RETURN
    415 IF ATTR(x+1,y)>64 THEN RETURN
    420 IF ATTR(x,y)<>7 THEN GOTO 260
    425 IF ATTR(x+1,y)<>7 THEN GOTO 260
    430 RETURN
    
    505 LET e=1: IF r<>1 THEN RETURN
    510 IF ATTR(x+1,y+1)>64 THEN RETURN
    515 IF ATTR(x,y+1)>64 THEN RETURN
    520 IF ATTR(x,y+1)<>7 THEN GOTO 260
    525 IF ATTR(x+1,y+1)<>7 THEN GOTO 260
    530 RETURN
    

    Fix this and you fix the game :-)
  • edited May 2011
    Jimmy wrote: »
    Spot the bug

    Did you reverse-engineer the Frank N Stein code back to MCODER BASIC? Impressive!

    The stack problem is very clear in the code above. Here's how I would have fixed this particular problem:
    200 GOTO con : REM this gets keyboard/joystick input and does GOTO 205
    205 IF key=0 THEN FOR i=1 TO 100: NEXT i: GOTO 260
    210 IF NOT (key<>2 AND key<>3 AND key<>5 AND key>=0) THEN GOTO 220
    211 LET e=0: IF r<>1 THEN GOTO 219
    212 IF ATTR(x,y)>64 THEN GOTO 219
    213 IF ATTR(x+1,y)>64 THEN GOTO 219
    214 IF ATTR(x,y)<>7 THEN GOTO 260
    215 IF ATTR(x+1,y)<>7 THEN GOTO 260
    219 POKE 23675,164: POKE 23676,221: GOSUB 1000: GOTO 260
    220 IF NOT (key<>1 AND key<>3 AND key<>4 AND y<31) THEN GOTO 230
    221 LET e=1: IF r<>1 THEN GOTO 229
    222 IF ATTR(x+1,y+1)>64 THEN GOTO 229
    223 IF ATTR(x,y+1)>64 THEN GOTO 229
    224 IF ATTR(x,y+1)<>7 THEN GOTO 260
    225 IF ATTR(x+1,y+1)<>7 THEN GOTO 260
    229 POKE 23675,78: POKE 23676,222: GOSUB 1100: GOTO 260
    230 IF (key<>1 AND key<>2 AND key<>5 AND p=2 AND dir=0) THEN LET y=y+1: GOSUB 1200: LET y=y-1: GOTO 260
    240 IF (key<>1 AND key<>2 AND key<>4 AND p=2 AND dir=1) THEN LET GOSUB 1200
    260 GOSUB 1400
    300 GOTO 200
    

    In the code above I just "inlined" sub-routine 405-430 directly into lines 211-215, and sub-routine 505-530 directly into lines 221-225, without changing anything else.
    Creator of ZXDB, BIFROST/NIRVANA, ZX7/RCS, etc. I don't frequent this forum anymore, please look for me elsewhere.
  • edited May 2011
    Did you reverse-engineer the Frank N Stein code back to MCODER BASIC? Impressive!
    I am finishing off a guide on decompiling :smile:

    Actually there's quite a few more bugs in this program :sad:

    I fixed the 4 bugs in the earlier code and played the game. Unfortunately despite using the 4 fixes the stack was still getting bigger.

    If you follow the GOSUB 1200 from the earlier block, you'll see:
    1200 LET dr=0: POKE 59197,0: POKE 53799,1
    1201 IF ATTR(x+2,y)=20 THEN POKE 53799,5: GOSUB 3300: GOTO 1211
    1202 IF ATTR(x+2,y)=4 THEN POKE 53799,2: GOTO 1211
    1203 IF ATTR(x+2,y)=5 THEN POKE 53799,3: GOSUB 3310: GOTO 1211
    1204 IF ATTR(x+2,y)=15 THEN GOSUB 3205: GOTO 1211
    1205 IF ATTR(x+2,y)=3 THEN GOSUB 3000: GOTO 1211
    1206 IF ATTR(x+2,y)=6 THEN GOSUB 3500: GOTO 1211
    1207 IF (ATTR(x+1,y+1)=4 AND num=8) THEN GOSUB 3615
    1208 IF (ATTR(x+2,y)<>2 AND ATTR(x+2,y)=69) THEN POKE 23402,50: POKE 23403,1: GOTO 1215
    1209 IF (ATTR(x+2,y)=69 AND key<>1 AND key<>2) THEN POKE 23402,30: POKE 23403,1: GOTO 1215
    1211 IF die=0 THEN RETURN
    1212 IF die=1 THEN GOTO 1530
    1215 LET dr=dr+1: IF die=1 THEN GOTO 1530
    1217 IF (ATTR(x+2,y)>64 AND ATTR(x+2,y)=69) THEN LET die=1: GOTO 1530
    
    As you can probably see if you've died then it does a GOTO 1530, which does:
    1530 IF dr<=4 THEN GOTO 1540
    1532 POKE 23675,166: POKE 23676,219 : IF e=1 THEN PRINT AT y,x;" ";AT y-1,x+1;"/159/160": GOTO 1540
    1535 IF e=0 THEN PRINT AT y,x;" ";AT y,x+1;"/162/161"
    1540 POKE 23403,0: FOR q=1 TO 10: POKE 23402,150-(q*10): PAUSE 5: NEXT q
    1550 LET liv=liv-1: IF liv=0 THEN GOTO 1600
    1560 GOTO 100
    
    Then if you follow line 100 onwards you'll end up in the loop starting at line 200 again. So every time you lose a life you make the stack get bigger!!

    On further investigation the routine at line 1200 is CALLed from TEN different places! The number of patches is going to be quite long...

    Line 1560 if you're interested starts at address 34151. Battle Bunny had already spotted this leak and the earlier 4 leaks. I'm now going to work out why the patch at 35449 is needed.

    If you set a conditional breakpoint at 29575 to check when SP<$60A7 you will soon pick up stack leaks.
  • edited May 2011
    I am finishing off a guide on decompiling
    very interesting
  • jpjp
    edited May 2011
    Jimmy wrote: »
    ...As you can probably see if you've died then it does a GOTO 1530, which...

    ...is considered harmful... :razz:

    Nice work, by the way!
  • edited May 2011
    Jimmy wrote: »
    On further investigation the routine at line 1200 is CALLed from TEN different places! The number of patches is going to be quite long...

    That's why I decided a while back that I was on a hiding to nothing trying to track down all the JPs breaking out of sub-routines and adopted the Gordian knot approach of ignoring all the complexities and just resetting the stack pointer every level (prompted by my discovering that the programmer was doing that anyway on the options screen).

    The "Invalid colour" crashes in Crusoe are caused by the same dodgy programming technique as well, although there's also other things wrong with it. I'm just sorting out an assembler listing so I can fix any obvious errors before I resume playing it, as it was getting a bit tedious having to keep restarting.
  • edited May 2011
    Battle Bunny's approach to fix this problem can be easily explained from this source code provided by Jimmy:
    200 GOTO con : REM this gets keyboard/joystick input and does GOTO 205
    205 IF key=0 THEN FOR i=1 TO 100: NEXT i: [B]GOTO 260[/B]
    210 IF (key<>2 AND key<>3 AND key<>5 AND key>=0) THEN GOSUB 405: POKE 23675,164: POKE 23676,221: GOSUB 1000: [B]GOTO 260[/B]
    220 IF (key<>1 AND key<>3 AND key<>4 AND y<31) THEN GOSUB 505: POKE 23675,78: POKE 23676,222: GOSUB 1100: [B]GOTO 260[/B]
    230 IF (key<>1 AND key<>2 AND key<>5 AND p=2 AND dir=0) THEN LET y=y+1: GOSUB 1200: LET y=y-1: [B]GOTO 260[/B]
    240 IF (key<>1 AND key<>2 AND key<>4 AND p=2 AND dir=1) THEN LET GOSUB 1200
    260 GOSUB 1400
    
    300 GOTO 200
    
    405 LET e=0: IF r<>1 THEN RETURN
    410 IF ATTR(x,y)>64 THEN RETURN
    415 IF ATTR(x+1,y)>64 THEN RETURN
    420 IF ATTR(x,y)<>7 THEN [B]GOTO 260[/B]
    425 IF ATTR(x+1,y)<>7 THEN [B]GOTO 260[/B]
    430 RETURN
    
    505 LET e=1: IF r<>1 THEN RETURN
    510 IF ATTR(x+1,y+1)>64 THEN RETURN
    515 IF ATTR(x,y+1)>64 THEN RETURN
    520 IF ATTR(x,y+1)<>7 THEN [B]GOTO 260[/B]
    525 IF ATTR(x+1,y+1)<>7 THEN [B]GOTO 260[/B]
    530 RETURN
    

    There are 8 ocurrences of GOTO 260 in the code above, which corresponds exactly to the 8 ocurrences of JP $7531 that Battle Bunny identified. The first 4 ocurrences are correct, but the last 4 ocurrences (inside sub-routines 405-430 and 505-530 invoked using GOSUB) make the sub-routine exit without using RETURN, thus leaving a return address in the stack. If it happens too many times, the stack grows too large and eventually crashes the game.

    Battle Bunny fixed this problem by removing the return address from the stack in such cases. If the BASIC language had a POP statement to remove a return address from the stack, his fix would be equivalent to this:
    200 GOTO con : REM this gets keyboard/joystick input and does GOTO 205
    205 IF key=0 THEN FOR i=1 TO 100: NEXT i: GOTO 260
    210 IF (key<>2 AND key<>3 AND key<>5 AND key>=0) THEN GOSUB 405: POKE 23675,164: POKE 23676,221: GOSUB 1000: GOTO 260
    220 IF (key<>1 AND key<>3 AND key<>4 AND y<31) THEN GOSUB 505: POKE 23675,78: POKE 23676,222: GOSUB 1100: GOTO 260
    230 IF (key<>1 AND key<>2 AND key<>5 AND p=2 AND dir=0) THEN LET y=y+1: GOSUB 1200: LET y=y-1: GOTO 260
    240 IF (key<>1 AND key<>2 AND key<>4 AND p=2 AND dir=1) THEN LET GOSUB 1200
    260 GOSUB 1400
    
    300 GOTO 200
    
    405 LET e=0: IF r<>1 THEN RETURN
    410 IF ATTR(x,y)>64 THEN RETURN
    415 IF ATTR(x+1,y)>64 THEN RETURN
    420 IF ATTR(x,y)<>7 THEN [B]GOTO 5029[/B]
    425 IF ATTR(x+1,y)<>7 THEN [B]GOTO 5029[/B]
    430 RETURN
    
    505 LET e=1: IF r<>1 THEN RETURN
    510 IF ATTR(x+1,y+1)>64 THEN RETURN
    515 IF ATTR(x,y+1)>64 THEN RETURN
    520 IF ATTR(x,y+1)<>7 THEN [B]GOTO 5029[/B]
    525 IF ATTR(x+1,y+1)<>7 THEN [B]GOTO 5029[/B]
    530 RETURN
    
    [B]5029 POP: GOSUB 1400: GOTO 200[/B]
    

    This change was enough to fix the main cause of stack growth. For all other (less serious) cases, instead of fixing one by one, he simply resets the stack at the end of each level, which seems like a great solution to me.
    Creator of ZXDB, BIFROST/NIRVANA, ZX7/RCS, etc. I don't frequent this forum anymore, please look for me elsewhere.
  • edited May 2011
    BTW the previous post made me realize that this part of the final bugfix:
    PATCH1: POP  BC                  ; 193
            CALL $81A4               ; 205 164 129
            JP   $7387               ; 195 135 115
    

    Could be reduced to this:
    PATCH1: POP  BC                  ; 193
            JP   $7531               ; 195 49 117
    

    Although it's not worth it to change it anymore :)
    Creator of ZXDB, BIFROST/NIRVANA, ZX7/RCS, etc. I don't frequent this forum anymore, please look for me elsewhere.
Sign In or Register to comment.