Unused Citadel bits

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


User avatar
streaks
Posts: 262
Joined: Thu Oct 13, 2005 2:08 pm
Contact:

Re: Unused Citadel bits

Post by streaks » Sat Dec 30, 2017 11:48 pm

Tile size: Damn, just checked my tilesets and yeh some of them were wrong. I'd assumed 20x16, but in my old rips some of them are like 32x16 (the grass/sand/ground tile) but looking closer it can be reduced to 20x8. Better start over on my wall tile rips. You mentioned it before but it didn't click. I think there are only 6 wall variations used with various palette assignments so it's not a big job to re-source them. I imagine the top-left corner of the screenspace aligns with the top-left corner of the tile image..

Rope monster: Thanks, this is one big Citadel thread completed for me.
streaksy (at) gmail (dot) com

User avatar
streaks
Posts: 262
Joined: Thu Oct 13, 2005 2:08 pm
Contact:

Re: Unused Citadel bits

Post by streaks » Sat Dec 30, 2017 11:49 pm

(Those tile dimensions account for the mode 2 pixel doubling, so 32x is really 16x)
streaksy (at) gmail (dot) com

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

Re: Unused Citadel bits

Post by Diminished » Sun Dec 31, 2017 10:01 am

streaks wrote:Better start over on my wall tile rips.
Woke up, read this, stuffed PNG export into my script.

Here's the entire tileset according to my reimplementation. I wasn't going to publish this, because a) technically it's copyrighted and b) as I mentioned, a minute fraction of the layers don't render correctly and I'd hoped to wait until they did, but it sounds like it might save you some work.
citadel-bbc-retail-tiles.zip
(49.21 KiB) Downloaded 47 times
Last edited by Diminished on Sun Dec 31, 2017 10:09 am, edited 1 time in total.

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

Re: Unused Citadel bits

Post by Diminished » Sun Dec 31, 2017 10:09 am

For comparison, here's the set from citadel-early, which exhibits some differences as I mentioned.
citadel-bbc-early-tiles.zip
(50.28 KiB) Downloaded 34 times

User avatar
streaks
Posts: 262
Joined: Thu Oct 13, 2005 2:08 pm
Contact:

Re: Unused Citadel bits

Post by streaks » Sun Dec 31, 2017 12:43 pm

Haaa, this is mint. Thank you. I'm going to get busy on this.
streaksy (at) gmail (dot) com

User avatar
streaks
Posts: 262
Joined: Thu Oct 13, 2005 2:08 pm
Contact:

Re: Unused Citadel bits

Post by streaks » Sun Dec 31, 2017 1:19 pm

Not seeing torches among the tiles. Tile 063 in the early version looks like it might be a garbled torch, and in the retail version it's even more messed up.
streaksy (at) gmail (dot) com

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

Re: Unused Citadel bits

Post by Diminished » Sun Dec 31, 2017 1:41 pm

Sorry, that's a bug. The torch is actually the final tile, 117, which isn't in the ZIP file. Tile 116 (ceiling spikes) has a messed-up final layer, so what I think is happening is that it's trying to read the data for tile 117 as part of tile 116. (EDIT: nope, see five posts down.) Something similar may be happening with the mummies.

Here are the torches.
117.png
117.png (368 Bytes) Viewed 2069 times
EDIT: also, the player characters aren't in the tileset either -- as you might expect, these are special-cased and don't reside in the main tile data. The game pulls that data from a dedicated table at (IIRC) 0x600 (which I assume is replaced depending on whether you picked a male or female character), and I haven't attempted to decode that yet, although it probably uses the same format as the main tiles.

EDIT2: One other thing: There is one tile that's loaded in every room that has water, although it never seems to be displayed, and I think this is the tile you refer to as the "garbled torch" EDIT: nope, this is tile 114. There are to my mind two possibilities, but I haven't had the chance to investigate. This is either an unused water animation that Jakobsen originally implemented, decided he didn't like (or consumed too much runtime), and forgot to remove the graphics for; or (more likely) it's used for the "splash" when you jump into water. Not sure which.
Last edited by Diminished on Mon Jan 01, 2018 9:52 pm, edited 3 times in total.

User avatar
streaks
Posts: 262
Joined: Thu Oct 13, 2005 2:08 pm
Contact:

Re: Unused Citadel bits

Post by streaks » Sun Dec 31, 2017 2:07 pm

Oh, right. Gotcha.

If you're interested I found another little weird detail. You know the roof of the witch's house on the micro version.. there's a magenta pixel offset on the bottom-left corner of the triangle. It looks like it's just a star but it's actually solid for some reason.
streaksy (at) gmail (dot) com

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

Re: Unused Citadel bits

Post by Rich Talbot-Watkins » Sun Dec 31, 2017 2:58 pm

Yeah, the screenshots I added upthread show that. The reason for it is that, for some reason, the triangle coordinates which make the roof are slightly off (the two bottom y coordinates are not equal). OS 1.20 has a small bug where triangles aren't plotted correctly in this case (it just plots the first pixel and then gives up), while the Master OS renders them properly (as can be seen from the Master screenshot above). Weird that the triangle data was a bit off there!

User avatar
streaks
Posts: 262
Joined: Thu Oct 13, 2005 2:08 pm
Contact:

Re: Unused Citadel bits

Post by streaks » Sun Dec 31, 2017 4:04 pm

Aye, I assumed it was triangle dodginess
streaksy (at) gmail (dot) com

User avatar
streaks
Posts: 262
Joined: Thu Oct 13, 2005 2:08 pm
Contact:

Re: Unused Citadel bits

Post by streaks » Mon Jan 01, 2018 4:18 pm

Just realised... there's a pit of those rope monsters in the lab thing down the well without the rope xor'd over them. Sheesh.

Since I'm here, I'm noticing a few other things missing from that gfx rip collection, like ground thorns, ladders, rope.. Easy for me to get, but thought you might not have noticed (you probably did).

Edit:
Found four flavours of ground thorn: magenta and yellow that point right, red and cyan that point left.

Found four flavours of ladders and noticed the ladders are weird. There appears to be three variations of each colour. Full ladders, ladders with the top rung missing (for tops of ladders), and a half-height ladders with the top rung missing for tops of ladders that rise slightly above floor level.

Also noticed while screenshot-ripping sprites: the female's feet and hands are yellow except when climbing a rope, when they're cyan, and she suddenly has a cyan belt while climbing. Also she has pigtails, but you can only tell from behind when there's no rope xor'd over her back.
Last edited by streaks on Mon Jan 01, 2018 5:30 pm, edited 1 time in total.
streaksy (at) gmail (dot) com

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

Re: Unused Citadel bits

Post by Diminished » Mon Jan 01, 2018 5:02 pm

Sorry, I've been peddling misinformation again.

*consults notes*

Tile 114 is the one that I said might be an unused water animation, or the splash graphics.
Tile 115 is the ceiling spikes, which my script partially renders.
Tile 116 is the ground spikes, which my script doesn't render for some reason, and is missing from the ZIP.
Tile 117 is the torches, which my script will render if prodded correctly.

118 - 127 are undefined.

The ladders and ropes don't have tiles -- the game draws those procedurally, using loops.

EDIT: Pretty compact routines, too.

Ropes:

Code: Select all

                          ; blit ropes
3794: P_next_rope         jsr rdata_xy_alt_vptr
3797:                     adc #$04
3799:                     tax
379a:                     tya
379b:                     cmp #$f0
379d:                     bcs L37a2
379f:                     ora #$08
37a1:                     tay
37a2: L37a2               jsr coords_to_VRAM_ptr
37a5:                     ldx #$6c
37a7: P_rope_outer        ldy #$07
37a9: P_rope_inner        lda (vram_ptr),y
37ab:                     and #$3f
37ad:                     bne L37cb
37af:                     txa
                          ; blit first line
37b0:                     sta (vram_ptr),y
37b2:                     dey
37b3:                     lda #$5c
                          ; blit second line
37b5:                     sta (vram_ptr),y
37b7:                     dey
37b8:                     bpl P_rope_inner
37ba:                     sec
                          ; vram_ptr -= 0x280
37bb:                     lda vram_ptr
37bd:                     sbc #$80
37bf:                     sta vram_ptr
37c1:                     lda vram_ptr + 1
37c3:                     sbc #$02
37c5:                     sta vram_ptr + 1
37c7:                     cmp #$49
37c9:                     bcs P_rope_outer
37cb: L37cb               dec decor_bar
37cd:                     bne P_next_rope
                          
37cf: Lropes_done         lda room_N_plus_2
37d1:                     lsr a
37d2:                     lsr a
37d3:                     lsr a
37d4:                     lsr a
37d5:                     bne L37da
37d7: Ljump_to_stars      jmp Ldo_stars
Ladders:

Code: Select all

                          ; ladders
37da: L37da               sta decor_bar
                          ; first byte contains coordinates
37dc: P_next_ladder       jsr rdata_xy_alt_vptr
37df:                     tya
37e0:                     adc #$08
37e2:                     tay
37e3:                     jsr coords_to_VRAM_ptr
                          ; second byte contains pointer to tile?
37e6:                     jsr get_next_room_byte
37e9:                     and #$7c
37eb:                     lsr a
37ec:                     lsr a
37ed:                     sta tileptr_L
37ef:                     tya
37f0:                     and #$03
37f2:                     asl a
37f3:                     tax
                          ; load alt_vram_ptr low and high using two consecutive values from T_40C1
37f4:                     ldy #$01
37f6: P_ladder_get_avptr  lda T_40C1 + 8,x
                          ; alt_vram_ptr is just used to hold some colour bytes
37f9:                     sta alt_vram_ptr,y
37fc:                     inx
37fd:                     dey
37fe:                     bpl P_ladder_get_avptr
3800:                     bmi L380e
3802: P_ladder_outer      lda vram_ptr
3804:                     sbc #$7f
3806:                     sta vram_ptr
3808:                     lda vram_ptr + 1
380a:                     sbc #$02
380c:                     sta vram_ptr + 1
380e: L380e               lda #$40
3810:                     ldy #$1f
                          ; erase anything under the ladder
3812: P_ladder_blit_3     sta (vram_ptr),y
3814:                     dey
3815:                     bpl P_ladder_blit_3
3817:                     ldx alt_vram_ptr
                          ; CONSTANT: ladder rung colours
3819:                     ldy #$1c
381b:                     jsr draw_ladder_rung
381e:                     lda tileptr_L
3820:                     beq L3827
                          ; CONSTANT: ladder rung colours
3822:                     ldy #$18
3824:                     jsr draw_ladder_rung
3827: L3827               lda alt_vram_ptr + 1
3829:                     ldx #$07
382b:                     ldy #$27
                          ; blit right-hand ladder rail
382d: P_ladder_blit_1     sta (vram_ptr),y
382f:                     dey
3830:                     dex
3831:                     bpl P_ladder_blit_1
3833:                     ldy #$07
                          ; blit left-hand ladder rail
3835: P_ladder_blit_2     sta (vram_ptr),y
3837:                     dey
3838:                     bpl P_ladder_blit_2
383a:                     dec tileptr_L
383c:                     bpl P_ladder_outer
383e:                     dec decor_bar
3840:                     bne P_next_ladder
Ladder rung subroutine:

Code: Select all

                          ; draw horizontal line? only used by one piece of code at 37da
4074: draw_ladder_rung    sec
4075: P_draw_ladder_rung  txa
4076:                     sta (vram_ptr),y
4078:                     tya
4079:                     sbc #$08
407b:                     tay
407c:                     bpl P_draw_ladder_rung
407e:                     rts
Last edited by Diminished on Mon Jan 01, 2018 9:34 pm, edited 1 time in total.

User avatar
streaks
Posts: 262
Joined: Thu Oct 13, 2005 2:08 pm
Contact:

Re: Unused Citadel bits

Post by streaks » Mon Jan 01, 2018 5:35 pm

My missing tile screenshot-rips if you want 'em for comparison:
https://www.dropbox.com/s/0352ca1j3d880 ... y.rar?dl=0

Not "missing", but, yer know.

And if it's any use, the whole Citadel map with reference grid for tiles and rooms:
https://www.dropbox.com/s/bj9cd0qwg1ymv ... d.png?dl=0
streaksy (at) gmail (dot) com

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

Re: Unused Citadel bits

Post by Diminished » Fri Sep 14, 2018 7:23 pm

I went back to looking at my PHP reimplementation of the room unpacker, armed with the latest snapshot of b-em. I found a couple of bugs, so I'm getting a little closer.

It now gets through about 80% of the bytes representing The Well Wheel (previously it was doing fewer than 50% before giving up), and now looks like this:
ww2.png
It should look like this:
ww.png

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

Re: Unused Citadel bits

Post by Diminished » Sat Sep 22, 2018 5:22 pm

Closer now!

Still no rope or ladder routines, or pillars. However, the morass of tile code now looks like it's working close to 100%, including routines at &3274 and &32ab which stage data in a buffer at &400 and proved rather troublesome:
ww3.png
ww3.png (2.02 KiB) Viewed 1290 times
The number of rectangles drawn continues to be wrong (but I have a hunch about that).

I want to go after triangles next, so we can get the answer to that mystery about the Witch's House coordinates.
Last edited by Diminished on Sat Sep 22, 2018 5:24 pm, edited 2 times in total.

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

Re: Unused Citadel bits

Post by Diminished » Mon Sep 24, 2018 5:29 pm

So, the triangles. In order to try to understand what is going on, I modified b-em to print out equivalent BBC BASIC statements every time it did something triangle-related with the OS (i.e. illegal MOVEs and legal PLOT 85s). I also did some digging to try to find out how the OS and CRTC are set up, but I'm missing something.

This (mostly automatically generated) program ought to replicate the triangles in the Main Hall:

Code: Select all

1 MODE 2
2 ?&306=&B0: REM OS gfx window R.H. column
3 ?&30B=&08: REM OS text window top row
4 ?&30E=&E0:?&30F=&FF: REM OS graphics origin (ext. coords)
5 ?&FE00=&6:?&FE01=&18: REM 6845 vertical displayed chars=18

10 GCOL 0,129
11 CLG
12 GCOL 0,135

20 ?&314=119
30 ?&315=0
40 ?&316=152
50 ?&317=0
60 ?&324=40
70 ?&325=0
80 ?&326=152
90 ?&327=0
100 VDU 25,85,128,2,188,2
110 ?&314=20
120 ?&315=0
130 ?&316=120
140 ?&317=0
150 ?&324=40
160 ?&325=0
170 ?&326=120
180 ?&327=0
190 VDU 25,85,64,1,124,2
200 ?&314=10
210 ?&315=0
220 ?&316=72
230 ?&317=0
240 ?&324=20
250 ?&325=0
260 ?&326=71
270 ?&327=0
280 VDU 25,85,160,0,252,1
290 ?&314=139
300 ?&315=0
310 ?&316=120
320 ?&317=0
330 ?&324=120
340 ?&325=0
350 ?&326=120
360 ?&327=0
370 VDU 25,85,192,3,124,2
380 ?&314=140
390 ?&315=0
400 ?&316=72
410 ?&317=0
420 ?&324=149
430 ?&325=0
440 ?&326=71
450 ?&327=0
460 VDU 25,85,96,4,252,1
The shape is correct, but the vertical positioning is wrong compared to the actual game:
trgls.png

So, any ideas what I've missed here?

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

Re: Unused Citadel bits

Post by Rich Talbot-Watkins » Mon Sep 24, 2018 5:52 pm

Citadel uses a reduced height screen, using just the last 20 or so rows of a standard MODE 2 screen, and is then recentred vertically. So I think what you have there looks correct!

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

Re: Unused Citadel bits

Post by Diminished » Tue Sep 25, 2018 5:13 pm

Rich: How would you set that up?

Here is what the game does to draw that notorious Witch's House roof:

Code: Select all

1 MODE 2
2 ?&306=&B0: REM OS gfx window R.H. column
3 ?&30B=&08: REM OS text window top row
4 ?&30E=&E0:?&30F=&FF: REM OS graphics origin (ext. coords)
5 ?&FE00=&6:?&FE01=&18: REM 6845 vertical displayed chars=&18
1460 ?&314=119
1470 ?&315=0
1480 ?&316=72
1490 ?&317=0
1500 ?&324=40
1510 ?&325=0
1520 ?&326=71
1530 ?&327=0
1540 VDU 25,85,128,2,188,1
&316 and &326 are the OS-internal Y-coordinate low bytes for the two points that make up the base of the triangle. They differ -- one is 72 and the other is 71, leading to a lopsided triangle. On a Master, running the above program displays the bug as advertised:
stardot-trgls-1.png
witch-house-master-clean.png
So far, so good. But why?

I draw your attention to the subroutine at &407f (which I'm currently calling next_room_byte_alt; it had a much more stupid name in my previously published disassembly). This routine is one of two that are called to get the next byte from the room's data stream. (The other subroutine that does this -- next_room_byte -- is right below it at &4098).

Code: Select all

                          ; only the version in X is times 10 ! version in misc16 is just low nybble!
                          ; Y       = room_byte & 0xf0
                          ; misc16  = room_byte & 0x0f
                          ; X       = misc16 * 10
                          ; C       = 0 (?)
                          
407f: next_room_byte_alt  ldy cur_room_data_pos
4081:                     lda (room_pointer),y
4083:                     inc cur_room_data_pos
                          ; back up A
4085: coord_to_misc16_vp  tax
4086:                     and #$f0
                          ; Y = rdata byte & 0xf0
4088:                     tay
4089:                     txa
408a:                     and #$0f
                          ; A = rdata byte & 0xf
408c:                     tax
408d:                     asl a
408e:                     sta misc16
4090:                     asl a
4091:                     asl a
4092:                     adc misc16
                          ; set misc16 to rdata byte & 0xf
4094:                     stx misc16
                          ; whereas X contains 10 * (rdata byte & 0xf)
4096:                     tax
4097:                     rts
This subroutine is generally used for fetching coordinates from the room's byte stream. It splits the read byte into low and high nybbles. The high nybble goes into the Y register. The low one goes in the misc16 variable; this value is then also multiplied by 10 and put into the X register. This result is usually an X-coordinate; one nybble has a range from 0-15, giving you a grid which splits MODE 2's 160 horizontal pixels into sixteen ten-pixel-wide columns.

Does this always return with an empty carry flag? The code at 408d multiplies A by 10. The accumulator contents can never be larger than 0xf here, so the ADC at 4092 cannot produce a value larger than 150, and the carry should never be set. Is this correct?

Here is the actual triangle code. I have removed some bits which are hopefully irrelevant (X-coordinates, colour/plot mode changes, puzzle-related hacks). Note that (alt_tileptr + 1) is just being used as a temporary variable here. _old_gfx_cursor_Y_L and _cur_gfx_cursor_Y_L are the MOS locations &316 and &326, the low bytes of the MOVE y-coordinates.

It does some strange things I don't understand, so I'm just going to leave it here. In particular the code at 36f2 is odd, and there seems to be some carry flag weirdness that causes the third triangle Y-coordinate to modulate the first triangle Y-coordinate. This may be a straightforward missing-CLC bug; if you patch the emulator to clear the carry before the ADC at 371d and play on a Master, the problem goes away:
witch-house-master-clc-hax.png
However no guarantees can be made that it doesn't break some other triangle somewhere else in the game; I haven't investigated that yet, and I'm not convinced it's as simple as a missing CLC.

I am not an expert on 6502 or the Beeb so there may be any number of mistakes here.

Code: Select all

                          ; room [R]
36a6: P_triangles         jsr next_room_byte_alt

                          ; tempvar = room[R] & 0xf0:
36a9:                     sty alt_tileptr + 1

  <... snip ...>
                          
                          ; room[S] and [S+1] provide point2 and point3 to complete the triangle
36bf:                     jsr next_room_byte_alt
                          
  <... snip ...>

                          ; decrement high nybble, low nybble = F:
36c5:                     dey
36c6:                     tya
                          ; carry out will be set if room[S] high nybble was F;
                          ; otherwise this just sets the read room[S] low nybble to 7
36c7:                     adc #$08
36c9:                     eor #$ff
                          ; point1.Y = ~((room[S] & 0xf0) + 7 [+ C ?])
                          ; possible values (inputs from 00 to f0): F8 (+C), E8, D8, C8, B8, A8, 98, 88, 78, 68, 58, 48, 38, 28, 18, 08:
36cb:                     sta _old_gfx_cursor_Y_L

                          ; room [S+1]
36ce:                     jsr next_room_byte_alt

  <... snip ...>

                          ; Y is still the room[S+1] high nybble
                          ; compare the room[R] high nybble to the room[S+1] one we just read
                          ; NOTE THAT [S+1] does the PLOT 85, not the MOVE! so the PLOT 85 coord can affect one of the MOVE coords!
36f2: L36f2               cpy alt_tileptr + 1
36f4:                     beq L36f8
                          ; if they differ, decrement the stored room[R] high nybble we already read (what? why?!)
36f6:                     dec alt_tileptr + 1

  <... snip ...>

                          ; Y is still the room[S+1] high nybble
3708:                     tya
3709:                     eor #$ff
370b:                     tay

  <... snip ...>

                          ; this next part is not directly relevant to the buggy MOVE instructions, it's
                          ; a coordinate (vdu25_trgl_y_low) that is used in the PLOT 85 instruction;
                          ; however, it has an effect upon the carry
                          
                          ; Y is still the room[S+1] high nybble (with bits now flipped)
3715:                     tya
3716:                     asl a
                          ; this final ASL moves bit 6 of ~(room[S+1]) into the carry, it's then added into the ADC below .....
3717:                     asl a
                          ; trgl_y_low for PLOT 85 = [S+1] high nybble * 4:
3718:                     sta vdu25_trgl_y_low
                          ; room[R] & 0xf0, possibly decremented?
371b:                     lda alt_tileptr + 1
                          ; carry polluted by the ASL at 3717?
371d:                     adc #$08
371f:                     eor #$ff
3721:                     sta _cur_gfx_cursor_Y_L
                          
  <... snip ...>
  
                          ; execute VDU25 command
3724:                     ldx #$05
                          ; use OS to draw a filled triangle:
3726: P_vdu_25            lda vdu25_trgl_y_high,x
3729:                     jsr putchar
372c:                     dex
372d:                     bpl P_vdu_25
                          
372f: Lskipped_vdu25      dec decor_bar
                          ; P_triangles_a not shown but is just a trampoline to P_triangles
3731:                     bne P_triangles_a
Last edited by Diminished on Tue Sep 25, 2018 7:30 pm, edited 4 times in total.

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

Re: Unused Citadel bits

Post by Diminished » Sat Sep 29, 2018 5:25 pm

Here's a patch which should be useful for working on this game.

It modifies the game's "attract mode", so you can flick through every possible room ID from 0 to 255 by pressing the left and right keys at the title screen (Z and X if using control scheme 1). It also displays the displayed room ID in hexadecimal in the top right of the screen.

I've attached a disc image based on the 2-file "CITAX" version (thanks Bill Carr). CHAIN "CITADEL" to load.

The patch is in two pieces; one replaces the attract room table at &750; the other replaces the normal attract mode routine starting at &424D. No other code is touched so the game should still be playable normally.

Assemble using BeebAsm:

Code: Select all

; CITADEL ATTRACT MODE PATCH
; 'Diminished', 30th September 2018
; use left and right controls to flick through every room ID at title screen

; *LOAD CITAX 3000
; apply patch in memory; add &2C00 onto asm addresses, then
; *SAVE CITAX 3000 +4600 5E00 1900

; this will convert the .bin file into a BBC BASIC program for patching:
; #!/bin/bash
;
; Y=10
; X=$[ 0x2c00 + 0x424d ]
;
; for N in `hexdump -C attract-patch-v2-424D.bin | sed 's/|.*|//g' | tr 'a-z' 'A-Z' | sed -e 's/[0-9A-F]\{8\}//g'` ; do
;   printf '%u ?&%04X = &%s\n' $Y $X $N
;   X=$[ $X + 1 ]
;   Y=$[ $Y + 10 ]
; done
;
; Y=1000
; X=$[ 0x2c00 + 0x750 ]
;
; for N in `hexdump -C attract-patch-v2-0750.bin | sed 's/|.*|//g' | tr 'a-z' 'A-Z' | sed -e 's/[0-9A-F]\{8\}//g'` ; do
;   printf '%u ?&%04X = &%s\n' $Y $X $N
;   X=$[ $X + 1 ]
;   Y=$[ $Y + 10 ]
; done


org &424D

master_speed_wait       = &4110
animate                 = &3b74
frame_counter           = &41
do_shifter              = &40af
check_kb                = &26f9
inputstate_left         = &34
inputstate_right        = &32
inputstate_action       = &35
P_tamper_check          = &4285
current_room_id         = &37
super_draw_room         = &411d
T_ATTRACT_ROOMS         = &750
_putchar                = &ffee

.start

.P_attract_inner
                    jsr master_speed_wait
                    jsr animate
                    inc frame_counter
                    jsr do_shifter
                    jsr check_kb
                    ; start game?
                    lda inputstate_action
                    beq P_tamper_check
                    
                    clc
                    
                    lda inputstate_left
                    beq Lcheck_right
                    dec current_room_id
                    bcc Lroom_changed
                    
.Lcheck_right
                    lda inputstate_right
                    beq Lattract_continue
                    inc current_room_id
                   
.Lroom_changed
                    ldx current_room_id
                    jsr super_draw_room
                    lda #' '
                    jsr _putchar
                    lda current_room_id
                    and #&f0
                    jsr T_ATTRACT_ROOMS
                    
.Lattract_continue  
                    clc
                    bcc P_attract_inner                   
                    
.end

save  "attract-patch-v2-424D.bin", start, end

org T_ATTRACT_ROOMS

.start2

                    lsr a
                    lsr a
                    lsr a
                    lsr a
                    ora #&30
                    cmp #&3a
                    bcc Lhex1
                    adc #6   ; actually 7: carry is set
.Lhex1
                    jsr _putchar
                    lda current_room_id
                    and #&f
                    ora #&30
                    cmp #&3a
                    bcc Lhex2
                    adc #6   ; actually 7: carry is set
.Lhex2
                    jmp _putchar

.end2

save  "attract-patch-v2-0750.bin", start2, end2
Attachments
attract-patch-v2.txt
(2.98 KiB) Downloaded 14 times
Citadel-2file-attractpatch3-10.ssd
(19.75 KiB) Downloaded 15 times
Last edited by Diminished on Sat Sep 29, 2018 5:26 pm, edited 1 time in total.

6502
Posts: 21
Joined: Sat Mar 17, 2018 1:04 pm
Location: London
Contact:

Re: Unused Citadel bits

Post by 6502 » Sun Oct 28, 2018 9:18 am

I'm confused where the actual tile data is store.
I see the tile list starts at &0955 and the first byte is the length which is then walked to the next such as &0990 .... &0996 .... &09a2.
Some are only 6 bytes long, so the actual tile pixel data must be elsewhere but I cannot find where?

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

Re: Unused Citadel bits

Post by Diminished » Sun Oct 28, 2018 5:30 pm

6502 wrote:
Sun Oct 28, 2018 9:18 am
I'm confused where the actual tile data is store.
I see the tile list starts at &0955 and the first byte is the length which is then walked to the next such as &0990 .... &0996 .... &09a2.
Some are only 6 bytes long, so the actual tile pixel data must be elsewhere but I cannot find where?
The tiles at &955 are encoded using some sort of variable-length, cunningly packed bytecode I still don't really understand. One thing I never tried is a black-box technique, where you fiddle with the tile data and see what the effect is of doing so. It's entirely likely that you'd gain more insight this way than you would just by gawking at the asm like I did.

There is a table in citadel.asm which is also involved. I believe each byte in this table contains a pair of MODE 2 pixels. Everything seems to be built up out of these pixel pairs, and I think the &955 data makes reference to these pixel values:

.T_BASE_PIXEL_PAIRS equb &00, &01, &04, &05, &10, &11, &14, &15, &5c, &4c, &6d, &4f, &53, &73, &5c, &7c

(not to be confused with TEXT_BASE_PIXEL_PAIRS, which should probably be called T_TEXT_BASE_PIXEL_PAIRS)

Effectively this would allow you to specify two pixels with just four bits, by indexing into this table. This dereferencing seems to take place at label L317f in citadel.asm.

Remember, some tiles have multiple layers, so there's the potential for the bytecode to become quite complicated.
Last edited by Diminished on Sun Oct 28, 2018 5:50 pm, edited 4 times in total.

6502
Posts: 21
Joined: Sat Mar 17, 2018 1:04 pm
Location: London
Contact:

Re: Unused Citadel bits

Post by 6502 » Mon Oct 29, 2018 8:36 am

Done some investigation last night as suggested by Diminished using the black-box method (Flipping bits and see what happens).

I used tile 7 at &09A2 (green energy flask) as this seems a simple non-animated tile.
Tile 7 bytes : 1d 00 00 8b 59 c0 0c 77 77 10 01 f7 7b 55 5a 55 56 b7 77 55 a5 66 66 bb 77 5a 55 66 55

This appears to have 4 seperate coloured pixels. Blue, green, cyan, black which is set in bytes 3 and 4 using 3 bits each.

Byte[0] : Length of record.

Byte[1] : Unknown

Byte[2] : Unknown

Byte[3] : Bits 0,1 is the tile height in pixels. Plotting the tile starts from the top going downwards, right to left.
00 = 4 px, 01 = 8 px, 10 = 12 px, 11 = 16 px.
Bits 2,3,4 is colour 1
000 = black, 001 = red, 010 = green, 011 = yellow, 100 = blue, 101 = magenta, 110 = cyan, 111 = white
Bits 5,6,7 is colour 2
(Same colour scheme as above)

Byte[4] : Bits 0,1 unknown, but these appear to be set to 11 when short tiles (only 6 bytes) are used. Suspect when set to 11 the
tile references another tile for it data, reducing the need to waste bytes when drawing say similar tiles like the crystals
which only have the colours changed.
Bits 2,3,4 is colour 3
(Same colour scheme as above)
Bits 5,6,7 is the tile width in pixels.
001 = 4 px, 010 = 6px, 011 = 8px, 100 = 10px, 101 = 12px, 110 = 14px, 111 = 16px

Byte[5] : Each nibble references .T_BASE_PIXEL_PAIRS equb &00, &01, &04, &05, &10, &11, &14, &15, &5c, &4c, &6d, &4f, &53, &73, &5c, &7c
to actually plot the tile. Like Diminished said, this allows a byte to plot 4 pixels. (Remember mode2 is 2 pixels per byte.) This saves space at the cost of higher complexity and CPU cycles.
Any colour for each pixel can be set using the set colours above in bytes 3 and 4.

Byte[6] : Next set of pixels .....


It was getting late so I went to bed after this.
Last edited by 6502 on Mon Oct 29, 2018 6:41 pm, edited 4 times in total.

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

Re: Unused Citadel bits

Post by Diminished » Mon Oct 29, 2018 4:57 pm

Cool.

I'm struggling mentally a bit at the moment, but I had a look at those unknown tile[1] and tile[2] bytes in the asm, and updated my local copy of the reassembly accordingly.

NOTES
- t_tslots_unk2[], where tile[1] bit 4 ends up, only ever seems to be written and not read. Having seen the rest of the code it's hard to believe that MJ would waste twelve precious bytes leaving an unused table lying around so there's probably something else going on here. Note to self (or anyone else): try setting read watchpoints on this table and see what happens.
- tile[1] top bit (bit 7) writes to tile_3; tile_3 is then written to t_tslots_tileU_hi3[tile_slot]. This is then used while the tile is being plotted (applies to both branch A and branch B of blit_500_to_vram). Looks like some sort of "post" colour modifier.
- tile[1] bits 5&6 shifted to 6&7, placed in tile_4; looks like a "pre" colour modifier, applied before the tile is unpacked into the graphics buffer at &500?
- tile[2] low nybble goes into t_tslots_tile2_lo4[tile_slot] (now t_tslots_animsub_a in my local copy); this chooses which animation "A-type" subroutine the sprite should use.
- tile[2] high nybble picks an animation speed, deciding which entry from T40D1_BY_TDATA_2_HI (now T_ANIM_SPEEDS_MASTER in my local copy) should be chosen for the animation; subsequently copied into t_tslots_t40d1_vals (now t_anims_init_ticks).

In short, I still don't know about tile[1]. tile[2] appears to be entirely animation related.
Last edited by Diminished on Mon Oct 29, 2018 4:58 pm, edited 1 time in total.

6502
Posts: 21
Joined: Sat Mar 17, 2018 1:04 pm
Location: London
Contact:

Re: Unused Citadel bits

Post by 6502 » Mon Oct 29, 2018 8:46 pm

Diminished wrote:
Mon Oct 29, 2018 4:57 pm
- tile[1] top bit (bit 7) writes to tile_3; tile_3 is then written to t_tslots_tileU_hi3[tile_slot]. This is then used while the tile is being plotted (applies to both branch A and branch B of blit_500_to_vram). Looks like some sort of "post" colour modifier.
Yes, correct it is a post modifier. Setting this bit to 1 puts a grid of flashing colours on the tile, which is detected in the game as an energy sucking anniversary. These flashing colours can be seen by deleting the VDU 19 lines 360 to 430 in the jezebel file. Prepare yourself for a psychedelic light show. (You know this already, but it's for anyone else reading.)

To prove it I changed Byte[1] bit 7 of the tentacle thing (tile 49) the main hall at 0DA6 from &80 to &00.
I could then walk through it harmlessly.


EDIT
Also just found out:-
Byte[1] bit 5 set to 1 allows the tile as climbable and also can be stood on, like the green pot in the main hall. I set bit 5 of &1141 to 0 and the pot was then unable to be climbed.

Byte[1] bit 6 set to 1 makes the tile solid like a wall. I set bit 6 of &1141 to 1 and I was unable to walk through the pot.

Byte[1] bit 4 appears to be set to 1 only in tile 39 the moving platform. I've done some experiments set and unsetting this bit, but nothing obvious happens differently.

Interestingly some tiles such as 54 (the mummy) has bits 5 and 6 set, making it climbable and solid, as well as bit 7 (enemy) set as well of course.


So to summarise:
Tile byte[1]:
Bit 7 - Energy sapping.
Bit 6 - Solid.
Bit 5 - Climbable.
Bit 4 - Not sure. Only Tile 39 uses it.
Last edited by 6502 on Tue Oct 30, 2018 6:41 am, edited 7 times in total.

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

Re: Unused Citadel bits

Post by Diminished » Mon Oct 29, 2018 9:55 pm

Good work.

Maybe [1] bit 4 really isn't used then. Good news if true. Eliminate the table, reclaim the RAM. I'll check it definitively in a emulator when I can be bothered.

6502
Posts: 21
Joined: Sat Mar 17, 2018 1:04 pm
Location: London
Contact:

Re: Unused Citadel bits

Post by 6502 » Mon Oct 29, 2018 10:50 pm

Diminished wrote:
Sat Dec 09, 2017 12:10 pm
Tile 005 at 0x990 in the BBC disc version. Encountered while re-implementing the game's tile and room unpackers in PHP.
item-5-full.png
Well looky what we have here....
6thcrystal.png
CITADEL WAS ORIGINALLY PLANNED TO HAVE A SIXTH CRYSTAL

That unused tile 005 dimished found at 0x990 is an unused crystal when rendered correctly. Crystals 1,2,3,4,5 are at 0x955, 0x978, 0x97e, 0x984, 0x98a and unused number 6 at 0x990.

Here's how I found and retrieved this lost treasure, buried all these years:

Code: Select all

Tile:0 Len:35 Pos:0x955 - b'00000695000000c4f5f50031000000c4f5f5f5f5f5f50031c488f5f6f5f5f5f93122'
Tile:1 Len:6 Pos:0x978  - b'00000e9f23'
Tile:2 Len:6 Pos:0x97e  - b'00001a9729'
Tile:3 Len:6 Pos:0x984 - b'00001a8b2f'
Tile:4 Len:6 Pos:0x98a  - b'00000a8f35'
Tile:5 Len:6 Pos:0x990  - b'8000ae6f3b'
Tile:6 Len:12 Pos:0x996  - b'60006481d7c3c3c3ebc880'
Tile:7 Len:29 Pos:0x9a2 - b'00008b59c00c77771001f77b555a5556b77755a56666bb775a556655'
etc......
Here's a dump of the first 8 tiles in the game I done automatically on a quick python script.
Tile 0 (crystal 1) is the first crystal, which as you can see, is long because it has all the data required to plot the shape of the crystal.
All the crystals are the same size and shape, only the colour changes, so why waste precious bytes plotting the size and shape again?
Tile 1 (crystal 2) is set to different colours, but sets byte 4 bits 0&1 to 1. This informs the tile processor that the next byte (byte5) is the offset back to tile 0.
Tile 1 offset is 0x23 which is 35 in decimal.
Tile 2 offset is 0x29 which is 41 in decimal.
....
Tile 5 offset is 0x3b which is 59 in decimal, the lost crystal, points back to tile 0.


The reason dimished couldn't render it properly was because the width of the crystal is incorrect (which I corrected). Also, oddly byte1 bit 7 is set which is the energy enemy deplete flag. Probably a change of encoding during development after the crystal was abandoned.

Edit: As you can see the crystal was suppose to be yellow and magenta, but has rendered with the magenta below where it's suppose to be black, another change of encoding during development I guess, or perhaps they all looked like that originally. Who knows.......
Last edited by 6502 on Mon Oct 29, 2018 10:57 pm, edited 7 times in total.

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

Re: Unused Citadel bits

Post by Diminished » Tue Oct 30, 2018 1:33 pm

Nice. That explains why it looked like gibberish.

I'm wondering if originally this was supposed to be something to do with the crystals being presented at the Sanctuary. Whether they were supposed to sit in some sort of receptacle, and then MJ decided it wouldn't work for whatever reason. Or perhaps (given that the deplete flag is set) there was meant to be a puzzle where an enemy had to be defeated somehow in order to take its crystal.

I feel like I missed a trick by disassembling the retail version rather than the Citadel-early build, because logically there are more likely to be artifacts in that version.

IIRC, the crystal graphics also turned up in MJ's previous game, The Pyramid. That one would also be worth disassembling, just to see how much code is shared between the two.

6502
Posts: 21
Joined: Sat Mar 17, 2018 1:04 pm
Location: London
Contact:

Re: Unused Citadel bits

Post by 6502 » Tue Oct 30, 2018 11:36 pm

Done some more testing on bytes 1 & 2 in the tile record tonight and here's what I've come up with:

Byte[1]:
Bit 0 - Tile on horizontal or vertical set movement path flag. Such as rope climbers.
Bit 1 - Tile on diagonal irregular set movement path flag. Such as flying sparklies.
Bit 2 - Tile chase flag. Such as monks.
Bit 3 - Suspect not used. Only some wall tiles (81,88) have this flag set, for no obvious reason.
Bit 4 - Suspect not used. Only tile 39 has this set for no obvious reason.
Bit 5 - Tile is climbable flag. Such as pots.
Bit 6 - Tile is solid flag. Such as walls.
Bit 7 - Tile drains energy if touched flag. Such as spikes.

Byte[2]:
Bits 0,1,2,3 - Seems to selects which frames are displayed during animation sequences.
Bits 4,5,6,7 - Speed of tile flipping in animations. 0 fastest, 16 slowest.

Of course don't take all this as gospel.
Last edited by 6502 on Tue Oct 30, 2018 11:47 pm, edited 1 time in total.

User avatar
streaks
Posts: 262
Joined: Thu Oct 13, 2005 2:08 pm
Contact:

Re: Unused Citadel bits

Post by streaks » Thu Nov 01, 2018 8:02 am

Only the platform tile has byte[1] bit 4 set? I thought you meant those two-colour platforms and I thought it accounted for that mysterious splash effect. Moving platforms? Maybe it's to make it respond to levers..? Or like... maybe it means, since they respond to levers, fewer things can be taken for granted in terms of static/mobile logic and certain hard-to-define shortcuts can't be applied to it..? Or maybe like.. it means use the regular movement logic bits to define the movement, but this bit indicates that there's more involved than just HOW it moves (also WHEN it moves)... I dunno.

The mystery crystal tile... I suspect that it was a prototype tile used while developing and tweaking the tile logic, and wasn't even nessecarily intended to be a crystal. It could just be that the five crystals (that don't actually look like crystals) adopted the shape of that test sprite because the shape was pleasing or had aquired sentiment. I've done things like that many times. Since it's deadly and garbled I suggest it was just a dev guinea pig tile that was kept as a reference or testing-ground for bitplay.
Last edited by Rich Talbot-Watkins on Thu Nov 01, 2018 10:36 am, edited 1 time in total.
Reason: Corrected formatting
streaksy (at) gmail (dot) com

User avatar
streaks
Posts: 262
Joined: Thu Oct 13, 2005 2:08 pm
Contact:

Re: Unused Citadel bits

Post by streaks » Thu Nov 01, 2018 8:06 am

Speaking of that splash effect... have you tried rendering that other mystery sprite that you thought was probably a discarded splash effect, but with different width? A new, more obvious shape/pattern might emerge like with the crystal..?
streaksy (at) gmail (dot) com

Post Reply