Unused Citadel bits
- Diminished
- Posts: 598
- Joined: Fri Dec 08, 2017 9:47 pm
- Contact:
Unused Citadel bits
Tile 005 at 0x990 in the BBC disc version. Encountered while re-implementing the game's tile and room unpackers in PHP.
Game never reads from its memory (beyond the first byte, which is needed to find the following item in the chain). It may have been intended as a texture rather than an item, but in order to be sure it was rendered correctly I decided I'd rather have the game display it than my own code.
Gold bar? Tile 031 at 0xafd.
Game never reads from its memory (beyond the first byte, which is needed to find the following item in the chain). It may have been intended as a texture rather than an item, but in order to be sure it was rendered correctly I decided I'd rather have the game display it than my own code.
Gold bar? Tile 031 at 0xafd.
Re: Unused Citadel bits
Very interesting! I was looking to explore Citadel's graphics and screen data at some point but never got round to it. It's good to see someone making progress with it. The second item certainly looks like a gold bar. 
Are you going to introduce yourself to us?

Are you going to introduce yourself to us?

- Diminished
- Posts: 598
- Joined: Fri Dec 08, 2017 9:47 pm
- Contact:
Re: Unused Citadel bits
Thanks.
The seductive thing about Citadel was that until the day you racked up that 99 score, you could never be sure you'd seen all of it. Now, I've got a 99 score many times, but some people are just goddamned stubborn. :P
I've been using the White Flame online 6502 disassembler for this task. This is flawed in a few ways, but it also has some appealing aspects. Worth a look if you've not seen it before, although it is very much geared towards the C64 (supports PETSCII but not ASCII, would you believe?)
http://www.white-flame.com/wfdis/
I modified B-em's debugger so that I could just take a 64K dump of the entire address space, and then tossed it into WFDIS. If you don't want your browser to eat 2 GB of RAM and freeze up for 15 minutes every time you load your work, you should probably avoid doing this (and, by the way, this is on an overclocked 4790K at 4.5 GHz with 16 GB in the tank; Linux/Firefox -- your mileage may vary, but I don't recommend doing what I did if you have anything vital open in a browser window). Once it's actually up and running, it's lethargic but usable, and I particularly like just being able to click on a label and be taken straight to its memory location, then use the browser "Back" button to get back to where I was originally. In fact, since I had my own code open alongside the assembly language, I started to get annoyed with my IDE for not working the same way as the disassembler.
Regarding Citadel-early; everyone can see where this version differs obviously from the retail version, but honestly it's the subtle changes that are easy to miss that I find more interesting. Have a gander at this: This is an animated GIF, so you'll have to click on it -- the preview won't animate it.
In case it isn't obvious (it wasn't to me), the game uses PLOT 85 (or 86, I can't remember the difference) to draw optional triangles as part of a room's decor, and this can be used both constructively to make shapes and destructively to hollow out non-rectangular areas (e.g. Main Hall ceiling). These are used to draw the parabolic dish in the Star Port.
The antenna changed too, in order to fix up the hole cut out of the triangles by the tile:
How many people spotted this secret passage in the Temple, which allows access to the pulpit from the rope on the outer wall? I don't know why it was removed in the retail version -- perhaps to save RAM:
It's a neat engine. If you were reversing something like a NES or Game Boy game, all of the graphics would be cut into equally-sized rectangular tiles, so if you wanted to find a big sprite like Citadel's monks or the cannons, you'd have to go trawling through the tile data looking for a bit of cowl here or a bit of arm there. Citadel natively supports arbitrarily sized tiles, so you get the entire monk in one go. I admit I was slightly disappointed to discover that the game spends the overwhelming majority (80% ?) of its time doing absolutely nothing, though. :P
I am now wondering whether Jakobsen's earlier game (The Pyramid?) shares its tile format with Citadel, because it certainly shares some of the graphics. If so, the (still unfinished) tile unpacker I've been working on might also unpack the graphics from that game.
The seductive thing about Citadel was that until the day you racked up that 99 score, you could never be sure you'd seen all of it. Now, I've got a 99 score many times, but some people are just goddamned stubborn. :P
I've been using the White Flame online 6502 disassembler for this task. This is flawed in a few ways, but it also has some appealing aspects. Worth a look if you've not seen it before, although it is very much geared towards the C64 (supports PETSCII but not ASCII, would you believe?)
http://www.white-flame.com/wfdis/
I modified B-em's debugger so that I could just take a 64K dump of the entire address space, and then tossed it into WFDIS. If you don't want your browser to eat 2 GB of RAM and freeze up for 15 minutes every time you load your work, you should probably avoid doing this (and, by the way, this is on an overclocked 4790K at 4.5 GHz with 16 GB in the tank; Linux/Firefox -- your mileage may vary, but I don't recommend doing what I did if you have anything vital open in a browser window). Once it's actually up and running, it's lethargic but usable, and I particularly like just being able to click on a label and be taken straight to its memory location, then use the browser "Back" button to get back to where I was originally. In fact, since I had my own code open alongside the assembly language, I started to get annoyed with my IDE for not working the same way as the disassembler.
Regarding Citadel-early; everyone can see where this version differs obviously from the retail version, but honestly it's the subtle changes that are easy to miss that I find more interesting. Have a gander at this: This is an animated GIF, so you'll have to click on it -- the preview won't animate it.
In case it isn't obvious (it wasn't to me), the game uses PLOT 85 (or 86, I can't remember the difference) to draw optional triangles as part of a room's decor, and this can be used both constructively to make shapes and destructively to hollow out non-rectangular areas (e.g. Main Hall ceiling). These are used to draw the parabolic dish in the Star Port.
The antenna changed too, in order to fix up the hole cut out of the triangles by the tile:
How many people spotted this secret passage in the Temple, which allows access to the pulpit from the rope on the outer wall? I don't know why it was removed in the retail version -- perhaps to save RAM:
It's a neat engine. If you were reversing something like a NES or Game Boy game, all of the graphics would be cut into equally-sized rectangular tiles, so if you wanted to find a big sprite like Citadel's monks or the cannons, you'd have to go trawling through the tile data looking for a bit of cowl here or a bit of arm there. Citadel natively supports arbitrarily sized tiles, so you get the entire monk in one go. I admit I was slightly disappointed to discover that the game spends the overwhelming majority (80% ?) of its time doing absolutely nothing, though. :P
I am now wondering whether Jakobsen's earlier game (The Pyramid?) shares its tile format with Citadel, because it certainly shares some of the graphics. If so, the (still unfinished) tile unpacker I've been working on might also unpack the graphics from that game.
- Diminished
- Posts: 598
- Joined: Fri Dec 08, 2017 9:47 pm
- Contact:
Re: Unused Citadel bits
Not sure what forum policy on double posts is. Let me know if I've messed up.
About eight years ago I worked out the RLE used for the game's title screen, so here's a short C snippet which is capable of unpacking the game's title screen from the CITAT file. (I can't remember which version of the game this applies to, I'm afraid).
It spits out a 24 bpp RGB bitmap at 320x256 (GIMP will load this in RAW mode).
Code below.
About eight years ago I worked out the RLE used for the game's title screen, so here's a short C snippet which is capable of unpacking the game's title screen from the CITAT file. (I can't remember which version of the game this applies to, I'm afraid).
It spits out a 24 bpp RGB bitmap at 320x256 (GIMP will load this in RAW mode).
Code below.
Code: Select all
#include <stdio.h>
#include <string.h>
// macro maps a mode 4 screen position to a linear one
#define OPOS(X) (24*((X/320)*320+(X%320)/8+(X%8)*40))
int main (int argc, char *argv[]) {
unsigned int i=0;
FILE *f1, *f2;
char buf[3];
if (argc!=3) {
printf ("usage: citat_unpacker <CITAT source file> <target bitmap file>\n");
return 1;
} else if (!(f1=fopen(argv[1],"r"))) {
printf ("Could not open source file \"%s\".\n", argv[1]);
return 2;
} else if (!(f2=fopen(argv[2],"w"))) {
printf ("Could not open output bitmap file \"%s\".\n", argv[2]);
fclose(f1);
return 3;
}
while (i<0x2800) {
int c,d=0,j;
unsigned int k;
// read one byte in c, if 0x00 or 0xff, read another in d:
if ((c=getc(f1)) == EOF || ((!c || c==0xff) && ((d=getc(f1)) == EOF))) {
fprintf (stderr, "error: EOF @ i=0x%0x\n", i);
fclose(f1);
fclose(f2);
return 4;
}
switch (c&0xff) {
case 0x00:
case 0xff:
// escaped ("packed") case
for (j=0;j<d+1;j++) {
if (fseek(f2, OPOS(i), SEEK_SET) == -1) {
printf ("Error seeking to position 0x%0x in output file.\n", OPOS(i));
fclose(f1);
fclose(f2);
return 5;
}
memset(buf,(~c)&0xff,3); // invert colours
for (k=0;k<8;k++)
fwrite(buf,1,3,f2); // write 24 bytes (= 8 pixels each with red, green, blue bytes)
i++;
}
break;
default:
// standard bitmapped byte case
if (fseek(f2, OPOS(i), SEEK_SET) == -1) {
printf ("Error seeking to position 0x%0x in output file.\n", OPOS(i));
fclose(f1);
fclose(f2);
return 5;
}
for (j=0;j<8;j++) {
memset(buf,((((~c)&0xff)<<j)&0x80)?0xff:0,3); // invert colours
fwrite(buf,1,3,f2); // red, green, blue
}
i++;
break;
}
}
fclose(f1);
fclose(f2);
return 0;
}
Re: Unused Citadel bits
Interesting stuff, thanks for sharing.
At this point the question on the tip of my tongue is. "Can it be converted to NULA 16 colours easily?"
At this point the question on the tip of my tongue is. "Can it be converted to NULA 16 colours easily?"
So many projects, so little time...
- Diminished
- Posts: 598
- Joined: Fri Dec 08, 2017 9:47 pm
- Contact:
Re: Unused Citadel bits
I confess, I had to google what NULA is. >_<
I spent 15 minutes thinking about it and it started to make my brain hurt. I don't know enough about either the NULA board or the game itself at this point to make an educated guess about how difficult that job would be.
Does the NULA board provide any extra RAM, or any way of easing the pressure on the standard 32K? Citadel's RAM usage is pretty tight (stands to reason, really). My feeling at this point is that it could be done, provided you could find some more RAM from somewhere. It might be a bit fiddly. Most of the game's drawing is achieved through a routine at 0x2564 in the BBC disc version, which blits "staged" MODE 2 pixel pairs from a memory buffer at 0x500, so you'd want to intercept that. IIRC there are a few places where it bypasses this, though, and operates on VRAM directly. Also, of course, the game's collision map is in VRAM too (by abusing the MODE 2 flashing colours), so you'd have to provide compatibility for that. Then there are some other things like the lightshows that occur when you use the teleporter, and the "ReProgramming" one, which might be hard to simulate with a different pixel format.
Short answer is I don't know.
Not yet, anyway.
I spent 15 minutes thinking about it and it started to make my brain hurt. I don't know enough about either the NULA board or the game itself at this point to make an educated guess about how difficult that job would be.
Does the NULA board provide any extra RAM, or any way of easing the pressure on the standard 32K? Citadel's RAM usage is pretty tight (stands to reason, really). My feeling at this point is that it could be done, provided you could find some more RAM from somewhere. It might be a bit fiddly. Most of the game's drawing is achieved through a routine at 0x2564 in the BBC disc version, which blits "staged" MODE 2 pixel pairs from a memory buffer at 0x500, so you'd want to intercept that. IIRC there are a few places where it bypasses this, though, and operates on VRAM directly. Also, of course, the game's collision map is in VRAM too (by abusing the MODE 2 flashing colours), so you'd have to provide compatibility for that. Then there are some other things like the lightshows that occur when you use the teleporter, and the "ReProgramming" one, which might be hard to simulate with a different pixel format.
Short answer is I don't know.

- Kecske Bak
- Posts: 720
- Joined: Wed Jul 13, 2005 8:03 am
- Location: Treddle's Wharf, Chigley
- Contact:
Re: Unused Citadel bits
To be fair, Citadel also had to work on an Electron, which could only run in Mode 2 at an arthritic pace.Diminished wrote:I admit I was slightly disappointed to discover that the game spends the overwhelming majority (80% ?) of its time doing absolutely nothing, though.
- Diminished
- Posts: 598
- Joined: Fri Dec 08, 2017 9:47 pm
- Contact:
Re: Unused Citadel bits
That's true, although the Elk version was kind of an afterthought, I believe?
At any rate, anyone who has tried the Cheat It Again Joe cheat for the game to increase the speed knows that the engine can absolutely rip along at full speed. The main wait loop lives at 0x4110:
One way I was thinking it might be improved would be to stagger the animations, so it would update the player one frame, then on the next frame it could update one of the enemies, the next frame a different one, and so on. Everything would animate at the same speed, but you would get a much smoother overall effect.
At any rate, anyone who has tried the Cheat It Again Joe cheat for the game to increase the speed knows that the engine can absolutely rip along at full speed. The main wait loop lives at 0x4110:
Code: Select all
master_speed_wait lda num_irqs_counter
; CONSTANT: overall game speed
cmp #$04
bcc master_speed_wait
lda #$00
sta num_irqs_counter
rts
- Rich Talbot-Watkins
- Posts: 1707
- Joined: Thu Jan 13, 2005 5:20 pm
- Location: Palma, Mallorca
- Contact:
Re: Unused Citadel bits
Great post! And welcome to the forum 
Citadel used to fascinate me when I was young. Back in school, the Citadel 'Zap' hack was going round, which had the option for super speed and added a key to fly. When I got my Master, I was utterly disappointed to discover that it didn't run (it got as far as writing "Welcome to..." and then hung), and I never figured out what the problem was. It was many years later that I borrowed (ahem) a friend's copy of Play It Again Sam on tape which had a Master-compatible version and I was able to play it once more.
Citadel really had a unique look and feel - the use of PLOT 85 triangles as well as conventional tile sprites really made it stand out. I remember seeing all the room names written backwards in the executable, scattered throughout the code. It's great that you've started to reverse-engineer it - it'd be really cool to see a disassembly one day (and to figure out why the Master wasn't happy to run it).

Citadel used to fascinate me when I was young. Back in school, the Citadel 'Zap' hack was going round, which had the option for super speed and added a key to fly. When I got my Master, I was utterly disappointed to discover that it didn't run (it got as far as writing "Welcome to..." and then hung), and I never figured out what the problem was. It was many years later that I borrowed (ahem) a friend's copy of Play It Again Sam on tape which had a Master-compatible version and I was able to play it once more.
Citadel really had a unique look and feel - the use of PLOT 85 triangles as well as conventional tile sprites really made it stand out. I remember seeing all the room names written backwards in the executable, scattered throughout the code. It's great that you've started to reverse-engineer it - it'd be really cool to see a disassembly one day (and to figure out why the Master wasn't happy to run it).
Re: Unused Citadel bits
It would also be interesting to know why the splashes occur. 

- Diminished
- Posts: 598
- Joined: Fri Dec 08, 2017 9:47 pm
- Contact:
Re: Unused Citadel bits
Oh, I am with you on the splashes, 100%. I've wanted to know the answer to that puzzle since I was ten, but I don't have the answer yet. It is indeed calling the splash subroutine erroneously (that's at 0x414b, and confirmed by breakpoint), but I don't know why. Another puzzle that I got the answer to is whether the "fast climb down" (hold down and jump/action while on a rope or a ladder) was a bug or a feature. (It's a feature -- game specifically checks the "action button is down" flag in the "climb down" code, and I can't see any reason why else it would do that unless that behaviour was intentional.)
It wouldn't run on a Master? I'm surprised -- it's not *that* naughty. It even ran on my 32K Model A!
I have no idea why. I thought it might be that it writes directly to MOS RAM locations for things like the graphics cursor, text cursor, plot colours, and the VSYNC interrupt vector -- but AllMem.txt seems to think those addresses didn't move on the Master. So that's another puzzle to solve.
It's interesting that you bring up the room names being stored backwards and also being "scattered around the code", because one of those things is actually a consequence of the other. There's a master table of room names in one page at 0x2400. This makes sense, since many room names are used several times (The Desert, The Well, The Pyramid, The Temple etc.), and the room data references these. However, rooms whose names are not in the master table have them inlined into the room data itself, immediately after the initial length byte. And this is also why the names are backwards -- since there are two possible sources for the room names, the game stages them on the stack before they're printed. And since stacks are LIFO, they have to be stored backwards.
Another cute thing is that there is a bit in the room data which dictates whether the name should have the word "The" prepended to it -- even those few bytes were precious. (Actually, it's not prepended, it's appended; it goes onto the stack last, and comes off first.)
Here's the room name printing code, which uses the MOS routine (I think this was known as OSWRCH? I've just called it "putchar". Kernighan & Richie come to mind before Wilson & Furber, I'm afraid.)
Here's a peek at how my PHP reimplementation is doing. So, The Well Wheel is meant to look like this:
My code currently renders it like this:
The tile in the very top-left hand corner of the screen is the "flood tile". The entire screen is filled with this before any further drawing is done. I haven't bothered to duplicate that piece of code because it's trivial and not really very useful at this point -- instead I start with a blue buffer, so you can still see where the rectangle drawing "hollowing out" code is doing its thing.
The game seems to make a few passes at what I'm calling "decor". One of these occurs before the rectangles and triangles are drawn, and those are the green/black/cyan bricks at the bottom. Then the room is hollowed out by the rectangle code, and the PLOT 85 triangles are drawn (IIRC, there can be up to eight triangles). Then the game makes a second "decor" pass, which draws the shield in the above image. The shield makes use of a tile-flipping system which uses a buffer at 0x400, but that's currently buggy, as is shown by the vomit to the right of the shield.
If you'd like to see the game actually draw the rooms (it normally redefines the graphics window, so you can't see it happen), try these pokes (again, this is the BBC disc version. Well, I think it is. It's just Citadel.ssd on my hard drive, I don't know where I got it from):
EDIT: sorry, copied the wrong pokes ... I seem to have lost the correct ones. D:
EDIT2:
*(0x4122)=0xea
*(0x4123)=0xea
*(0x4124)=0xea
*(0x4127)=0xea
*(0x4128)=0xea
*(0x4129)=0xea
It wouldn't run on a Master? I'm surprised -- it's not *that* naughty. It even ran on my 32K Model A!
I have no idea why. I thought it might be that it writes directly to MOS RAM locations for things like the graphics cursor, text cursor, plot colours, and the VSYNC interrupt vector -- but AllMem.txt seems to think those addresses didn't move on the Master. So that's another puzzle to solve.
It's interesting that you bring up the room names being stored backwards and also being "scattered around the code", because one of those things is actually a consequence of the other. There's a master table of room names in one page at 0x2400. This makes sense, since many room names are used several times (The Desert, The Well, The Pyramid, The Temple etc.), and the room data references these. However, rooms whose names are not in the master table have them inlined into the room data itself, immediately after the initial length byte. And this is also why the names are backwards -- since there are two possible sources for the room names, the game stages them on the stack before they're printed. And since stacks are LIFO, they have to be stored backwards.
Another cute thing is that there is a bit in the room data which dictates whether the name should have the word "The" prepended to it -- even those few bytes were precious. (Actually, it's not prepended, it's appended; it goes onto the stack last, and comes off first.)
Here's the room name printing code, which uses the MOS routine (I think this was known as OSWRCH? I've just called it "putchar". Kernighan & Richie come to mind before Wilson & Furber, I'm afraid.)
Code: Select all
; ROOM NAME
iny
; cur_room_data_pos=1
sty cur_room_data_pos
; fetch first byte of room data, room[1]
jsr get_next_room_byte
bmi L3486
; if (room[1] & 0x80 == 0), room name is inline
P_push_inline_name inx
; push name to stack
pha
tay
; terminator: if (room[1+x] & 0x80) then quit
bmi Lroomname_push_done
; room[2+x]
jsr get_next_room_byte
; or, if the following byte (room[2+x]) is zero, print "The"
beq Lpush_The
bne P_push_inline_name
; } else { // room name uses master room table
L3486 and #$7f
; reference in low 7 bits of room[1]
tax
ldy #$ff
; find pointer to the room name of the Xth room in master table; count the terminators
P_find_room_name iny
lda GLOBAL_ROOM_NAMES,y
bpl P_find_room_name
dex
bpl P_find_room_name
P_push_room_name pha
inx
iny
lda GLOBAL_ROOM_NAMES,y
; again, if the following byte is zero, end with a "The"; if the top bit is set, there isn't
bmi Lroomname_push_done
bne P_push_room_name
; } // endif room[1] & 0x80 thing
Lpush_The lda #$20
pha
lda #$65
pha
lda #$68
pha
lda #$54
pha
inx
inx
inx
inx
Lroomname_push_done stx foo
txa
lsr a
sec
sbc #$09
eor #$ff
tax
ldy #$00
jsr move_cursor_print_char_a
ldx foo
P_print_room_name pla
and #$7f
jsr putchar
dex
bpl P_print_room_name
ldx #$09
jsr move_cursor_print_char
ldx #$d3
lda v12d
bpl L34de
sed
lda #$00
sec
sbc v12d
cld
ldx #$dc
L34de stx _text_bgcolour
stx _text_fgcolour
tay
and #$0f
ora #$30
tax
tya
and #$f0
lsr a
lsr a
lsr a
lsr a
beq L34f3
ora #$10
L34f3 ora #$20
ldy #$09
jsr move_cursor_print_char_b
ldx #$d3
lda not_in_subroom
bpl L3508
eor #$ff
clc
adc #$01
ldx #$dc
L3508 stx _text_bgcolour
stx _text_fgcolour
ora #$30
jsr putchar
My code currently renders it like this:
The tile in the very top-left hand corner of the screen is the "flood tile". The entire screen is filled with this before any further drawing is done. I haven't bothered to duplicate that piece of code because it's trivial and not really very useful at this point -- instead I start with a blue buffer, so you can still see where the rectangle drawing "hollowing out" code is doing its thing.
The game seems to make a few passes at what I'm calling "decor". One of these occurs before the rectangles and triangles are drawn, and those are the green/black/cyan bricks at the bottom. Then the room is hollowed out by the rectangle code, and the PLOT 85 triangles are drawn (IIRC, there can be up to eight triangles). Then the game makes a second "decor" pass, which draws the shield in the above image. The shield makes use of a tile-flipping system which uses a buffer at 0x400, but that's currently buggy, as is shown by the vomit to the right of the shield.
If you'd like to see the game actually draw the rooms (it normally redefines the graphics window, so you can't see it happen), try these pokes (again, this is the BBC disc version. Well, I think it is. It's just Citadel.ssd on my hard drive, I don't know where I got it from):
EDIT: sorry, copied the wrong pokes ... I seem to have lost the correct ones. D:
EDIT2:
*(0x4122)=0xea
*(0x4123)=0xea
*(0x4124)=0xea
*(0x4127)=0xea
*(0x4128)=0xea
*(0x4129)=0xea
Last edited by Diminished on Fri Dec 15, 2017 8:54 pm, edited 1 time in total.
- Rich Talbot-Watkins
- Posts: 1707
- Joined: Thu Jan 13, 2005 5:20 pm
- Location: Palma, Mallorca
- Contact:
Re: Unused Citadel bits
Interesting details with the strings! So what's the format for each screen then? I guess it must need a few chunks of data:
- base texture
- filled/solid tilemap (1 bit each I guess)
- overlayed objects
- triangles
There's a thread here in which we talked about Citadel a little bit, and I mentioned the Master incompatibility thing again. I'd forgotten that the fix for the Master was actually to use Sideways RAM to store a bit of extra code which wouldn't fit in main RAM, so clearly the Master OS was much more fussy about not having its workspace trampled. Still can't imagine what would cause it to actually hang though.
One thing about the Master version is that some of the stripe fills (which exist as an undefined side effect of using GCOL with out of range values) look different. It also shows up the fact that the roof of the Witch's House is not straight (strange they didn't get the coordinates right), as on the Master PLOT 85 is more accurate (screengrabs courtesy of DaveJ from that other thread):
BBC B

Master

- base texture
- filled/solid tilemap (1 bit each I guess)
- overlayed objects
- triangles
There's a thread here in which we talked about Citadel a little bit, and I mentioned the Master incompatibility thing again. I'd forgotten that the fix for the Master was actually to use Sideways RAM to store a bit of extra code which wouldn't fit in main RAM, so clearly the Master OS was much more fussy about not having its workspace trampled. Still can't imagine what would cause it to actually hang though.
One thing about the Master version is that some of the stripe fills (which exist as an undefined side effect of using GCOL with out of range values) look different. It also shows up the fact that the roof of the Witch's House is not straight (strange they didn't get the coordinates right), as on the Master PLOT 85 is more accurate (screengrabs courtesy of DaveJ from that other thread):
BBC B
Master
Re: Unused Citadel bits
This is a fascinating thread - if there's a chance of a 16-colour adaption for NuLA that would be cracking.
Likewise - an Elk version that blanked off the garbage at the top/bottom of the screen would be very much welcome too. Is that a possibility?
Likewise - an Elk version that blanked off the garbage at the top/bottom of the screen would be very much welcome too. Is that a possibility?
- Diminished
- Posts: 598
- Joined: Fri Dec 08, 2017 9:47 pm
- Contact:
Re: Unused Citadel bits
I won't lie -- I don't know yet!Rich Talbot-Watkins wrote:Interesting details with the strings! So what's the format for each screen then? I guess it must need a few chunks of data:
- base texture
- filled/solid tilemap (1 bit each I guess)
- overlayed objects
- triangles
The trouble with trying to hack on a game like this is that you can't really modify it in any nontrivial way, because you can't reassemble it. Rather, you have to modify the emulator instead to effect the changes you wanted to make, and recompile that. Now, this is an entirely valid technique which I've used a lot, but it's too clunky to produce much insight.
I'm sure there are people out there who can stare at 8K of code and figure out its twists and turns and what it does just by staring at it -- but I'm not one of those individuals. At least, I reached a certain point where ogling the code and labelling variables and routines wasn't gaining me any extra insight. Part of the problem -- and I suspect everyone else already knows this, but I've never really done this before -- is the reuse of variables in the original game for multiple purposes. So, you can label a variable "vram_ptr" because the game uses it for that purpose in one place, but that's no guarantee that other code doesn't use the same location for some completely unrelated purpose. In order to actually understand the code, I was going to need to reimplement it.
So, I took the attitude that I was just going to do a very literal port of the machine code, not worrying much at all about whether I understood it or not. The only places I've really tried hard to understand what is going on is in areas that affect control flow, and that's because you have to normalise the deranged machine code branching in order to produce something that works in a structured language with no goto keyword.
Instead, my approach has been to instrument the emulator, printing out the program counter when it reaches locations that indicate branches taken, along with the values of relevant variables. The PHP code has also been heavily sprinkled with trace statements, printing out those same program counter addresses. Bugs in the reimplementation can then be found and fixed by comparing the two traces.
Once my code actually works, only then am I going to concern myself seriously with figuring out what it actually does, de-overloading variables that have multiple uses, and documenting the formats -- because at that point you have the power of trial and error on your side. Don't know if vram_ptr is actually used as a VRAM pointer in some other routine? Fine, just rename the variable in that routine and see what happens!
The back-of-my-mind goal here was to think about doing a remake using a modern engine. In the spirit of things like ScummVM, the idea was to have it use the game's original data files as its source material, because this neatly bypasses all issues with copyright. A remake would allow you to do some interesting things like have one continuous scrolling gameworld rather than the "flick-screen" employed by the original game, and there are some other potential gains, like being able to draw the vector stuff (triangles) at native resolution, yielding nice smooth edges. But I have no idea if I'll ever get to that point.
Having said all that, Rich -- I do know that the rectangle drawing code doesn't use a 1-bit bitmap as you suggest -- it's literally a list of rectangles (X, Y, width, height). One reason for this is that the rectangle code isn't just used for hollowing out -- the Witch's House window and door, for example, are non-black rectangles.
That's really interesting about the BBC Master and the Witch's House. I did just try pointing my code at the Witch's House to see if it could extract the triangle coordinates, but it's messing up somewhere before it gets to that point, so I'm not there yet.
Arcadian -- I don't think a non-garbaged Electron version would be possible, unfortunately. The BBC version just reprograms the ... CRTC (??) ... to mask off those garbage areas. If the Electron doesn't have the hardware to do this, I don't think it's possible. You would need to save several kilobytes of RAM in order to make those areas black. Perhaps something insane, like compressing the rooms, tiles, and room names with zlib would get you there ... but I don't know how else you could do it.
Re: Unused Citadel bits
Quite a few late Electron games blanked almost half the screen - I'm wondering if it is related to that tape counter interrupt - but I'd rather see garbage on the screen than have only the bottom half of the display being used by everything. Citadel is at least quite good at making it look like random noise compared to various Peter Scott games that just threw all the sprite data onto the screen. (I think Peter Scott used that half-screen blanking later on, if it was him who ported Predator.) The Tony Oakden games are interesting with regard to blanking, too, although it isn't always seamless and possibly slows those games down somewhat.Diminished wrote:Arcadian -- I don't think a non-garbaged Electron version would be possible, unfortunately. The BBC version just reprograms the ... CRTC (??) ... to mask off those garbage areas. If the Electron doesn't have the hardware to do this, I don't think it's possible. You would need to save several kilobytes of RAM in order to make those areas black. Perhaps something insane, like compressing the rooms, tiles, and room names with zlib would get you there ... but I don't know how else you could do it.
-
- Posts: 513
- Joined: Sat Dec 23, 2000 5:56 pm
- Contact:
Re: Unused Citadel bits
It probably depends on how serious you were with that 20% CPU usage comment? Otherwise: when the end-of-display interrupt files busy loop until the end of the final garbage line, set the game palette and return from interrupt. When the 100Hz interrupt fires, busy wait until the first garbage line and then set the palette to all black and return.Diminished wrote:Arcadian -- I don't think a non-garbaged Electron version would be possible, unfortunately. The BBC version just reprograms the ... CRTC (??) ... to mask off those garbage areas. If the Electron doesn't have the hardware to do this, I don't think it's possible. You would need to save several kilobytes of RAM in order to make those areas black. Perhaps something insane, like compressing the rooms, tiles, and room names with zlib would get you there ... but I don't know how else you could do it.
Assuming top and bottom garbage sizes are exactly the same, I think that leaves about 100 lines of doing something other than busy waiting: it's however many lines of garbage there are at the bottom plus the amount of time from the top of the display to the 100Hz interrupt minus the number of garbage lines at the top. So it's just the number of lines to the 100Hz interrupt.
You're on an Electron though. So it's around 100 lines with only 24 available RAM cycles on each. So, umm, prima facie 2,400 cycles/frame. Unworkable.
So shift the display to the top, putting all the garbage at the bottom, then just do: from 100Hz interrupt busy wait for start of garbage, set black palette; from end-of-display set real palette and immediately return. Eyeballing it, it looks like about eight character lines of garbage total (?), so the busy loop would occupy about 92 lines, but they're all pixel lines. So I make that approximately 164 pixels lines plus 56 blank lines of real game processing — somewhere around 7,520 cycles/frame.
Which is 18.8% (!) as much as a BBC.
(Also: I'm pretty sure that Spellbound Dizzy, on the 8-bit micros, also uses a vector map with patterned fills plus some pixel sprite adornments, but that's just an assumption based on the switch in developers and corresponding simpler but much larger map, plus the contours involved)
- Diminished
- Posts: 598
- Joined: Fri Dec 08, 2017 9:47 pm
- Contact:
Re: Unused Citadel bits
So, I typed up a post about the "20% CPU usage" thing, then I started second-guessing it. Then third-guessing it.
Earlier I posted this loop, where the game spends most of its time:
I got tired of trying to work it out analytically, so I just modified the emulator to time it: Over the course of 10 minutes, in a room with a reasonable amount of animation going on, the game spent 419 seconds in this loop, which is 70% of its runtime.
It also spends some time waiting for VSYNC in another place; I timed this, too, and that eats an additional 7% of its runtime.
It's tight, isn't it? But it might just be possible.
Earlier I posted this loop, where the game spends most of its time:
Code: Select all
master_speed_wait lda num_irqs_counter
; CONSTANT: overall game speed
cmp #$04
bcc master_speed_wait
lda #$00
sta num_irqs_counter
rts
I got tired of trying to work it out analytically, so I just modified the emulator to time it: Over the course of 10 minutes, in a room with a reasonable amount of animation going on, the game spent 419 seconds in this loop, which is 70% of its runtime.
It also spends some time waiting for VSYNC in another place; I timed this, too, and that eats an additional 7% of its runtime.
It's tight, isn't it? But it might just be possible.
- Diminished
- Posts: 598
- Joined: Fri Dec 08, 2017 9:47 pm
- Contact:
Re: Unused Citadel bits
Finally ... that was more of a struggle than I expected. One self-modification I carelessly missed, an off-by-one bug, and ... some other bug I can't remember. (Git knows.) I'm pretty sure now that the tile rendering works 100%, so I just need to finish figuring out the room format:
- Rich Talbot-Watkins
- Posts: 1707
- Joined: Thu Jan 13, 2005 5:20 pm
- Location: Palma, Mallorca
- Contact:
Re: Unused Citadel bits
I'm guessing the lack of the chimney shaft in that picture is some other off-by-one bug (maybe the last rectangle or something?). Does Citadel's sprite routine have the ability to recolour sprites as well as flip them (going by the shield whose right half has different colours to the left half)?
- Diminished
- Posts: 598
- Joined: Fri Dec 08, 2017 9:47 pm
- Contact:
Re: Unused Citadel bits
Not sure whether it's an off-by-one on the chimney shaft ... I just looked at that code now, and altering the loop to draw one more rectangle doesn't seem to fix it. At this time my guess is that the rope drawing code is responsible for hollowing that section out, but although I've found and mostly annotated that piece of disassembly, I haven't made much effort to understand it yet. As Jakobsen mentioned when interviewed for Retro Gamer, the ropes and ladders are drawn procedurally.
I was also wrong about the shield. This is why I've avoided publishing anything yet. At some point I'll likely get sick of working on this, and I'll publish everything then, but posting things prematurely that are wrong is just a big waste of everyone's time ...
Now I have more confidence in the tile extracting code, I took a shot at divorcing it from the room code, i.e. calling it directly so that all the tiles could be extracted to bitmaps one after the other, and I got a surprise -- many of the tiles have multiple "layers". The shield is one of them:
This doesn't use the flipping routine I mentioned earlier (at 0x3274), which mostly seems to be used by animations -- I've confirmed this by NOP-neutering it, and the shield is unaffected. That's not to say that the tile unpacker doesn't contain some piece of "flip and recolour" code which does a similar job, but if that code does exist then it's quickly running out of places to hide.
A mummy with white eyes also appears to be unused content, but I can't absolutely confirm that until a way can be found to make the game display it rather than my own scripts, because evidence suggests I'm not quite bug-free yet:
The game doesn't seem to use the white-eyed mummies, so it's hard to say if it's intentional or not ... but that last sprite doesn't look right ...
I was also wrong about the shield. This is why I've avoided publishing anything yet. At some point I'll likely get sick of working on this, and I'll publish everything then, but posting things prematurely that are wrong is just a big waste of everyone's time ...
Now I have more confidence in the tile extracting code, I took a shot at divorcing it from the room code, i.e. calling it directly so that all the tiles could be extracted to bitmaps one after the other, and I got a surprise -- many of the tiles have multiple "layers". The shield is one of them:
This doesn't use the flipping routine I mentioned earlier (at 0x3274), which mostly seems to be used by animations -- I've confirmed this by NOP-neutering it, and the shield is unaffected. That's not to say that the tile unpacker doesn't contain some piece of "flip and recolour" code which does a similar job, but if that code does exist then it's quickly running out of places to hide.
A mummy with white eyes also appears to be unused content, but I can't absolutely confirm that until a way can be found to make the game display it rather than my own scripts, because evidence suggests I'm not quite bug-free yet:
The game doesn't seem to use the white-eyed mummies, so it's hard to say if it's intentional or not ... but that last sprite doesn't look right ...
- Diminished
- Posts: 598
- Joined: Fri Dec 08, 2017 9:47 pm
- Contact:
Re: Unused Citadel bits
Thought of a way to test it -- find another tile with >=4 layers, and modify the emulator to substitute one tile for the other. Don't know why that didn't occur to me immediately.
So, those white-eyed mummies can be added to the list of unused content. Also to my list of bugs.
So, those white-eyed mummies can be added to the list of unused content. Also to my list of bugs.

Re: Unused Citadel bits
My gawd, Beeb people are clever.
A modern native-rez version of the triangle cut-out stuff would be iffy. The lower the resolution the less exact coordinates have to be. Plus any variation in scale resolution throws it. It might work perfectly with the original vertices but I doubt it.
What an interesting thread this is. Remaking a functionally exact copy of Citadel isn't a big feat, but the fun is picking it a part and understanding a childhood favourite I suppose. Well beyond me. My only input to this is the upscaling to native rez stuff.
A modern native-rez version of the triangle cut-out stuff would be iffy. The lower the resolution the less exact coordinates have to be. Plus any variation in scale resolution throws it. It might work perfectly with the original vertices but I doubt it.
What an interesting thread this is. Remaking a functionally exact copy of Citadel isn't a big feat, but the fun is picking it a part and understanding a childhood favourite I suppose. Well beyond me. My only input to this is the upscaling to native rez stuff.

streaksy (at) gmail (dot) com
Re: Unused Citadel bits
By the way I'm ALMOST SURE I've seen white-eyed mummies at some point.... trying to work out when....
streaksy (at) gmail (dot) com
Re: Unused Citadel bits
Checking this thread sometimes, hoping for an update. It's interesting. 

streaksy (at) gmail (dot) com
Re: Unused Citadel bits
Hi Diminished, in which issue of Retro User was M. Jakobsen's interview ?
- Diminished
- Posts: 598
- Joined: Fri Dec 08, 2017 9:47 pm
- Contact:
Re: Unused Citadel bits
Well, some of them are -- most of them posted in the Kevin Edwards tape protection thread. I don't feel especially clever 90% of the time ...streaks wrote:My gawd, Beeb people are clever.
It's funny. I got here through a process sometimes referred to as "yak shaving". Over the past few years I've been half-heartedly working on a (PC) game engine. I made a series of improvements to it fairly recently (it gained, amongst other things, a dreadful Turing-complete programming language that I just made up) and decided I wanted to find something with which to test it. I thought to myself "you know, I could probably throw together a remake of Citadel in a few days with this -- I wonder how hard it would be to extract all the tiles and room data from the original game? Can't be that hard, surely. What's this I've found, an online 6502 disassembler? Hm. This shouldn't take long."streaks wrote:Remaking a functionally exact copy of Citadel isn't a big feat, but the fun is picking it a part and understanding a childhood favourite I suppose. Well beyond me. My only input to this is the upscaling to native rez stuff.
That was months ago.
I'm pretty sure I've never seen them anywhere in the BBC versions, so the only place I can think of that they might be hanging out is in the Electron version. As always, I'd be happy to be proven wrong.streaks wrote:By the way I'm ALMOST SURE I've seen white-eyed mummies at some point.... trying to work out when....
Sorry -- I'm not the world's most mentally durable individual, and I'm prone to motivational lapses. Plus I had to play guitar for a charity gig a few days before Christmas so the last couple of weeks have mostly involved fretting about that. Haha, fretting. Don't get up. I'll see myself out.streaks wrote:Checking this thread sometimes, hoping for an update. It's interesting.
Of course now I'm looking at the code again and it's making very little sense. I will either get back to working on it soon or I'll just publish what I've done and see if anyone else is brave enough to take over.
May 2012. I don't actually have a proper copy, but to my shame I was able to dig up a dodgy version online.fuzzel wrote:Hi Diminished, in which issue of Retro User was M. Jakobsen's interview ?
EDIT: just to be clear, that's Retro Gamer, not Retro User.
Re: Unused Citadel bits
Did you decode the baddie/platform movement patterns? That's probably the main source of hassle in a pixel-perfect remake. Everything else can be eyeballed from a screenshot map and I've still got a fluctuating urge to remake it as well.
The mummies with white eyes... I remember seeing it and thinking "why is that mummy's eyes white?" They might have even flashed white for some reason. And I think I've only ever seen the Beeb version. Wasn't aware of other versions. You know.. it might be something weird like pressing break and their eyes turned white for a second before the screen clears and at the time I'd have attributed it to a palette change instead of a pointer change. I know how little sense that makes but the memory is vague and disconnected and I have no good ideas. I'm also happy to be proven wrong about it. Familiarity isn't proof. I might be committing brainwrong. Whatev..
The mummies with white eyes... I remember seeing it and thinking "why is that mummy's eyes white?" They might have even flashed white for some reason. And I think I've only ever seen the Beeb version. Wasn't aware of other versions. You know.. it might be something weird like pressing break and their eyes turned white for a second before the screen clears and at the time I'd have attributed it to a palette change instead of a pointer change. I know how little sense that makes but the memory is vague and disconnected and I have no good ideas. I'm also happy to be proven wrong about it. Familiarity isn't proof. I might be committing brainwrong. Whatev..
streaksy (at) gmail (dot) com
- Diminished
- Posts: 598
- Joined: Fri Dec 08, 2017 9:47 pm
- Contact:
Re: Unused Citadel bits
I haven't looked deeply into the animations, since they're something that's dealt with after the initial room drawing is done.
Having said that, they probably go something like this:
- movement: Each sprite has a bounding box; when it hits one of the edges of the box, it changes direction. I have a feeling that this also happens automatically when the sprite encounters a wall, but that's a guess. Normally a sprite that hits its boundary just reverses its movement direction, but not always (e.g. the Arena bouncing squares make a 90 degree turn when they hit the walls). Of course the monks and the "follower"-type enemies (like the one in the room west of the Main Hall) break this rule by just homing in on you.
- animation: The game uses a few different animation modes -- e.g. layer cycling (Arena bouncing squares), horizontal flips (pig heads), etc. IIRC these should be quite easy to figure out because each different animation mode performs its updates by calling a dedicated subroutine, and the room data which defines the animations just picks one of these subroutines to use for that sprite.
- speed: Animation speeds come from one-byte entries in a constant table of length 16, meaning the speed is encoded as a single 4-bit value which just picks one of these table entries. Offhand I can't remember whether these apply to the movement speed, the animation speed, or both.
Another thing is that most of the game's coordinates are actually packed into the room data on a grid system rather than a pixel-accurate one (grid might be 16x10 but I don't have the code to hand). There's actually a dedicated subroutine for pulling a coordinate pair out of the room data, which is encoded as a single byte whose top and bottom nybbles give you the X and Y coordinates on the grid system.
I don't think any of it is that hard.
Having said that, they probably go something like this:
- movement: Each sprite has a bounding box; when it hits one of the edges of the box, it changes direction. I have a feeling that this also happens automatically when the sprite encounters a wall, but that's a guess. Normally a sprite that hits its boundary just reverses its movement direction, but not always (e.g. the Arena bouncing squares make a 90 degree turn when they hit the walls). Of course the monks and the "follower"-type enemies (like the one in the room west of the Main Hall) break this rule by just homing in on you.
- animation: The game uses a few different animation modes -- e.g. layer cycling (Arena bouncing squares), horizontal flips (pig heads), etc. IIRC these should be quite easy to figure out because each different animation mode performs its updates by calling a dedicated subroutine, and the room data which defines the animations just picks one of these subroutines to use for that sprite.
- speed: Animation speeds come from one-byte entries in a constant table of length 16, meaning the speed is encoded as a single 4-bit value which just picks one of these table entries. Offhand I can't remember whether these apply to the movement speed, the animation speed, or both.
Another thing is that most of the game's coordinates are actually packed into the room data on a grid system rather than a pixel-accurate one (grid might be 16x10 but I don't have the code to hand). There's actually a dedicated subroutine for pulling a coordinate pair out of the room data, which is encoded as a single byte whose top and bottom nybbles give you the X and Y coordinates on the grid system.
I don't think any of it is that hard.
Re: Unused Citadel bits
Yeh the theory is easy, but the actual list of bound-box coords would be a big faff to get manually, watching every room with baddies in slow-motion to get the movement nodes. But if it's purely cell-based instead of pixel-based then that definitely takes the edge off. I just checked and I think the reason I thought it was pixel-based is because some of the ornaments (cauldrens etc) are drawn on irregular offsets which threw me.
Here's something... you know the rope monsters? Do you have a rip of that sprite without the rope xor'd over it? I could un-xor it manually I suppose but I've always wondered what they're supposed to look like not xor-imposed over rope. I've always thought of them as lobsters.
Here's something... you know the rope monsters? Do you have a rip of that sprite without the rope xor'd over it? I could un-xor it manually I suppose but I've always wondered what they're supposed to look like not xor-imposed over rope. I've always thought of them as lobsters.
streaksy (at) gmail (dot) com
- Diminished
- Posts: 598
- Joined: Fri Dec 08, 2017 9:47 pm
- Contact:
Re: Unused Citadel bits
Yeah, don't be fooled by the "flood" background tile repeats. The flood tile can be literally any size and the game will just repeat it at its native size, so the background won't necessarily line up with the grid.streaks wrote:Yeh the theory is easy, but the actual list of bound-box coords would be a big faff to get manually, watching every room with baddies in slow-motion to get the movement nodes. But if it's purely cell-based instead of pixel-based then that definitely takes the edge off. I just checked and I think the reason I thought it was pixel-based is because some of the ornaments (cauldrens etc) are drawn on irregular offsets which threw me.
I really should finish the room unpacker. All these tedious numbers would just magically fall out of it if it were working properly.

Yep:streaks wrote:Here's something... you know the rope monsters? Do you have a rip of that sprite without the rope xor'd over it? I could un-xor it manually I suppose but I've always wondered what they're supposed to look like not xor-imposed over rope. I've always thought of them as lobsters.