Citadel Item Randomiser 0.4 (updated)

reminisce about bbc micro & electron games like chuckie egg, repton, elite & exileRelated forum: adventures


Diminished
Posts: 98
Joined: Fri Dec 08, 2017 9:47 pm
Contact:

Citadel Item Randomiser 0.4 (updated)

Post by Diminished » Thu Jan 04, 2018 3:54 am

The latest version of this is the much-improved 0.4, which you can get lower down this thread.

(The original first post in this thread follows).

CITADEL ITEM RANDOMISER
Going Rogue

INTRODUCTION
Item randomiser utilities are well known to aficionados of non-linear console games, being particularly common amongst Zelda and Metroid titles. They aim to breathe new life into well-worn classics by randomising the positions of items within the game, so a new and novel route needs to be found by the player in order to complete it.

Basic randomisers simply shuffle item locations brainlessly, which can lead to deadlocked situations in which the player cannot know for sure if a particular layout can be solved. The most simple example is when a key ends up stuck behind a door that requires that same key to open it. More advanced randomisers (including this one) incorporate solvers, which are able to check whether or not a particular permutation may be completed.

Originally written as a proof-of-concept in Ruby, this Citadel randomiser has been ported to BBC BASIC so it can be run directly on original hardware or from within an emulator.

SEEDS
Each supported permutation is represented by a single integer or "seed" in the range 1 to 999,999,999. The randomiser can either generate a solvable seed automatically, or the user may supply his or her own. Please note that since permutations are generated using BASIC's RND() keyword, seeds may not be compatible between different versions of BBC BASIC.

QUIRKS
Two items in the game are not randomised and are always available from their original locations. The first is the crystal obtained from the sarcophagus in The Pyramid, since this is not an item that appears on an item pad but rather is traded directly into the player's inventory. The second unrandomised item is the figurine on the alien planet. Once the Star Port has been destroyed, it is impossible to return to the alien planet to pick up any items that may have been left there. Choose your route wisely!

There is a "warp" in this game which involves taking a trampoline to The Ocean on the east side of The Temple (where the Egyptian statuette is usually found), and using it to climb up into the game's title screen. By falling off the right side of the title screen and down one more screen past The Ocean, then holding left as you fall further, it is possible to land on the Central Tower battlements and skip solving the puzzle that normally would require the bucket. The solver is aware of this trick, and may be configured to either accept or reject seeds that use this alternative to collecting the bucket. (This glitch may also be used to skip collecting the East Tower key, but currently the solver does not acknowledge this).

No special status is granted to the three secret rooms that normally contain the crowns. The player is expected to know where these are, and any item may appear in them. Additionally, players do not win £100 for collecting the three crowns.

Not all seeds will yield the maximum 99 points; however it should be possible to destroy the Star Port and prevent the alien invasion on any verified seed.

LIMITATIONS
This software was tested with the original BBC Micro Model B disc version Stairway To Hell disc image of Citadel (disc image "Citadel.ssd" having an MD5 of c03f242992408838157844070f08a6a4). Electron and BBC tape versions are not supported at this time, and it has not been tested on the "Play It Again, Sam" BBC version, either. At this time it has only been tried under emulation, and not on vintage hardware.

At present, this tool works by patching the CITAM file on the disc for each new seed, and as such requires write access to the disc. *** DO NOT USE IT ON AN ORIGINAL COPY OF THE GAME ***. Emulators will need to be configured to allow writes to the disc image. The software does have a facility for restoring the CITAM file to its original state (answer "N" to the "Randomise?" question). In-memory patching of the binary would be a neater solution, but this software is not currently capable of it.

The solver is written in BASIC, uses poor quality algorithms and is slow. On a standard BBC B, it takes several seconds to check each possible seed for solvability, and so in unlucky cases it may take a minute or two for it to find a suitable seed.

DISC IMAGE
This disc image contains a copy of the game with the randomiser utility added to the root directory:
Citadel-randomiser-0.1.ssd
(200 KiB) Downloaded 35 times
Type CHAIN "RANDO" to start the randomiser. Once it has modified the CITAM file, you can CHAIN "CITADEL" to launch the game as normal. To re-randomise with a new seed or restore CITAM to its original state, simply press BREAK and then CHAIN "RANDO" again.

SOURCE

Code: Select all

   10 MODE 7
   20 CLEAR:RZ%=FALSE:AS%=FALSE:SD%=-1:IW%=TRUE:DIM ROOMS%(36):DIM DTA%(38):DIM INV%(38)
   30 PRINT "Citadel Randomiser v0.1":PRINT "by 'Diminished', January 2018":PRINT
   40 PROCfill
   50 RZ%=FNyn("Randomise"):IF NOT RZ% PRINT "Restoring default items ...":GOTO 180
   60 AS%=FNyn("Automatic randomisation seed"):IF AS% THEN 130
   70 REPEAT:INPUT "Enter seed: " SD%:IF SD%<=0 PRINT "Seed must be a positive integer."
   80 UNTIL SD%>0
   90 PRINT:PROCshuffle(SD%)
  100 IF FNsolve THEN 180
  110 PRINT:PRINT CHR$(129);"Warning: This seed looks unsolvable!"
  120 IW%=TRUE:IF NOT FNyn("Are you sure") THEN PRINT "Aborting.":END ELSE 180
  130 IW%=FNyn("Allow island trampoline warp")
  140 PRINT:REPEAT
  150 GI%=RND(-TIME)
  160 IF RZ% PROCshuffle(RND(999999999))
  170 UNTIL FNsolve
  180 PROCsave
  190 END
  200 DEF FNyn(M$)
  210 PRINT M$;" (Y/N)? ";
  220 LOCAL A$
  230 REPEAT:A$=GET$:UNTIL A$="y" OR A$="n" OR A$="Y" OR A$="N"
  240 PRINT A$
  250 =(A$="Y" OR A$="y")
  260 DEF PROCdebug
  270 PRINT
  280 LOCAL LI%, LJ%
  290 LJ%=0
  300 FOR LI%=0 TO 37
  310 PRINT ;LI%;" => ";INV%(LI%);"  ";
  320 IF LI% MOD 4=3 PRINT
  330 IF INV%(LI%) LJ%=LJ%+1
  340 NEXT
  350 PRINT "------"
  360 ENDPROC
  370 DEF FNibr(RID%)
  380 LOCAL LI%, LJ%
  390 LJ%=-1
  400 FOR LI%=0 TO 37
  410 IF RID%=DTA%(LI%) LJ%=LI%:LI%=38
  420 NEXT
  430 =LJ%
  440 DEF PROCfill
  450 LOCAL LI%, LJ%
  460 FOR LI%=0 TO 37
  470 READ LJ%
  480 DTA%(LI%)=LJ%
  490 IF LI%<27 ROOMS%(LI%)=LJ%
  500 IF LI%=28 ROOMS%(LI%-1)=LJ%
  510 IF LI%>29 ROOMS%(LI%-2)=LJ% 
  520 NEXT
  530 ENDPROC
  540 DEF PROCshuffle(SEED%)
  550 PRINT "Trying seed ";SEED%;" ... ";
  560 LOCAL LI%, LJ%
  570 LI%=RND(-SEED%)
  580 FOR LI%=0 TO 37:DTA%(LI%)=-1:NEXT
  590 FOR LI%=0 TO 35
  600 REPEAT:LJ%=RND(36)-1:UNTIL DTA%(LJ%)<0
  610 DTA%(LJ%)=ROOMS%(LI%)
  620 NEXT
  630 FOR LI%=37 TO 30 STEP -1
  640 DTA%(LI%)=DTA%(LI%-2)
  650 NEXT
  660 DTA%(28)=DTA%(27)
  670 DTA%(27)=&17
  680 DTA%(29)=&E1
  690 ENDPROC
  700 DEF FNsolve
  710 LOCAL AT%, LI%
  720 AT%=0
  730 FOR LI%=0 TO 37:INV%(LI%)=FALSE:NEXT
  740 INV%(FNibr(&5E))=TRUE:INV%(FNibr(&84))=TRUE:INV%(FNibr(&81))=TRUE
  750 INV%(FNibr(&6C))=TRUE:INV%(FNibr(&87))=TRUE:INV%(FNibr(&85))=TRUE
  760 INV%(FNibr(&5C))=TRUE:INV%(FNibr(&82))=TRUE:INV%(FNibr(&70))=TRUE
  770 INV%(FNibr(&58))=TRUE:INV%(FNibr(&83))=TRUE:INV%(FNibr(&5A))=TRUE
  780 FOR AT%=0 TO 6
  790 IF NOT INV%(17) THEN 810
  800 INV%(FNibr(&86))=TRUE:IF INV%(24) AND INV%(30) INV%(FNibr(&72))=TRUE
  810 IF NOT INV%(14) THEN 840
  820 INV%(FNibr(&D5))=TRUE:IF INV%(2) INV%(FNibr(&D6))=TRUE
  830 IF INV%(26) AND INV%(21) INV%(FNibr(&C4))=TRUE
  840 IF NOT INV%(11) THEN 890
  850 INV%(FNibr(&EA))=TRUE:INV%(FNibr(&68))=TRUE:INV%(FNibr(&64))=TRUE:INV%(FNibr(&1))=TRUE
  860 IF INV%(0) OR INV%(1) INV%(FNibr(&29))=TRUE
  870 IF INV%(0) AND INV%(1) AND INV%(25) AND INV%(7) AND INV%(8) AND INV%(9) AND INV%(10) AND INV%(3) INV%(FNibr(&5))=TRUE:GOTO 990
  880 IF INV%(23) OR INV%(24) OR INV%(30) INV%(FNibr(&90))=TRUE:IF INV%(4) AND INV%(5) AND INV%(6) INV%(FNibr(&A6))=TRUE
  890 IF NOT INV%(16) THEN 910
  900 INV%(FNibr(&95))=TRUE:INV%(FNibr(&97))=TRUE:IF INV%(24) OR INV%(30) INV%(FNibr(&94))=TRUE
  910 IF INV%(12) AND INV%(2) INV%(FNibr(&36))=TRUE
  920 IF INV%(13) INV%(FNibr(&1C))=TRUE
  930 IF INV%(15) INV%(FNibr(&6E))=TRUE:INV%(FNibr(&6F))=TRUE
  940 IF INV%(20) INV%(FNibr(&B))=TRUE
  950 IF IW% AND INV%(3) AND (INV%(24) OR INV%(30)) INV%(FNibr(&B))=TRUE
  960 IF INV%(3) INV%(FNibr(&A3))=TRUE:INV%(FNibr(&CB))=TRUE:INV%(FNibr(&E4))=TRUE
  970 NEXT
  980 PRINT "No solution.":=FALSE
  990 PRINT "OK!":=TRUE
 1000 DEF PROCsave
 1010 PRINT:IF NOT FNyn("OK to modify CITAM file") PRINT "Aborting.":END
 1020 LOCAL LI%,FP%
 1030 FP%=OPENUP("CITAM")
 1040 IF FP%=0 PRINT 'CHR$(129);"ERROR:";CHR$(135);"CITAM file was not found.":PRINT '"Aborting.":END
 1050 PTR#FP%=&480
 1060 FOR LI%=0 TO 38
 1070 IF EOF#FP% PRINT 'CHR$(129);"ERROR:";CHR$(135);"EOF while writing CITAM.":PRINT '"Aborting.":CLOSE#FP%:END
 1080 BPUT#FP%,DTA%(LI%)
 1090 NEXT
 1100 CLOSE#FP%
 1110 PRINT '"All done!"
 1120 ENDPROC
 1130 DATA &72,&D5,&97,&6E,&01,&5E,&1C,&6F,&0B,&A6,&C4,&84,&68,&81,&36,&6C
 1140 DATA &87,&95,&05,&86,&85,&5C,&64,&82,&CB,&E4,&EA,&17,&70,&E1,&58,&83
 1150 DATA &A3,&29,&5A,&90,&94,&D6

Last edited by Diminished on Tue Jan 23, 2018 9:31 am, edited 5 times in total.

User avatar
lurkio
Posts: 1765
Joined: Tue Apr 09, 2013 11:30 pm
Location: Doomawangara
Contact:

Re: Citadel Item Randomiser 0.1

Post by lurkio » Thu Jan 04, 2018 11:20 pm

I don't know Citadel very well at all, but I was intrigued by this Randomiser, so I hope you don't mind that I had a go at doing a version that doesn't need the disc to be writable:

Play online:
Download:
I altered $.RANDO so that it stores the calculated randomised data at &A00, and I added a short machine-code routine at &A28 which moves the data to &1780 after $.CITAM has been loaded by $.CITAL, which I modified by adding JSR &A28 at &547F.

Please let me know what I've done wrong -- because I'm far from sure that I've managed to avoid breaking things in critical ways!

:?:

Diminished
Posts: 98
Joined: Fri Dec 08, 2017 9:47 pm
Contact:

Re: Citadel Item Randomiser 0.1

Post by Diminished » Thu Jan 04, 2018 11:58 pm

lurkio wrote:I don't know Citadel very well at all, but I was intrigued by this Randomiser, so I hope you don't mind that I had a go at doing a version that doesn't need the disc to be writable:

Play online:
Download:
  • Citadel-randomiser-0.2.ssd.zip
I altered $.RANDO so that it stores the calculated randomised data at &A00, and I added a short machine-code routine at &A28 which moves the data to &1780 once $.CITAM has been loaded by $.CITAL, which I modified by adding JSR &A28 at &547F.

Please let me know what I've done wrong -- because I'm far from sure I've managed to avoid breaking things in critical ways!

:?:
Aha, excellent work.

Now I feel embarrassed that I've made you do this with such a poorly optimised shuffler and solver. :(

There are two things that make it so slow:

a) it uses a brain-dead shuffling algorithm. I've never actually implemented the Knuth one myself since I've always been using languages that provide it as part of the standard library. The only time I've ever implemented my own shuffling algorithm was as a child, in (you guessed it) BBC BASIC, and that's the algorithm that appears here. It goes through the source slots sequentially; for each source slot it picks a destination slot at random. If the slot is already occupied, it picks another one at random, and continues in that fashion until it fills them all. It doesn't take a genius to work out that this has a pretty horrendous time order. I even knew it as a kid, but didn't understand how to improve it.

b) it uses a brain-dead lookup algorithm, FNibr(). The game employs a table with one slot per item pad, and in each slot it stores the room ID in which that item is currently placed. For the solver, the reverse is also needed -- you need to know which items are in which rooms, so a way of getting the item ID indexed by room ID is needed, rather than the other way around. The solver does this the same way as the game does, simply by linearly scanning the table for the current room ID each time and then counting how many slots it took to find it. While the item IDs have nice contiguous IDs, the IDs of rooms with item pads don't. My Ruby solver used a hashtable alongside a linear array to do lookups in either direction, but hashes are obviously not available in BBC BASIC. The way to do this would have been just to define a mostly empty 255-slot array so that items could be looked up instantly. I think I probably avoided this because I was worried about memory usage. In fact all the arrays in the program use 4-byte integer variables where single bytes would have sufficed, but I don't think there's a particularly clean way of doing this in BBC BASIC.

Anyway, I've already played through three randomiser seeds tonight, but I will test this now. Thanks.

User avatar
lurkio
Posts: 1765
Joined: Tue Apr 09, 2013 11:30 pm
Location: Doomawangara
Contact:

Re: Citadel Item Randomiser 0.1

Post by lurkio » Fri Jan 05, 2018 12:21 am

Diminished wrote:The way to do this would have been just to define a mostly empty 255-slot array so that items could be looked up instantly. I think I probably avoided this because I was worried about memory usage. In fact all the arrays in the program use 4-byte integer variables where single bytes would have sufficed, but I don't think there's a particularly clean way of doing this in BBC BASIC.
Can you not use DIM to reserve the required number of bytes in memory (rather than to dimension an array)?

The rest of the algorithmic issues you mentioned are beyond me, I'm afraid!

#-o

Diminished
Posts: 98
Joined: Fri Dec 08, 2017 9:47 pm
Contact:

Re: Citadel Item Randomiser 0.1

Post by Diminished » Fri Jan 05, 2018 4:13 pm

lurkio wrote:Can you not use DIM to reserve the required number of bytes in memory (rather than to dimension an array)?
Well, shucks. I had no idea you could do this.

I've just tested your 0.2 version of this in-browser, and beat a seed with 99 points (and 265 energy remaining. 8)) So this looks like it works just fine.

There are a few improvements I'd still like to make (improved performance, a configuration option for 100% completion -- and perhaps I should put some of the documentation into the program itself), so at some point I'll hopefully synthesise a 0.3 version that incorporates these. (It may have to wait until I've done my tax return >_>). However, the version you've produced here looks like the current best option (unless you want to play using a cheat loader, in which case the 0.1 on-disc patcher might work out better).

I've had a lot of fun with this; randomisers are really great for prolonging the life of a game you already know like the back of your hand, and I'm happy to apply this historically very Nintendo-centric idea to an Acorn title, particularly since I got it to work entirely on the original hardware. You can't do that on a NES!

Who wants to do Palace of Magic and Citadel 2?

Diminished
Posts: 98
Joined: Fri Dec 08, 2017 9:47 pm
Contact:

Re: Citadel Item Randomiser 0.1 (and now 0.2)

Post by Diminished » Fri Jan 12, 2018 1:24 am

So ... could anyone with more experience than I have hazard a guess as to why this huge NOP sled exists in CITAL, after the call to OSFILE?

Code: Select all

...
5476:                     ldx #$4d
5478:                     ldy #$56
547a:                     lda #$ff
547c:                     jsr $ffdd
547f:                     nop
5480:                     nop
5481:                     nop
5482:                     nop
5483:                     nop
5484:                     nop
5485:                     nop
5486:                     nop
5487:                     nop
5488:                     nop
5489:                     nop
548a:                     nop
548b:                     nop
548c:                     nop
548d:                     nop
548e:                     nop
548f:                     nop
5490:                     nop
5491:                     nop
5492:                     nop
5493:                     nop
5494:                     nop
5495:                     nop
5496:                     nop
5497:                     nop
5498:                     nop
5499:                     nop
549a:                     nop
549b:                     nop
549c:                     nop
549d:                     nop
549e:                     nop
549f:                     nop
54a0:                     nop
54a1:                     nop
54a2:                     nop
54a3:                     nop
54a4:                     nop
54a5:                     nop
54a6:                     nop
54a7:                     nop
54a8:                     nop
54a9:                     nop
54aa:                     jsr S5720
54ad:                     lda #$15
54af:                     ldx #$07
54b1:                     jsr $fff4
... etc.
Looks like prime real estate to stuff my payload into (lurkio's patch used it too), but I'm intrigued to know why it's there. Oddly, when I fill it with code, at least one of the payload bytes seems to get corrupted by something somehow, but I think I can just branch over the offending section.

crj
Posts: 834
Joined: Thu May 02, 2013 4:58 pm
Contact:

Re: Citadel Item Randomiser 0.1 (and now 0.2)

Post by crj » Fri Jan 12, 2018 4:32 am

Are you playing a hacked version?

Immediately after an OSFILE, that has the whiff of a place where copy protection code has been removed.

Diminished
Posts: 98
Joined: Fri Dec 08, 2017 9:47 pm
Contact:

Re: Citadel Item Randomiser 0.1 (and now 0.2)

Post by Diminished » Fri Jan 12, 2018 6:10 am

Aha, that sounds likely. It might also explain why something seems to modify it.

Which is actually where this gets interesting for the archivists, because I'm using the version from STH (this one right here).

So if this is the hallmark of removed copy protection, then there seem to be two possibilities:

a) the protection in question was only applied to the tape version, and was removed in the disc version, but this seems unlikely; surely you'd just reassemble the game for the disc version without the NOP bloat?

or, b) the version in the STH archive isn't actually an original, which means that somebody really ought to archive a proper one from its original media.

You can check it yourself by breakpointing at 547F, and then disassembling from 547F when the breakpoint is hit.

(For the record, I do own an original copy of this game, but it's on tape, and therefore not very convenient to work with.)

User avatar
Rich Talbot-Watkins
Posts: 1352
Joined: Thu Jan 13, 2005 5:20 pm
Location: Palma, Mallorca
Contact:

Re: Citadel Item Randomiser 0.1 (and now 0.2)

Post by Rich Talbot-Watkins » Fri Jan 12, 2018 8:29 am

This looks like a transfer from tape ("Press Play" in the loader), and is a Master-compatible version, which means it contains the patch to move some code to Sideways RAM bank 5. I'm fairly sure that those NOPs are in the released version (I remember also being baffled by them when transferring it to disk). My guess is that whoever did the Master compatibility patch for Superior (it wasn't Michael Jakobsen) just left things in a bit of a mess. I haven't compared it with the original release; maybe there was some basic decryption or something going on there before.

User avatar
Rich Talbot-Watkins
Posts: 1352
Joined: Thu Jan 13, 2005 5:20 pm
Location: Palma, Mallorca
Contact:

Re: Citadel Item Randomiser 0.1 (and now 0.2)

Post by Rich Talbot-Watkins » Fri Jan 12, 2018 8:59 am

I just took a look at the original disk release. CITAL is a strange bit of code: in place of the NOPs there's a bunch of code which (after loading CITAM), reads the catalog info for CITAM, and then loads it again at its natural location. *INFO for CITAM looks like this:

$.CITAM 008000 00A670 004100 021

Here's the code:

Code: Select all

547F  LDA #&BC
5481  PHA
5482  LDA #&C7
5484  PHA          ; push stack sentinel bytes for unwinding stack after an error
5485  LDX #&4D
5487  LDY #&56
5489  LDA #&05
548B  JSR &FFDD   ; read catalog info for CITAM
548E  LDA #&A3
5490  STA &0202
5493  LDA #&54
5495  STA &0203   ; trap BRKV so any error which comes next is handled
5498  LDX #&4D
549A  LDY #&56
549C  LDA #&FF 
549E  JSR &FFDD    ; load CITAM again at its natural load address (&8000) ???
54A1  INC &95
54A3  PLA          ; unwind stack after error
54A4  CMP #&C7
54A6  BNE &54A3
54A8  PLA
54A9  CMP #&BC
54AB  BNE &54A8
54AD  LDA #&15  ; etc...
So, not decryption or any other type of protection from what I can tell. There's no use of OSWORD &7F at all there. Though the code is quite peculiar all the same.

The NOP'd version is definitely from the Master-compatible release, so it looks like they just went through, got rid of all the junk and didn't bother to tidy it up.

Diminished
Posts: 98
Joined: Fri Dec 08, 2017 9:47 pm
Contact:

Re: Citadel Item Randomiser 0.1 (and now 0.2)

Post by Diminished » Fri Jan 12, 2018 9:28 am

Thanks, Rich!

I'm slightly disappointed that the STH version wasn't the original, since I'm pretty sure that's the one I spent two months disassembling!

I'm baffled as to why CITAM would be reloaded at &8000. I'm not an expert on the machine's internals, but ... isn't that all ROM and I/O up there? *scratches head*

The reason why I'm asking, of course, is that I'm trying to follow game modding best practices with the next (hopefully final) version of the randomiser. 0.1 patches things on-disc and would operate fine on probably any disc version of CITAM, and so can be distributed without needing to make available any of the original game's code -- the downside is that it requires destructive write access to the game disc. lurkio's 0.2 version is better in the sense that it will work with a write-protected disc, which has advantages for situations like jsbeeb, but the downside is that it achieves this using a patched CITAL, so in order to gain its advantage over 0.1 you're pretty much forced to distribute part of the original game code along with the randomiser.

The version I'm working on now is able to patch the game as it loads, so it requires neither a writeable disc nor a modified game. Unfortunately I've developed it against the STH copy, which corresponds to no official release of the game! I did think the "Press Play" thing was fishy.

User avatar
Rich Talbot-Watkins
Posts: 1352
Joined: Thu Jan 13, 2005 5:20 pm
Location: Palma, Mallorca
Contact:

Re: Citadel Item Randomiser 0.1 (and now 0.2)

Post by Rich Talbot-Watkins » Fri Jan 12, 2018 10:02 am

Well, none of the STH images are originals, because the SSD format can't support protected disks. So all of those images are necessarily versions which have had their protection stripped and put onto a clean DFS format disk image.

The Citadel.ssd file on STH appears to be direct rip from tape of the Master-compatible release. I remember when I did the same thing, transferring the files from tape to disk was absolutely trivial - no tricky protection schemes, just locked tape files. So it's pretty much a 'clean' image.

I also have no idea what's going on with reloading CITAM at &8000 (and particularly that it almost expects it to generate an error) - maybe billcarr has an idea on what they wanted to do there?

To be honest, I don't think it's really a big deal to create a new disk image containing Citadel and your patch. If you take the STH Citadel.ssd as a clean starting point, I reckon it's fine to use that to create a new version with your randomiser built in. STH did that once before with a patch I made for Chuckie Egg which added extra colours. From the user's point of view, it's much easier to just load a new disk image and boot it, and have everything 'just work'.

User avatar
Rich Talbot-Watkins
Posts: 1352
Joined: Thu Jan 13, 2005 5:20 pm
Location: Palma, Mallorca
Contact:

Re: Citadel Item Randomiser 0.1 (and now 0.2)

Post by Rich Talbot-Watkins » Fri Jan 12, 2018 10:05 am

Here's the .fdi image for the original disk release of Citadel I was using, by the way. Use B-Em to load it.
Attachments
Citadel (Superior) (B) (Disc) [disc].zip
(1.35 MiB) Downloaded 29 times

User avatar
billcarr2005
Posts: 1237
Joined: Fri Sep 09, 2005 3:01 pm
Location: UK
Contact:

Re: Citadel Item Randomiser 0.1 (and now 0.2)

Post by billcarr2005 » Fri Jan 12, 2018 11:03 am

The original disk isn't formatted in any strange way, just has some tracks with Deleted Data, but it isn't used as a form of protection.
Strangely enough, on the original disk version of Citadel that I imaged as an FSD, the JSR FFDD that would read the catalogue information has been NOP'd out

Code: Select all

547F  LDA #&BC
5481  PHA
5482  LDA #&C7
5484  PHA          ; push stack sentinel bytes for unwinding stack after an error
5485  LDX #&4D
5487  LDY #&56
5489  LDA #&05
548B  NOP
548C NOP
548D NOP
The string "Press PLAY" is also on the disk, but after 0DFF in the BASIC file, just like it was modified and saved ... on the FDI just E5

On the FDI, the "count" after the title "SUPERIOR" is (10) on the FSD (11) ... I'll check if I have any other, differing copies!
Attachments
Citadel.zip
SSD of original disk
(26.01 KiB) Downloaded 29 times
Last edited by billcarr2005 on Fri Jan 12, 2018 11:07 am, edited 1 time in total.

Diminished
Posts: 98
Joined: Fri Dec 08, 2017 9:47 pm
Contact:

Re: Citadel Item Randomiser 0.1 (and now 0.2)

Post by Diminished » Fri Jan 12, 2018 11:06 am

Rich Talbot-Watkins wrote:Well, none of the STH images are originals, because the SSD format can't support protected disks. So all of those images are necessarily versions which have had their protection stripped and put onto a clean DFS format disk image.
Ah, I didn't realise this.
billcarr2005 wrote:The original disk isn't formatted in any strange way, just has some tracks with Deleted Data, but it isn't used as a form of protection.
Strangely enough, on the original disk version of Citadel that I imaged as an FSD, the JSR FFDD that would read the catalogue information has been NOP'd out

Code: Select all

547F  LDA #&BC
5481  PHA
5482  LDA #&C7
5484  PHA          ; push stack sentinel bytes for unwinding stack after an error
5485  LDX #&4D
5487  LDY #&56
5489  LDA #&05
548B  NOP
548C NOP
548D NOP
The string "Press PLAY" is also on the disk, but after 0DFF in the BASIC file, just like it was modified and saved.
Okay, this gets weirder and weirder.
I also have no idea what's going on with reloading CITAM at &8000 (and particularly that it almost expects it to generate an error) - maybe billcarr has an idea on what they wanted to do there?
I may have stumbled across the answer by accident:
Diminished wrote:Oddly, when I fill it with code, at least one of the payload bytes seems to get corrupted by something somehow, but I think I can just branch over the offending section.
Out of the mouths of babes and sucklings, etc.

Near the top of CITAL:

Code: Select all

5473:                     jsr S5665
Which does this:

Code: Select all

5665: S5665               lda $88
5667:                     jsr S5620
566a:                     eor #$17
566c:                     sta $88
566e:                     lda #$c8
5670:                     sta $55d4
5673:                     lda #$44
5675:                     sta L561d + 2
5678:                     lda #$00
567a:                     sta $95
567c:                     lda #$04
567e:                     sta $549d <- HELLO NURSE!
5681:                     lda #$01
5683:                     nop
5684:                     nop
5685:                     nop
5686:                     rts
And, from your disassembly of the vanilla copy, this:

Code: Select all

5498  LDX #&4D
549A  LDY #&56
549C  LDA #&FF
549E  JSR &FFDD    ; load CITAM again at its natural load address (&8000) ???
So ... I know what OSFILE &FF does, but what would OSFILE &4 do? :O

It seems that when the NOP sled in the STH version actually runs, one of those opcodes really becomes an undocumented NOP &04.

Here's the fun conspiracy theory part: Maybe it's a coincidence, but it so happens that the SPEECH! code in the loader ("Ceetadel, Ceetadel") hammers some code placed at 547c, so you can't just breakpoint 547c before loading the game in order to catch CITAL before it loads CITAM -- it results in incessant breakpoint spam. However, there's no such restriction on breakpointing just one instruction later, at 547f. And, once you've picked your character's gender and key layout, the game cheekily drops in one final "Ceetadel!", so you can't breakpoint it during the character selection either.

For reference, here's the whole of CITAL according to WFDIS's disassembly of the STH copy.

Code: Select all

 cital.bin 
5400:                     43 49 54 41 44 45 4c 20 50 6c 65 61 73 65 20 64 
5410:                     6f 20 6e 6f 74 20 63 6f 70 79 20 6d 79 20 50 72 
5420:                     6f 67 72 61 6d 20 21 20 4d 4a 2e 20 31 36 2d 31 
5430:                     31 2d 31 39 38 35 
5436: L5436               lda #$95
5438:                     nop
5439:                     sta $0a
543b:                     lda #$a9
543d:                     sei
543e:                     sta $0b
5440:                     ldy #$00
5442:                     nop
5443:                     nop
5444:                     nop
5445:                     cli
5446:                     nop
5447:                     lda #$a3
5449:                     sta $0c
544b:                     lda #$60
544d:                     sta S5620
5450:                     lda #$0b
5452:                     ldx #$00
5454:                     jsr S562a
5457:                     lda #$04
5459:                     ldx #$01
545b:                     jsr S562a
545e:                     lda #$c8
5460:                     ldx #$03
5462:                     jsr S562a
5465:                     lda #$e5
5467:                     ldx #$ff
5469:                     jsr S562a
546c:                     lda #$e1
546e:                     ldx #$30
5470:                     jsr S562a
5473:                     jsr S5665
5476:                     ldx #$4d
5478:                     ldy #$56
547a:                     lda #$ff
547c:                     jsr $ffdd
547f:                     nop
5480:                     nop
5481:                     nop
5482:                     nop
5483:                     nop
5484:                     nop
5485:                     nop
5486:                     nop
5487:                     nop
5488:                     nop
5489:                     nop
548a:                     nop
548b:                     nop
548c:                     nop
548d:                     nop
548e:                     nop
548f:                     nop
5490:                     nop
5491:                     nop
5492:                     nop
5493:                     nop
5494:                     nop
5495:                     nop
5496:                     nop
5497:                     nop
5498:                     nop
5499:                     nop
549a:                     nop
549b:                     nop
549c:                     nop
549d:                     nop
549e:                     nop
549f:                     nop
54a0:                     nop
54a1:                     nop
54a2:                     nop
54a3:                     nop
54a4:                     nop
54a5:                     nop
54a6:                     nop
54a7:                     nop
54a8:                     nop
54a9:                     nop
54aa:                     jsr S5720
54ad:                     lda #$15
54af:                     ldx #$07
54b1:                     jsr $fff4
54b4:                     lda $3566
54b7:                     sta $0e
54b9:                     ldy #$00
54bb:                     sty $00
54bd:                     lda $53ff
54c0:                     sta $02
54c2:                     sty $53ff
54c5:                     lda #$59
54c7:                     sta $06
54c9: L54c9               lda $1300,y
54cc:                     nop
54cd:                     nop
54ce:                     nop
54cf:                     nop
54d0:                     sta $0400,y
54d3:                     nop
54d4:                     nop
54d5:                     nop
54d6:                     nop
54d7:                     dey
54d8:                     bne L54c9
54da:                     inc L54c9 + 2
54dd:                     inc $54d2
54e0:                     lda $54d2
54e3:                     cmp #$45
54e5:                     bcc L54c9
54e7:                     lda $00
54e9:                     eor #$ff
54eb:                     cmp $02
54ed:                     beq L54f1
54ef:                     nop
54f0:                     nop
54f1: L54f1               lda #$0f
54f3:                     ldx #$01
54f5:                     jsr S562a
54f8:                     lda $82
54fa:                     beq L551b
54fc:                     ldy #$10
54fe: L54fe               lda $053f,y
5501:                     sta $05c3,y
5504:                     dey
5505:                     bne L54fe
5507: L5507               lda $0400,y
550a:                     sta $0610,y
550d:                     dey
550e:                     bne L5507
5510:                     ldy #$3f
5512: L5512               lda $0500,y
5515:                     sta $0710,y
5518:                     dey
5519:                     bpl L5512
551b: L551b               ldx #$27
551d: L551d               lda $0590,x
5520:                     sta $04c8,x
5523:                     dex
5524:                     bpl L551d
5526:                     lda $95
5528:                     bne L54f1
552a:                     lda #$90
552c:                     ldx #$fd
552e:                     ldy #$01
5530:                     jsr $fff4
5533:                     lda #$16
5535:                     jsr $ffee
5538:                     lda #$04
553a:                     jsr $ffee
553d:                     lda $95
553f:                     sta L561d + 1
5542:                     beq L5549
5544:                     lda #$26
5546:                     sta L561d + 2
5549: L5549               lda #$06
554b:                     sta $fe00
554e:                     lda #$01
5550:                     sta $fe01
5553:                     lda #$9a
5555:                     ldx #$f4
5557:                     jsr $fff4
555a:                     lda #$80
555c:                     sta $034c
555f:                     ldx #$02
5561:                     stx $034d
5564:                     lda #$20
5566:                     sta $034f
5569:                     lda #$80
556b:                     sta $0352
556e:                     stx $0353
5571:                     ldy #$30
5573:                     sty $034e
5576:                     lda #$50
5578:                     sta $0354
557b:                     stx $0355
557e:                     ldx #$00
5580:                     stx $0356
5583:                     stx $0358
5586:                     stx $036d
5589:                     stx $036e
558c:                     lda #$0f
558e:                     sta $0360
5591:                     lda #$01
5593:                     sta $0361
5596:                     lda #$aa
5598:                     sta $0362
559b:                     lda #$55
559d:                     sta $0363
55a0:                     sta $d1
55a2:                     stx $0350
55a5:                     sty $0351
55a8:                     stx $fe00
55ab:                     lda #$7f
55ad:                     sta $fe01
55b0:                     inx
55b1:                     stx $fe00
55b4:                     lda #$50
55b6:                     sta $fe01
55b9:                     inx
55ba:                     stx $fe00
55bd:                     lda #$62
55bf:                     sta $fe01
55c2:                     inx
55c3:                     stx $fe00
55c6:                     lda #$28
55c8:                     sta $fe01
55cb:                                                      9c 69 03 9c 6a 
55d0:                     03 ea ea a9 10 a2 03 20 2a 56 a2 1d a0 00 b9 2f 
55e0:                     56 20 ee ff c8 ca 10 f6 a2 0c 8e 00 fe a9 08 8d 
55f0:                     01 fe e8 8e 00 fe a9 80 8d 01 fe a9 f0 8d 21 fe 
5600:                     38 e9 0f 30 f8 a9 0d a2 04 20 2a 56 a9 00 8d 20 
5610:                     02 a9 25 8d 21 02 a9 0e a2 04 20 2a 56 
561d: L561d               jmp $3280
                          
5620: S5620               clc
5621:                     adc $00
5623:                     sta $01
5625:                     bne L561d
5627:                     jmp $3600
                          
562a: S562a               ldy #$00
562c:                     jmp $fff4
                          
562f:                                                                  14 
5630:                     1c 00 1f 13 08 18 00 00 00 00 f8 04 c0 02 1d 00 
5640:                     00 e0 ff 17 00 0a 20 00 00 00 00 00 00 5f 56 00 
5650:                     13 00 00 00 00 00 00 00 00 00 00 00 00 00 00 43 
5660:                     49 54 41 4d 0d 
5665: S5665               lda $88
5667:                     jsr S5620
566a:                     eor #$17
566c:                     sta $88
566e:                     lda #$c8
5670:                     sta $55d4
5673:                     lda #$44
5675:                     sta L561d + 2
5678:                     lda #$00
567a:                     sta $95
567c:                     lda #$04
567e:                     sta $549d
5681:                     lda #$01
5683:                     nop
5684:                     nop
5685:                     nop
5686:                     rts
                          
5687:                     00 00 00 00 00 00 00 00 00 
5690: S5690               lda #$05
5692:                     sta $f4
5694:                     sta $fe30
5697:                     nop
5698:                     nop
5699:                     ldx #$ff
569b: L569b               lda $56a4,x
569e:                     sta $b000,x
56a1:                     dex
56a2:                     bne L569b
56a4:                     rts
                          
56a5:                                    ea ea a9 12 a2 00 a0 00 20 ee ff 
56b0:                     8a 20 ee ff 98 20 ee ff ea ea ea 60 ea ea c9 8d 
56c0:                     f0 30 c9 1e f0 31 c9 a3 f0 32 c9 70 f0 33 0a 0a 
56d0:                     2e e1 40 0a 0a 2e e1 40 0a 0a 2e e1 40 0a 0a 2e 
56e0:                     e1 40 a9 12 20 ee ff a5 04 20 ee ff ad e1 40 4c 
56f0:                     ee ff a0 0b 4c 64 b0 a0 03 4c 64 b0 a0 09 4c 64 
5700:                     b0 a0 04 4c 64 b0 ea ea a9 12 20 ee ff a5 04 20 
5710:                     ee ff 98 4c ee ff 60 00 00 00 00 00 00 00 00 00 
5720: S5720               ldx #$01
5722:                     lda #$00
5724:                     jsr $fff4
5727:                     txa
5728:                     cmp #$03
572a:                     beq L572d
572c:                     rts
                          
572d: L572d               jsr S5690
5730:                     lda #$20
5732:                     sta $45a0
5735:                     lda #$01
5737:                     sta $45a1
573a:                     lda #$b0
573c:                     sta $45a2
573f:                     lda #$ea
5741:                     sta $45a3
5744:                     sta $45a4
5747:                     sta $45a5
574a:                     sta $45b0
574d:                     sta $45b1
5750:                     sta $45b2
5753:                     lda #$20
5755:                     sta $45b6
5758:                     lda #$18
575a:                     sta $45b7
575d:                     lda #$b0
575f:                     sta $45b8
5762:                     lda #$00
5764:                     sta $50e0
5767:                     rts
                          
5768:                                             00 00 00 00 00 00 00 00 
5770:                     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 
5780:                     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 
5790:                     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 
57a0:                     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 
57b0:                     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 
57c0:                     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 
57d0:                     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 
57e0:                     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 
57f0:                     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
To be honest, I don't think it's really a big deal to create a new disk image containing Citadel and your patch. If you take the STH Citadel.ssd as a clean starting point, I reckon it's fine to use that to create a new version with your randomiser built in. STH did that once before with a patch I made for Chuckie Egg which added extra colours. From the user's point of view, it's much easier to just load a new disk image and boot it, and have everything 'just work'.
Yeah, I'll probably have to resign myself to this; I just would have felt slightly better if I could have produced an entirely clean version.
Rich Talbot-Watkins wrote:Here's the .fdi image for the original disk release of Citadel I was using, by the way. Use B-Em to load it.
Thanks a lot for this.

User avatar
billcarr2005
Posts: 1237
Joined: Fri Sep 09, 2005 3:01 pm
Location: UK
Contact:

Re: Citadel Item Randomiser 0.1 (and now 0.2)

Post by billcarr2005 » Fri Jan 12, 2018 11:15 am

Just to further muddy the waters, in both the FDI and FSD

Code: Select all

5681:                     lda #$01
5683:                     sta $54cf
5686:                     rts

User avatar
billcarr2005
Posts: 1237
Joined: Fri Sep 09, 2005 3:01 pm
Location: UK
Contact:

Re: Citadel Item Randomiser 0.1 (and now 0.2)

Post by billcarr2005 » Fri Jan 12, 2018 11:30 am

Here's the 2 file (CITADEL and CITAX), no speech, no title screen, PIAS1 re-release BBC Master compatible disk version, which I guess would've been the last official release of the game :?:

(CHAIN"CITADEL" to load!)
Attachments
CITADEL (PIAS1).zip
(15.52 KiB) Downloaded 31 times

User avatar
Rich Talbot-Watkins
Posts: 1352
Joined: Thu Jan 13, 2005 5:20 pm
Location: Palma, Mallorca
Contact:

Re: Citadel Item Randomiser 0.1 (and now 0.2)

Post by Rich Talbot-Watkins » Fri Jan 12, 2018 11:40 am

billcarr2005 wrote:Just to further muddy the waters, in both the FDI and FSD

Code: Select all

5681:                     lda #$01
5683:                     sta $54cf
5686:                     rts
And in the original FDI we have:

Code: Select all

54C5  LDA #&59
54C7  STA &06
54C9  LDA &1300,Y
54CC  STY &01
54CE  EOR &06     <-- self modified to EOR &01 by that code
54D0  STA &0400,Y
54D3  EOR &00
54D5  STA &00
54D7  DEY
54D8  BNE &54C9
And earlier, as Diminished pointed out, the second OSFILE &FF is changed to an OSFILE &04 which attempts to write catalog information to the file (the original will be write-protected, so will cause the "Disc read only" error, though if it succeeds, I don't see that anything bad can happen).

It's just bizarre code. Obfuscated by jumping and self-modification, but not really difficult to understand if you have the time to go through it and look at all the places it amends itself in plain sight. It seems like a very weak attempt at 'protection'. The later release which patches a lot of this out with NOPs does equally weird things (like substituting a SEI/CLI instead of a NOP when removing old code - see &543D, &5445).

That routine at &5720 looks like the bit that patches the game for Master compatibility - it puts a small amount of code at &B001 and then patches the main code to jump there in certain situations. If anyone has the time to look at what it's doing, maybe we can see once and for all why it wasn't compatible with the Master series originally.
Diminished wrote:
Rich Talbot-Watkins wrote:Well, none of the STH images are originals, because the SSD format can't support protected disks. So all of those images are necessarily versions which have had their protection stripped and put onto a clean DFS format disk image.
Ah, I didn't realise this.
IMO it was a big mistake right from the start to use a format without any metadata, as it provides no possibility for upgrading the spec of the disk image. And the only way we can distinguish double sided images from single sided ones is from the file extension, which seems pretty horrible. I would prefer .fdi as a standard disk image format for all BBC disks (particularly since it can hold a simplified data format which works out little bigger than a .ssd), but what's done is done, I guess!

User avatar
Rich Talbot-Watkins
Posts: 1352
Joined: Thu Jan 13, 2005 5:20 pm
Location: Palma, Mallorca
Contact:

Re: Citadel Item Randomiser 0.1 (and now 0.2)

Post by Rich Talbot-Watkins » Fri Jan 12, 2018 11:53 am

billcarr2005 wrote:Here's the 2 file (CITADEL and CITAX), no speech, no title screen, PIAS1 re-release BBC Master compatible disk version, which I guess would've been the last official release of the game :?:

(CHAIN"CITADEL" to load!)
There will in fact have been another version, as that's not Master Compact compatible! (at &5728, it checks the OSBYTE 0 result for equality with 3, instead of being greater than 3).

Diminished
Posts: 98
Joined: Fri Dec 08, 2017 9:47 pm
Contact:

Re: Citadel Item Randomiser 0.1 (and now 0.2)

Post by Diminished » Fri Jan 12, 2018 1:31 pm

Well, this was more fun than working on the randomiser.
Rich Talbot-Watkins wrote:That routine at &5720 looks like the bit that patches the game for Master compatibility - it puts a small amount of code at &B001 and then patches the main code to jump there in certain situations. If anyone has the time to look at what it's doing, maybe we can see once and for all why it wasn't compatible with the Master series originally.
Hm.

Code: Select all

>dis b001
    B001 : NOP         
    B002 : NOP         
    B003 : LDA #12     
    B005 : LDX #00     
    B007 : LDY #00     
    B009 : JSR FFEE    
    B00C : TXA         
    B00D : JSR FFEE    
    B010 : TYA         
    B011 : JSR FFEE    
    B014 : NOP         
    B015 : NOP         
    B016 : NOP         
    B017 : RTS         

>dis b018
    B018 : NOP         
    B019 : NOP         
    B01A : CMP #8D     
    B01C : BEQ B04E    
    B01E : CMP #1E     
    B020 : BEQ B053    
    B022 : CMP #A3     
    B024 : BEQ B058    
    B026 : CMP #70     
    B028 : BEQ B05D    
    B02A : ASL A       
    B02B : ASL A       
    B02C : ROL 40E1    
    B02F : ASL A       
    B030 : ASL A       
    B031 : ROL 40E1    
    B034 : ASL A       
    B035 : ASL A       
    B036 : ROL 40E1    
    B039 : ASL A       
    B03A : ASL A       
    B03B : ROL 40E1    
    B03E : LDA #12     
    B040 : JSR FFEE    
    B043 : LDA 04      
    B045 : JSR FFEE    
    B048 : LDA 40E1    
    B04B : JMP FFEE    
    B04E : LDY #0B     
    B050 : JMP B064    
    B053 : LDY #03     
    B055 : JMP B064    
    B058 : LDY #09     
    B05A : JMP B064    
    B05D : LDY #04     
    B05F : JMP B064    
    B062 : NOP         
    B063 : NOP         
    B064 : LDA #12     
    B066 : JSR FFEE    
    B069 : LDA 04      
    B06B : JSR FFEE    
    B06E : TYA         
    B06F : JMP FFEE    
    B072 : RTS
Looks like a load of VDU 18 shims? The game normally just pokes the graphics colours into RAM from &359.

User avatar
Rich Talbot-Watkins
Posts: 1352
Joined: Thu Jan 13, 2005 5:20 pm
Location: Palma, Mallorca
Contact:

Re: Citadel Item Randomiser 0.1 (and now 0.2)

Post by Rich Talbot-Watkins » Fri Jan 12, 2018 1:47 pm

Yes, I also just had a look at it. Seems like, on the Master, VDU variable &359 moved to &36D. On the Master &359 is "0 if plotting graphics foreground, 8 if plotting graphics background" - seems like any other value can make it hang completely! So they just replaced those writes with legal GCOLs instead. The only other patch is when clearing the screen - on the regular version it sets the left margin to 14, and on the Master patch it sets it to 0. No idea what difference this makes or why it was necessary.

For that tiny amount of code that was added, it surprises me that there was absolutely nowhere else in memory they could've put it. I suspect they could've just changed the STA &359 to STA &36D as well. Also, why is the patch code full of NOPs? It's all strange.

User avatar
Rich Talbot-Watkins
Posts: 1352
Joined: Thu Jan 13, 2005 5:20 pm
Location: Palma, Mallorca
Contact:

Re: Citadel Item Randomiser 0.1 (and now 0.2)

Post by Rich Talbot-Watkins » Fri Jan 12, 2018 2:12 pm

Incidentally, that weird code in the patch which is checking for certain magic values and converting them to other magic values: that's translating a 'stripy' colour value into the best flat colour that can be selected using a legal GCOL call. That's why stuff like the Witch's House roof looks flat red on the Master instead of the stripy texture that it has on the Beeb. I guess, had they poked the Master VDU variable directly, they could've had it looking identical.

Diminished
Posts: 98
Joined: Fri Dec 08, 2017 9:47 pm
Contact:

Re: Citadel Item Randomiser 0.1 (and now 0.2)

Post by Diminished » Fri Jan 12, 2018 2:19 pm

Rich Talbot-Watkins wrote:Also, why is the patch code full of NOPs?
Maybe I've been awake too long, but I keep giggling at this sentence. It seems like all I've done for two days is stare at deranged code that's inexplicably full of NOPs.

I think I need to gawk at CITAM for a bit to regain my sanity. There isn't a single NOP in that. Fact!

Diminished
Posts: 98
Joined: Fri Dec 08, 2017 9:47 pm
Contact:

Re: Citadel Item Randomiser 0.1 (and now 0.2)

Post by Diminished » Fri Jan 12, 2018 2:25 pm

Rich Talbot-Watkins wrote:Incidentally, that weird code in the patch which is checking for certain magic values and converting them to other magic values: that's translating a 'stripy' colour value into the best flat colour that can be selected using a legal GCOL call. That's why stuff like the Witch's House roof looks flat red on the Master instead of the stripy texture that it has on the Beeb. I guess, had they poked the Master VDU variable directly, they could've had it looking identical.
Ah, I wondered if that was the case. You'd think they could have corrected the triangle coordinates while they were at it ...

I might have a look, and see if we can maybe come up with a better Master version than Superior managed ...

User avatar
Rich Talbot-Watkins
Posts: 1352
Joined: Thu Jan 13, 2005 5:20 pm
Location: Palma, Mallorca
Contact:

Re: Citadel Item Randomiser 0.1 (and now 0.2)

Post by Rich Talbot-Watkins » Fri Jan 12, 2018 2:40 pm

I just tried it: I replaced all the STA &359 with STA &36D and disabled the Master patch. Result? Hangs like the original version.

So then I patched that LDA #&0E:STA &308 with LDA #0, and it no longer hangs, so that was the main problem. I have no idea why it was originally doing that, or why it causes a Master to hang. (Anyone got any ideas?)

But things are still broken. There are spurious cyan triangles in half the screens now. I think the reason for that is that Michael Jakobsen exploited undefined behaviour in OS 1.20 by setting the GCOL plot mode (via STA &35B) to values outside the 'allowed' range of 0-5 (you can actually do this directly with GCOL too). This does an out of range lookup in an OS table which is used to calculate the colour mask for pixels being plotted - you could exploit that to get stripey patterns in BASIC (e.g. try on a BBC B: GCOL 62,135:CLG). Now there was no need for him to do that, as he was setting the colour (via &359) to an arbitrary value anyway, but anyway... I don't know how the Master OS deals with this, but it's not really so easy to patch - so I guess that's why they resorted to doing it 'legally'... seems like the easiest option in the end.

Diminished
Posts: 98
Joined: Fri Dec 08, 2017 9:47 pm
Contact:

Re: Citadel Item Randomiser 0.1 (and now 0.2)

Post by Diminished » Fri Jan 12, 2018 5:10 pm

Worth a try, Rich ...

I might as well post the current state of my CITAM disassembly ... It's unfinished and I know for a fact it contains plenty of inconsistencies and mistakes, and much of the variable and subroutine naming could be improved. I don't think WFDIS supports comments longer than a single line and it only allows them on their own line -- you can't place them after instructions, so many of them are more terse than I'd like. The PHP code I used to extract the tile sets is annotated a bit better, but overall that's even more of a mess than this is.
citadel-disassembly-2.txt.zip
(106.93 KiB) Downloaded 35 times
citadel-disassembly-1.txt.zip EDIT: replaced this with a slightly updated version 2 that adds a few acknowledgements along with the remarks in this post, and removes one instance of profanity that occurred when I got particularly frustrated with the room unpacking code ... <_<

I just let WFDIS grind away on the entire 64K address space, so both the VRAM and all the ROM is included.

Standard labels start with L. Loop labels are prefixed either with "Lloop", or "P_" which I started using later on so I could make the labels longer. Similarly, self-modified locations (of which there are plenty) start with "Lselfmod_" or, later on, "SM_". Tables are prefixed with "t_" for variable ones or "T_" for constant ones. OS-owned variables and routines I've tended to prefix with a single underscore. At some point I started labelling useful inline constants by placing comments containing the word CONSTANT above them, in the extraordinarily unlikely event that I got to the point where I might be able to produce a version of the source that can be reassembled.

Constant values use uppercase. Variables and subroutines just use unprefixed lowercase names.

Oh, and this was extracted from WFDIS in a very roundabout fashion, since that software has no facility currently for exporting disassembly in any useful format at all. Namely I saved the thing as an HTML page in Firefox, and then ran a very hastily hacked together PHP script on the saved HTML. I think the conversion is okay, but there may be a few issues with it.

By the way, this was produced from a memory dump of a running game, so everything has "real-world" values rather than initial ones ...
Last edited by Diminished on Sun Jan 14, 2018 4:32 pm, edited 2 times in total.

User avatar
billcarr2005
Posts: 1237
Joined: Fri Sep 09, 2005 3:01 pm
Location: UK
Contact:

Re: Citadel Item Randomiser 0.1 (and now 0.2)

Post by billcarr2005 » Fri Jan 12, 2018 5:17 pm

Rich Talbot-Watkins wrote: There will in fact have been another version, as that's not Master Compact compatible! (at &5728, it checks the OSBYTE 0 result for equality with 3, instead of being greater than 3).
I see we've been here before... :)
viewtopic.php?t=4603#p41304

Still haven't got the 3.5 ADFS(?) disk imaged... my drive is flaky :(

User avatar
billcarr2005
Posts: 1237
Joined: Fri Sep 09, 2005 3:01 pm
Location: UK
Contact:

Re: Citadel Item Randomiser 0.1 (and now 0.2)

Post by billcarr2005 » Sat Jan 13, 2018 12:38 pm

Rich Talbot-Watkins wrote:
billcarr2005 wrote:Here's the 2 file (CITADEL and CITAX), no speech, no title screen, PIAS1 re-release BBC Master compatible disk version, which I guess would've been the last official release of the game :?:

(CHAIN"CITADEL" to load!)
There will in fact have been another version, as that's not Master Compact compatible! (at &5728, it checks the OSBYTE 0 result for equality with 3, instead of being greater than 3).
The BBC Master Compact version just bypasses the OSBYTE 0 check altogether with

Code: Select all

5720 JMP 572D
So it looks as though the rest of the code might be the same

Diminished
Posts: 98
Joined: Fri Dec 08, 2017 9:47 pm
Contact:

Re: Citadel Item Randomiser 0.1 (and now 0.2)

Post by Diminished » Tue Jan 16, 2018 2:41 pm

billcarr2005 wrote:Here's the 2 file (CITADEL and CITAX), no speech, no title screen, PIAS1 re-release BBC Master compatible disk version, which I guess would've been the last official release of the game :?:

(CHAIN"CITADEL" to load!)
Thanks for these, Bill ... I quite fancy this one, as you don't have to sit through the tedious SPEECH! every time.

Diminished
Posts: 98
Joined: Fri Dec 08, 2017 9:47 pm
Contact:

Citadel Item Randomiser 0.4

Post by Diminished » Mon Jan 22, 2018 10:23 pm

0.4 is mostly a rewrite. I probably should have tested it a little more but I have other things to do ...

- built-in PRNG means seeds now compatible between Model B and Master
- pyramid sarcophagus and its corresponding crystal are both now randomised
- solver now calculates maximum points for each seed, allowing filtering by 100% / low% games
- option to disable the attract mode (it betrays various item locations)
- universal loader should load any unprotected (i.e. Play It Again, Sam) version of the game from tape or disc
- solver / shuffler ported to assembler, performance improved by an order of magnitude

Available here:
citadel-randomiser-0.4-rc3.zip
(45.72 KiB) Downloaded 38 times
(Sorry, I accidentally uploaded rc2 here originally instead of rc3 -- it should be the correct version this time.)

Post Reply