Make a level with one room and set the "room below" and the "room to the right" links to point to the current room.
The game will hang after you enter this room.
It seems to me that the hang can be delayed or disabled by putting floors into the room. Sometimes the game hangs when the kid is about to jump off the floors.
I noticed this bug originally in the DOS version, but it is also present in other versions:
Ths SNES version is also affected by this bug.
I also tried this in the Mac version (using the Total Pack), and it also hangs!
Then I tried the Apple II (using this), and it also hangs.
The level format of the following versions is the same as that of the DOS version. I overwrote the original level 1 with the edited using a hex editor.
The Atari ST version also hangs.
The C64 version also hangs.
The SEGA-CD version also hangs.
So it looks like this bug was carried over to many versions.
If I convert the level to PoP2 (DOS), it does not hang.
The game hangs on certain irregular room links
Re: The game hangs on certain irregular room links
Interesting. I've tried this and I think only the room below needs to point to the current room.David wrote:Make a level with one room and set the "room below" and the "room to the right" links to point to the current room. [...] It seems to me that the hang can be delayed or disabled by putting floors into the room. Sometimes the game hangs when the kid is about to jump off the floors.
As soon as the prince moves to a column that doesn't have a tile (does not need to be in the row the prince is on), the game hangs.
After the game hangs, if you wait 36 or so seconds, you hear the prince scream and then you can either keep playing (even though the screen doesn't update; probably also depends on what your room looks like) or press Ctrl+r to restart and see the main screen with different colors. Then, when you enter level 1, the prince has 232 or 132 lives. (The number of lives may be random, I've just tested it twice.)
Re: The game hangs on certain irregular room links
You're right, only the "down" link is needed.Norbert wrote:Interesting. I've tried this and I think only the room below needs to point to the current room.
I started with all directions linked to itself, and removed each until the hang disappeared; then I put that back.
This time becomes shorter if you increase the cycles in DOSBox.Norbert wrote:After the game hangs, if you wait 36 or so seconds, you hear the prince scream
If I press shift-L a few times, the colors of the room (if it's not empty) change to palace colors - I guess that's when I reach level 4.Norbert wrote:and then you can either keep playing (even though the screen doesn't update; probably also depends on what your room looks like)
The title screen will use some already loaded palette.Norbert wrote:or press Ctrl+r to restart and see the main screen with different colors.
If you make a screenshot with Ctrl-F5, it will be saved as an indexed (paletted) image.
Open the image in GIMP and open the Colormap Dialog. The palette that the title screen should use is the 4th or 5th row from the bottom.
The wrong palette is either the 1st row (hardcoded palette) or the 6th (palace, if you pressed shift-L until level 4).
How did you check that? The easiest way I can think of is to save the game and look into prince.sav.Norbert wrote:Then, when you enter level 1, the prince has 232 or 132 lives. (The number of lives may be random, I've just tested it twice.)
All these make me think that the game overwrites some memory that it shouldn't.
There is an unofficial version of DOSBox that can save states: http://ykhwong.x-y.net/xe/143
I saved the state before and after the hang, and for example, one of the hidden reviews are changed: from "game so" to "game!so".
There are other changes, but this is the only one that is obviously wrong.
Not long after my first post I found (with a debugger) the code that hangs:
Code: Select all
seg006:1199 check_spike_below proc far ; CODE XREF: check_stuff+7D↑P
seg006:1199 ; sub_F48+60↑P
seg006:1199
seg006:1199 right_col = word ptr -0Ah
seg006:1199 tile_col = word ptr -8
seg006:1199 tile_row = word ptr -6
seg006:1199 room = word ptr -4
seg006:1199 not_finished = word ptr -2
seg006:1199
seg006:1199 push bp
seg006:119A mov bp, sp
seg006:119C sub sp, 0Ah
seg006:119F push word_1E9F0
seg006:11A3 push cs
seg006:11A4 call near ptr get_tile_div_mod_m7
seg006:11A7 mov [bp+right_col], ax
seg006:11AA or ax, ax
seg006:11AC jl loc_7EDD
seg006:11AE mov al, char_curr_row
seg006:11B1 cbw
seg006:11B2 mov [bp+tile_row], ax
seg006:11B5 mov al, char_room
seg006:11B8 cbw
seg006:11B9 mov [bp+room], ax
seg006:11BC push xpos
seg006:11C0 push cs
seg006:11C1 call near ptr get_tile_div_mod_m7
seg006:11C4 mov [bp+tile_col], ax
seg006:11C7 jmp short loc_7EA7
seg006:11C9 ; ───────────────────────────────────────────────────────────────────────────
seg006:11C9
seg006:11C9 loc_7E79: ; CODE XREF: check_spike_below+81↓j
seg006:11C9 mov al, curr_tile2
seg006:11CC sub ah, ah
seg006:11CE push ax
seg006:11CF push cs
seg006:11D0 call near ptr tile_is_floor
seg006:11D3 or ax, ax
seg006:11D5 jnz reached_bottom
seg006:11D7 cmp curr_room, 0 ; have we reached a bottomless pit?
seg006:11DC jz reached_bottom
seg006:11DE mov ax, curr_room
seg006:11E1 cmp [bp+room], ax ; are we in a different room?
seg006:11E4 jnz reached_bottom
seg006:11E6 inc [bp+tile_row] ; look further down
seg006:11E9 mov [bp+not_finished], 1
seg006:11EE
seg006:11EE reached_bottom: ; CODE XREF: check_spike_below+3C↑j
seg006:11EE ; check_spike_below+43↑j ...
seg006:11EE cmp [bp+not_finished], 0
seg006:11F2 jnz loc_7EB6
seg006:11F4 inc [bp+tile_col] ; look further right
seg006:11F7
seg006:11F7 loc_7EA7: ; CODE XREF: check_spike_below+2E↑j
seg006:11F7 mov ax, [bp+right_col]
seg006:11FA cmp [bp+tile_col], ax ; are we over the right edge?
seg006:11FD jg loc_7EDD
seg006:11FF mov al, char_curr_row
seg006:1202 cbw
seg006:1203 mov [bp+tile_row], ax ; in this column, go to the original row
seg006:1206
seg006:1206 loc_7EB6: ; CODE XREF: check_spike_below+59↑j
seg006:1206 mov [bp+not_finished], 0
seg006:120B push [bp+room] ; room
seg006:120E push [bp+tile_col] ; tile_col
seg006:1211 push [bp+tile_row] ; tile_row
seg006:1214 push cs
seg006:1215 call near ptr get_tile
seg006:1218 cmp al, tiles_2_spike
seg006:121A jnz loc_7E79
seg006:121C push curr_room
seg006:1220 mov al, curr_tile_pos
seg006:1223 sub ah, ah
seg006:1225 push ax
seg006:1226 call animate_spike_2
seg006:122B jmp short reached_bottom
seg006:122D ; ───────────────────────────────────────────────────────────────────────────
seg006:122D
seg006:122D loc_7EDD: ; CODE XREF: check_spike_below+13↑j
seg006:122D ; check_spike_below+64↑j
seg006:122D mov sp, bp
seg006:122F pop bp
seg006:1230 retf
seg006:1230 check_spike_below endp
- The search reaches room 0.
- The search leaves the current room. More precisely, after the game checked the first tile that is not in the current room. (That is, if the spike is in the top row of the room below, it will still come out.)
- The search finds a tile that has floor. If it's a spike then it comes out.
The second could be replaced with: The search goes below row 2.
If the room below is the current room, then the first two can't happen. If the current column is empty then the third cannot happen.
I examined the saved states a bit more:
It seems to me that this is a stack overflow. One of the functions called by get_tile() calls itself everytime it crosses a room boundary.
When tile_row reaches 159Dh, the search stops, because the stack has overwritten the room links! - And of course everything after it.
The "spill" consists of sequences of "E8 00 nn nn", where nnnn is the segment where get_tile was relocated.
With hex codes:
Code: Select all
000096F9:i55 push bp
000096FA:i8BEC mov bp,sp
000096FC:i83EC0A sub (w) sp,+0A
000096FF:iFF36103D push (w) [+3D10]
00009703:i0E push cs
00009704:iE837F2 calln file:0000893E
00009707:i8946F6 mov [bp-0A],ax
0000970A:i0BC0 or ax,ax
0000970C:i7C7F jl file:0000978D
0000970E:iA0273D mov al,[+3D27]
00009711:i98 cbw
00009712:i8946FA mov [bp-06],ax
00009715:iA02B3D mov al,[+3D2B]
00009718:i98 cbw
00009719:i8946FC mov [bp-04],ax
0000971C:iFF361C3D push (w) [+3D1C]
00009720:i0E push cs
00009721:iE81AF2 calln file:0000893E
00009724:i8946F8 mov [bp-08],ax
00009727:iEB2E jmps file:00009757
00009729:iA0F942 mov al,[+42F9]
0000972C:i2AE4 sub ah,ah
0000972E:i50 push ax
0000972F:i0E push cs
00009730:iE855F4 calln file:00008B88
00009733:i0BC0 or ax,ax
00009735:i7517 jne file:0000974E
00009737:i833E2A4300 cmp (w) [+432A],+00
0000973C:i7410 je file:0000974E
0000973E:iA12A43 mov ax,[+432A] ; this
00009741:i3946FC cmp [bp-04],ax ; this
00009744:i7508 jne file:0000974E ; this
00009746:iFF46FA inc [bp-06]
00009749:iC746FE0100 mov [bp-02],0001
0000974E:i837EFE00 cmp (w) [bp-02],+00
00009752:i7512 jne file:00009766
00009754:iFF46F8 inc [bp-08]
00009757:i8B46F6 mov ax,[bp-0A]
0000975A:i3946F8 cmp [bp-08],ax
0000975D:i7F2E jg file:0000978D
0000975F:iA0273D mov al,[+3D27]
00009762:i98 cbw
00009763:i8946FA mov [bp-06],ax
00009766:iC746FE0000 mov [bp-02],0000
0000976B:iFF76FC push (w) [bp-04]
0000976E:iFF76F8 push (w) [bp-08]
00009771:iFF76FA push (w) [bp-06]
00009774:i0E push cs
00009775:iE8EEED calln file:00008566
00009778:i3C02 cmp al,02
0000977A:i75AD jne file:00009729
0000977C:iFF362A43 push (w) [+432A]
00009780:iA0ED42 mov al,[+42ED]
00009783:i2AE4 sub ah,ah
00009785:i50 push ax
00009786:i9AE3085508 callf file:00008E33
0000978B:iEBC1 jmps file:0000974E
0000978D:i8BE5 mov sp,bp
0000978F:i5D pop bp
00009790:iCB retf
This seems to fix the problem but the behavior of the spikes doesn't seem to change. (Including the case when the spike is in the top row of the room below.)
The corresponding part in the Apple II source: (CTRLSUBS.S)
Code: Select all
*-------------------------------
*
* C H E C K S P I K E S
*
* Spikes spring out when char passes over them (at any
* height).
*
*-------------------------------
CHECKSPIKES
lda rightej
jsr getblockxp
bmi ]rts
sta tempright
* for blockx = leftblock to rightblock
lda leftej
jsr getblockxp
:loop sta blockx
jsr sub
lda blockx
cmp tempright
beq ]rts
clc
adc #1
jmp :loop
sub sta tempblockx
lda CharBlockY
sta tempblocky
lda CharScrn
sta tempscrn
:loop jsr rdblock1
cmp #spikes
bne :again
jmp trigspikes
:again jsr cmpspace
bne ]rts
lda tempscrn
beq ]rts ;null scrn
cmp CharScrn
bne ]rts ;wait till he's on same screen
inc tempblocky
jmp :loop ;check 1 level below
Re: The game hangs on certain irregular room links
I see that your Prince of Wateria contains an endless fall on level 12 and 13.
It uses two similar rooms. Did you use two rooms to avoid this bug, or because it looks better?
Repetition of Time also contains an endless fall on level 3, but there it consists of three rooms.
It uses two similar rooms. Did you use two rooms to avoid this bug, or because it looks better?
Repetition of Time also contains an endless fall on level 3, but there it consists of three rooms.
Re: The game hangs on certain irregular room links
Because it probably looks a little bit better when the player ends up in that endless fall.David wrote:Did you use two rooms to avoid this bug, or because it looks better?
I never tested it with just one room, so I never ran into the bug this thread is about.
Another impressive analysis you've done in your previous post here, by the way.