Nobody likes Shogun, right? It’s long, linear, and boring. I hate playing through it to test bugs. But there are 4 versions, with nontrivial differences–they did fix some bugs.
I’ve known for many years that the big difference in the object tree between versions 295 and 311 is the addition of a second “Main Deck” location. I always assumed that represented some bug fix, that there must have been some way in which the Main Deck in the Escape chapter would give an inappropriate message belonging to the Voyage to Osaka, or vice versa. I still haven’t found such a bug. Maybe they were just trying to simplify the code by separating the two locations.
But this change did actually introduce a new bug.
In versions 311 and up, when you get to Wharf in the Escape chapter, if you go up instead of east, then instead of the galley Mariko is telling you to board, you go back to the Main Deck from the Voyage to Osaka chapter! The description there tells you the rowers are resting after their ordeal, but they aren’t there. You can go aft to
the quarterdeck, where the description talks about the helm and safety line that aren’t there. Mariko keeps telling you to get aboard the galley, but she’s not there, and you are stuck.
There are two ways to get from Wharf to Main Deck, and they clearly only changed one of the two exits. Having objects named MAIN-DECK and MAIN-DECK-2 got too confusing.
Interesting! Yes, despite enjoying the TV show back in the day, and despite having read some of the book (I was young, it’s a huge book!), I’ve never really given Shogun the game a fair go.
We have the know-how and the tools to try and fix bugs, so if you have a nice clear description of “what would fix it is 1,2,3 etc” then we can have a go.
Bocfel includes a couple of Shogun patches, but not for this particular problem.
These patches are only applied to the last release, 322-890706.
Somewhat relatedly, there is also a bug in Shogun releases 283 through 295 that will crash Bocfel with a stack underflow error if you try to BOW. I’m not sure how Frotz handles this, but it doesn’t complain.
But there are 4 versions, with nontrivial differences–they did fix some bugs.
It seems there are 19 known versions, but I’m not sure which ones were actually released.
Stack underflow could happen because (A) a routine returns, and there is no full stack frame on the stack, or (B) code tries to pull a single value from the stack, or read the top value on the stack, when the stack has no values on it.
(A) can only happen in the initial routine. In Inform 6, I think the programmer is protected, so they can’t cause this error.
When (B) happens, the terp can just give some dummy value back and hope for the best. If the value wasn’t that important, the game should keep working.
The offending line is this: (release 295-890321) 1650a: 6d 01 4a STORE [L00],G3a
In release 311-890510, it is changed to this: 16b87: 2d 01 f2 STORE L00,Ge2
At a glance this looks to me like a null pointer dereference, caused by treating the local variable as a pointer when it shouldn’t be, but I find these opcodes confusing.
EDIT: According the Z-Machine standards document, opcode 0x6d is store variable, variable while 0x2d is store constant, variable, but I can’t square that with the disassembly created by txd.
When (B) happens, the terp can just give some dummy value back and hope for the best. If the value wasn’t that important, the game should keep working.
Bocfel tends to be very strict about these things. “Hope for the best” is not how it rolls.
This reads the value in L00, and uses this value as a variable number, e.g. if the value held in L00 is 0, this should read the value in G3a and store on the stack.
I’m not certain right now if this means it modifies the top value on the stack (which has to be there), or if it pushes a new value onto the stack. For inc, inc_chk etc, it operates directly on the top value on the stack, and I think this works the same.
The most updated copy of source code was by @Shin back in 2020, editing line 1607 was (as expected) easy but I hit 100+ errors on trying to compile; all of which look something like this:
[error MDL0200] : calling unassigned atom: GET-CLASSIFICATION
in SYNTAX called at C:\Users\User\Documents\ZILF\development\shogun-develop\shogun-develop\syntax.zil:82
in INSERT-FILE called at a5.zil:60
Line 82 is: SYNTAX VERBOSE = V-VERBOSE
So, i’m curious, and will tinker some more!
@heasm66@eriktorbjorn could you guys spot anything super obvious & easy before I enter a rabbithole?
I don’t have access to my build computer at the moment, but I can look at it tomorrow. This is a “new parser”-game so you need to link in the Infocom zillib that does a lot of MDL stuff. Both zillib and zillib\parser needs to be in the path.
I found the script I used to build it. It looks like this
@echo off
setlocal
set PATH=..\..\ZILF\zilf-0.9.6-win-x64\net5.0;%PATH%
IF EXIST shogun\a5.z? del shogun\a5.z?
IF EXIST shogun\a5*.*zap del shogun\a5*.*zap
zilf -w -ci shogun\a5.zil -ip shogun -ip zillib -ip zillib\parser
zapf -ab shogun\a5.zap > shogun\a5_freq.xzap
del shogun\a5_freq.zap
zapf shogun\a5.zap
pause
From the 2003 vintage Infocom bug list (The Infocom Bugs List: James Clavell's Shogun) (which reports only a different bug, which I think is at least cognate to the one Nathan noticed), nor in the Nathan Simpson’s list (Nathan Simpson's list of Infocom bugs) so we’re effectively facing a new bug. Kudos to Nathan in finding it !
Congrats and
Best regards from Italy,
dott. Piergiorgio.
Perhaps I should make a separate thread about “my” Shogun bug, but I wonder what you all think. Is it safe to patch it to just set L00 directly, like the later versions do, instead of using the indirect store instruction?
As far as I can tell it works fine, but I’m having a hard time understanding the intention of the original code.
STORE [L00], G3a means “pop a value from the stack, then store the value of register $3A into that numbered register”. STORE L00, Ge2 means “store the value of register $E2 into the top value of the stack, overwriting whatever is there”.
The former is basically never used (or rather, basically never intentionally used), so my guess, not knowing any of the context, is that the latter was intended. You’d probably want to change it to STORE L00, G3a: “store the value of register $3A into the top value of the stack, overwriting whatever is there”. My guess is that register $3A in release 295 corresponds to register $E2 in release 311; some new variables got added somewhere, so the allocation of registers got shuffled around.
STORE is a confusing instruction because it’s not a store instruction, in the usual Z-machine sense—it doesn’t use the opcode field that indicates which register to put the output in. Instead, one of its inputs is the number of the register to use. And in this case, the intent is probably “constant 0” (meaning register #0, the top of the stack), not “register 0” (read from register #0 = pop a value from the stack, then use that result as the register number).
(TXD uses “local” and “global” for what I just call “registers” for clarity. Register 0 is the top of the stack, which is neither local nor global, it’s something else, but the lower-numbered registers are the “locals”, which is why TXD calls it L00 instead of G00.)
Are you sure about this? It seems to me that TXD calls the top of the stack “(SP)” while L00 is in fact the first “real” local variable (register 1, while the top of the stack is register 0.) Which of course is TXD trying to simplify things but instead making everything more confusing.
The sensible thing to do would have been to not use L00 at all, and start counting the local variables from 1, so that their index numbers would match those of the corresponding registers.