Date: Wed, 24 Apr 1996 08:34:47 -0700 To: tghack-list@cpac.washington.edu From: Benjamin Quinn X-Software: MLF v2.1, Copyright 1995, 1996 X-Original-Id: <199604241328.JAA04222@grex.cyberspace.org> Subject: CD disassembly (1 of 3) Okay guys, I've gotten all the files done for the disassembly posts. From the subject line, you've probably guessed that there will be 3 posts. Good, I haven't lost anyone yet. This post is background info, nothing too in depth. The second post will be the actual disassembled code, and finally I will attempt (!) to explain the picture file format in the last post. NOTES 1: BACKGROUND ON THE SYSTEM: - As previously mentioned on the tghack-list, the system is based on a 65C02 processor, which has a 16-bit address bus. To access more memory, it uses some sort of a segmented architecture. To rehash what is known: The processor uses one of 8 'memory-map' registers, based on what memory address is being resolved: $0000-$1FFF -> MMR0 $2000-$3FFF -> MMR1 $4000-$5FFF -> MMR2 $6000-$7FFF -> MMR3 $8000-$9FFF -> MMR4 $A000-$BFFF -> MMR5 $C000-$DFFF -> MMR6 $E000-$FFFF -> MMR7 These memory-map registers can hold as much information as the accumulator (8 bits), shown by the 'TAM0'/'TAM2'/etc. instructions referenced by the code fragment which appeared on the turbo-list a long time ago, reposted recently by Joe LoCicero. I found the original posts and noticed that the 'TAM' operations were actually two-byte instructions - and found that the second byte is some kind of a bit-field. But since the apparent convention is to show it as an 'implicit addressing mode' instruction, I assume that only one register can be referenced at once. These 'TAM' operations are shown on the listing as 'native' instructions. It is unclear as to how these MMR's _truly_ affect memory resolution, but it is clear they become the most-significant bits in the 'effective' address. Since all of the accumulator's 8 bits appear to be used in the setting of these registers, it can be assumed that they are significant to 8 bits. The only unclear part is whether the top three bits of the original address are actually used, or simply there to decode which MMR to use. So, the total addressing range of the CPU is somewhere between 21 and 24 bits (2 megabytes to 16 megabytes). 2: THE PROGRAM'S LOCATION IN MEMORY - The words 'frame' or 'sector' may be used interchangeably in this annotation to refer to a physical block (2048 bytes) on CD-ROM. In fact, the correct word should be 'frame'. - This listing shows the program on Frame #3 of track #1 (only track in use) - This frame loads into memory at $3000 (inferred from program logic) 3: THE PROGRAM'S USE OF ZERO-PAGE MEMORY - Zero-page locations (ie. $00) are separate and distinct from $0000 addresses. Zero-page is mapped to RAM, whereas $0000 addresses are mapped to another area of memory (by the 'memory paging unit' previously mentioned on the list), which is apparently a hardware-control interface area - Also for zero-page: low addresses ($00) are used for scratchpad values, whereas high ($FF) addresses are used for passing parameters to subroutines (by convention, at least) -- Ben Quinn || "Druid in training, || My homepage is benquinn@cyberspace.org || Logger by trade" || not functional. ------------------------------------------------------------------ Date: Wed, 24 Apr 1996 08:39:46 -0700 To: tghack-list@cpac.washington.edu From: Benjamin Quinn X-Software: MLF v2.1, Copyright 1995, 1996 X-Original-Id: <199604241332.JAA04518@grex.cyberspace.org> Subject: CD disassembly (2 of 3) ; Hawaiian Island Girls ; ; START OF MAIN SECTION OF PROGRAM: ; 3000: EA NOP 3001: EA NOP 3002: EA NOP 3003: EA NOP 3004: EA NOP 3005: 20 86 31 JSR $3186 ; initialization 3008: A9 01 LDA #$01 ; 'resettable' joypads = only joypad #1 has 300A: 8D 0A 31 STA $310A ; 'reset' privilege 300D: 20 33 30 JSR $3033 ; set first picture 3010: 20 95 30 JSR $3095 ; read picture 3013: 20 B9 30 JSR $30B9 ; display picture 3016: 20 10 31 JSR $3110 ; read joystick port 3019: AD FB 30 LDA $30FB ; controller #1 301C: 29 21 AND #$21 ; '->' or button 'I' (next pic) 301E: F0 05 BEQ $3025 3020: 20 49 30 JSR $3049 ; Increment picture counter 3023: 80 EB BRA $3010 ; read & display 3025: AD FB 30 LDA $30FB 3028: 29 82 AND #$82 ; '<-' or button 'II' (prev pic) 302A: F0 05 BEQ $3031 302C: 20 6C 30 JSR $306C ; decrement picture counter 302F: 80 DF BRA $3010 ; read & display 3031: 80 E3 BRA $3016 ; re-read joystick port ; ; Move first picture's starting frame address ; into 'current address' area - addresses are ; stored as 2-byte numbers, range $0-$FFFF ; (probably offset from beginning of track) ; 3033: AD FC 37 LDA $37FC 3036: 85 08 STA $08 3038: AD FD 37 LDA $37FD 303B: 85 09 STA $09 303D: 60 RTS ; ; Move last picture's starting frame address ; into 'current address' area ; 303E: AD FE 37 LDA $37FE 3041: 85 08 STA $08 3043: AD FF 37 LDA $37FF 3046: 85 09 STA $09 3048: 60 RTS ; ; Increment current picture number ; 3049: A5 08 LDA $08 304B: 18 CLC 304C: 69 14 ADC #$14 ; Add size of picture (20 frames) 304E: 85 08 STA $08 3050: A5 09 LDA $09 3052: 69 00 ADC #$00 ; catch carry from previous add 3054: 85 09 STA $09 3056: AD FF 37 LDA $37FF ; check if cycled past final picture 3059: C5 09 CMP $09 305B: 90 0B BCC $3068 305D: D0 0C BNE $306B 305F: AD FE 37 LDA $37FE 3062: C5 08 CMP $08 3064: 90 02 BCC $3068 3066: 80 03 BRA $306B 3068: 20 33 30 JSR $3033 ; reset to first picture (if cycled) 306B: 60 RTS ; ; Decrement current picture number ; 306C: A5 09 LDA $09 306E: CD FD 37 CMP $37FD ; check if already at 1st picture 3071: D0 09 BNE $307C 3073: A5 08 LDA $08 3075: CD FC 37 CMP $37FC 3078: D0 02 BNE $307C 307A: 80 0F BRA $308B 307C: A5 08 LDA $08 307E: 38 SEC 307F: E9 14 SBC #$14 ; subtract size of picture (20 frames) 3081: 85 08 STA $08 3083: A5 09 LDA $09 3085: E9 00 SBC #$00 3087: 85 09 STA $09 3089: 80 03 BRA $308E 308B: 20 3E 30 JSR $303E ; reset to final picture (if cycled) 308E: 60 RTS ; ; 'Reset' function - called when run+select hit ; 308F: A2 FF LDX #$FF 3091: 9A TXS ; set stack pointer 3092: 4C 00 30 JMP $3000 ; start program over ; ; Read picture from disk ; ; Reads $14 (20 decimal) frames from CD ; (Trouble is, 3 of these are not in use by the picture) ; ; 3095: A9 00 LDA #$00 ; not sure what this is (possibly MSB of 3097: 85 FC STA $FC ; 3-byte sequence) 3099: A5 09 LDA $09 ; frame # of block start (MSB of 2-byte) 309B: 85 FD STA $FD 309D: A5 08 LDA $08 ; frame # of block start (LSB) 309F: 85 FE STA $FE 30A1: A9 01 LDA #$01 ; possibly 'track #' for relative loc. 30A3: 85 FF STA $FF 30A5: A9 00 LDA #$00 ; buffer to load into -> $3800 30A7: 85 FA STA $FA 30A9: A9 38 LDA #$38 30AB: 85 FB STA $FB 30AD: A9 14 LDA #$14 ; size of block = $0014 (20) frames 30AF: 85 F8 STA $F8 30B1: A9 00 LDA #$00 30B3: 85 F9 STA $F9 30B5: 20 09 E0 JSR $E009 ; ROM call for CDROM read 30B8: 60 RTS ; ; Not 100% sure of all details, but displays picture ; ; important-looking addresses: ; $3DC0 - start of actual picture ; $3A00 - pallette 2 ; $3800 - pallette 1 (buffer start address) ; 30B9: 20 74 31 JSR $3174 30BC: A9 C0 LDA #$C0 30BE: 85 04 STA $04 30C0: A9 3D LDA #$3D 30C2: 85 05 STA $05 30C4: 20 28 32 JSR $3228 30C7: A9 00 LDA #$00 30C9: 85 06 STA $06 30CB: A9 3A LDA #$3A 30CD: 85 07 STA $07 30CF: 20 5B 32 JSR $325B 30D2: A9 00 LDA #$00 ; start of picture buffer 30D4: 85 00 STA $00 30D6: A9 38 LDA #$38 30D8: 85 01 STA $01 30DA: 20 00 32 JSR $3200 30DD: 60 RTS ; ; Countdown delay timer ; delays $110000 * accumulator in 'counts' ; or, about 5 million cycles @ 8(?) MHz ; (times accumulator value) ; ; This assumes 5 cycles for the INC, ; and 2 cycles for the branch (as listed ; in a 65C02 databook) ; 30DE: A8 TAY 30DF: A9 00 LDA #$00 30E1: 85 0A STA $0A 30E3: A9 00 LDA #$00 30E5: 85 0B STA $0B 30E7: A9 F5 LDA #$F5 30E9: 85 0C STA $0C 30EB: E6 0A INC $0A 30ED: D0 FC BNE $30EB 30EF: E6 0B INC $0B 30F1: D0 F8 BNE $30EB 30F3: E6 0C INC $0C 30F5: D0 F4 BNE $30EB 30F7: 88 DEY 30F8: D0 E5 BNE $30DF 30FA: 60 RTS ; ; Storage - joystick information area ; 30FB: 00 00 00 00 00 ; 5 joysticks - current poll 3100: 00 00 00 00 00 ; 5 joysticks - previous poll 3105: 00 00 00 00 00 ; 5 joystocks - current 'delta' 310A: 00 ; 'Important' joysticks - bit field 310B: 01 02 04 08 10 ; Joystick #'s to determine whether 'important' ; ; Poll joysticks ; ; Location $1000 appears to be a control read/write ; port which can only read 4 bits at a time; ; We knew that the keys in the joypad were multiplexed, ; but it comes as a surprise that the *program* is ; what toggles the 74LS157 multiplexer's line to read the ; pad twice. ; ; Also, real logic values are read into the port - the ; joystick's keys are default high, and are 'pulled' low ; when pressed. Therefore, if a '1' value is to mean ; 'pressed', the values read from the port must be ; inverted (programmer speak: 'complemented') ; ; Since the 'Y' register is used as a counter for the 5 ; joypads, I assume that the first instruction clears the 'Y' ; register ; 3110: C2 ??? ; Clear Y (?) 3111: A9 01 LDA #$01 3113: 8D 00 10 STA $1000 3116: A9 03 LDA #$03 ; reset to first joystick (?) 3118: 8D 00 10 STA $1000 311B: A9 01 LDA #$01 ; read first nybble 311D: 8D 00 10 STA $1000 3120: 48 PHA ; seems to be delay (9 cycles - slow logic ?) 3121: 68 PLA 3122: EA NOP 3123: B9 FB 30 LDA $30FB,Y ; move previous pad value to 'prev' area 3126: 99 00 31 STA $3100,Y 3129: AD 00 10 LDA $1000 ; Read joystick port (4 bits) 312C: 0A ASL ; shift them to upper nybble 312D: 0A ASL 312E: 0A ASL 312F: 0A ASL 3130: 99 FB 30 STA $30FB,Y ; store new value 3133: 9C 00 10 STZ $1000 ; toggle port (to read other 4 key values) 3136: 48 PHA ; delay (?) again 3137: 68 PLA 3138: EA NOP 3139: AD 00 10 LDA $1000 ; Read joystick port (other 4 bits) 313C: 29 0F AND #$0F ; clear unused bits 313E: 19 FB 30 ORA $30FB,Y ; merge 2 reads into 1 byte 3141: 49 FF EOR #$FF ; reset 'sense' of keys 3143: 99 FB 30 STA $30FB,Y ; store it 3146: 59 00 31 EOR $3100,Y ; check against previous value 3149: 39 FB 30 AND $30FB,Y 314C: 99 05 31 STA $3105,Y ; store 'new key pressed' key values 314F: C8 INY 3150: C0 05 CPY #$05 ; cycle for next of 5 joypads 3152: 90 C7 BCC $311B 3154: C2 ??? ; Clear Y (?) (restart cycle of 5 joypads) 3155: AD 0A 31 LDA $310A ; 'important' joystick(s) (reset allowed) 3158: 39 0B 31 AND $310B,Y 315B: F0 11 BEQ $316E ; not important enough to check 315D: B9 05 31 LDA $3105,Y 3160: C9 04 CMP #$04 ; 'select' key newly-pressed 3162: D0 0A BNE $316E 3164: B9 FB 30 LDA $30FB 3167: C9 0C CMP #$0C ; 'reset' pressed (run+select) 3169: D0 03 BNE $316E 316B: 4C 8F 30 JMP $308F ; reset action 316E: C8 INY ; check next joystick 316F: C0 05 CMP #$05 3171: 90 E2 BCC $3155 3173: 60 RTS ; ; Don't understand this part ; ; Probably some sort of hardware control ; 3174: 9C 02 04 STZ $0402 3177: 9C 03 04 STZ $0403 317A: A2 00 LDX #$00 317C: 9C 04 04 STZ $0404 317F: 9C 05 04 STZ $0405 3182: E8 INX 3183: D0 F7 BNE $317C 3185: 60 RTS ; ; Initialize ; ; Not sure what's going on with locations $0400-$0405 ; 3186: D4 ??? 3187: D8 CLD 3188: 78 SEI ; disable interrupts 3189: A9 FF LDA #$FF 318B: 53 01 TAM0 ; Xfer 'A' to MMR0 ($0000-$1FFF) 318D: A9 F8 LDA #$F8 318F: 53 02 TAM1 ; Xfer 'A' to MMR1 ($2000-$3FFF) 3191: A9 00 LDA #$00 3193: 53 80 TAM7 ; Xfer 'A' to MMR7 ($E000-$FFFF) 3195: 20 9B 31 JSR $319B 3198: 20 AD 31 JSR $31AD 319B: A9 00 LDA #$00 319D: 8D 00 04 STA $0400 ; reset some sort of hardware 31A0: 9C 02 04 STZ $0402 31A3: 9C 03 04 STZ $0403 31A6: 9C 04 04 STZ $0404 31A9: 9C 05 04 STZ $0405 31AC: 60 RTS ; ; Copy memory (hardware directives ?) ; to hardware-control area (?) ; ; Looks like Voodoo to me ; ; Since the 'X' register is used as an index ; in this subroutine, the first opcode could ; clear 'X' ; 31AD: 82 ??? 31AE: BD C8 31 LDA $31C8,X 31B1: 8D 00 00 STA $0000 31B4: E8 INX 31B5: BD C8 31 LDA $31C8,X 31B8: 8D 02 00 STA $0002 31BB: E8 INX 31BC: BD C8 31 LDA $31C8,X 31BF: 8D 03 00 STA $0003 31C2: E8 INX 31C3: E0 21 CPX #$21 31C5: 90 E7 BCC $31AE 31C7: 60 RTS ; ; Storage ; ; Used for hardware-control directives (?) ; 31C8: 05 80 00 31CB: 06 00 00 31CE: 07 00 00 31D1: 08 00 00 31D4: 09 00 00 31D7: 0A 02 02 31DA: 0B 1F 04 31DD: 0C 02 0F 31E0: 0D F1 00 31E3: 0E 04 00 31E6: 0F 10 00 31E9: 13 00 10 ; ; Endless loop ; ; Don't understand what the point is ; (setting hardware loc's ?) ; 31EC: AE 00 00 LDX $0000 31EF: 9C 02 04 STZ $0402 31F2: 9C 03 04 STZ $0403 31F5: 8A TXA 31F6: 8D 04 04 STA $0404 31F9: 9C 05 04 STZ $0405 31FC: E8 INX 31FD: 4C EC 31 JMP $31EC ; ; Copy 512 bytes to hardware unit ; ; inputs: zero page values $00/$01 = buffer start ; (only called with value #$3800) ; ; (seems to be the video processor; this is a pallette) ; 3200: 9C 02 04 STZ $0402 ; first, put a zero in hardware 3203: 9C 03 04 STZ $0403 ; both bytes 3206: A2 00 LDX #$00 3208: A0 00 LDY #$00 320A: B1 00 LDA ($00),Y ; copy 2 bytes into hardware loc. 320C: 8D 04 04 STA $0404 320F: C8 INY 3210: B1 00 LDA ($00),Y 3212: 8D 05 04 STA $0405 3215: E8 INX ; increment counter 3216: E0 00 CPX #$00 ; 256 pairs yet ? 3218: F0 0D BEQ $3227 321A: A5 00 LDA $00 ; add 2 to memory buffer pointer 321C: 18 CLC ; (2-byte value) 321D: 69 02 ADC #$02 321F: 85 00 STA $00 3221: 90 02 BCC $3225 3223: E6 01 INC $01 3225: 80 E1 BRA $3208 3227: 60 RTS ; ; Copy memory to hardware unit ; ; inputs: zero page values $04/$05 = buffer start ; (only called with value #$3DC0) ; ; (seems to also be video processor; this is the picture itself) ; ; Notice how 'segmented' architecture doesn't ; intrude - it seems that memory reg's 2-5 ; are all set to same region of memory ($F8xxxx) ; ; Probably also mem reg 6, but that is not needed ; in this routine (see loading from CDROM) ; 3228: A9 00 LDA #$00 322A: 8D 00 00 STA $0000 ; put a zero here 322D: A9 00 LDA #$00 322F: 8D 02 00 STA $0002 ; set $0002/$0003 = #$0400 3232: A9 04 LDA #$04 3234: 8D 03 00 STA $0003 3237: A9 02 LDA #$02 3239: 8D 00 00 STA $0000 323C: A0 00 LDY #$00 ; copy 2 bytes at a time 323E: B1 04 LDA ($04),Y ; to hardware unit 3240: 8D 02 00 STA $0002 3243: C8 INY 3244: B1 04 LDA ($04),Y 3246: 8D 03 00 STA $0003 3249: A5 04 LDA $04 ; add 2 to buffer pointer, and loop 324B: 18 CLC 324C: 69 02 ADC #$02 324E: 85 04 STA $04 3250: 90 02 BCC $3254 3252: E6 05 INC $05 3254: A5 05 LDA $05 ; if buffer pointer >=#$C000, stop 3256: C9 C0 CMP #$C0 3258: 90 E2 BCC $323C 325A: 60 RTS ; ; Copy memory to hardware unit ; ; inputs: zero page values $06/$07 = buffer start ; (only called with value #$3A00) ; ; (seems to also be video processor) ; (data is a pallette of a sort) ; ; 325B: A9 00 LDA #$00 325D: 8D 00 00 STA $0000 3260: A9 20 LDA #$20 ; put #$0020 into $0002/$0003 3263: 8D 02 00 STA $0002 3266: A9 00 LDA #$00 3268: 8D 03 00 STA $0003 326B: A9 02 LDA #$02 326D: 8D 00 00 STA $0000 326F: A9 40 LDA #$40 ; put #$0040 into $02/$03 3271: 85 02 STA $02 3273: A9 00 LDA #$00 3275: 85 03 STA $03 3277: A5 02 LDA $02 ; put counter LSB into $0002 3279: 8D 02 00 STA $0002 327C: A5 03 LDA $03 ; take MSB of counter, and 'OR' 327E: A0 00 LDY #$00 ; it with data at current pointer 3280: 11 06 ORA ($06),Y ; value 3282: 8D 03 00 STA $0003 3285: A5 02 LDA $02 ; inc counter 3287: 18 CLC 3288: 69 01 ADC #$01 328A: 85 02 STA $02 328C: 90 02 BCC $3290 328E: E6 03 INC $03 3290: A5 06 LDA $06 ; inc pointer value 3292: 18 CLC 3293: 69 01 ADC #$01 3294: 85 06 STA $06 3296: 90 02 BCC $329A 3298: E6 07 INC $07 329A: A5 03 LDA $03 ; loop until counter >=$0420 329C: C9 04 CMP #$04 329F: 90 D6 BCC $3277 32A1: A5 02 LDA $02 32A3: C9 20 CMP #$20 32A5: 90 D0 BCC $3277 32A7: 60 RTS ; ; Storage - don't know what yet (probably unused) ; 32A8: 00 00 04 00 20 00 24 00 32B0: 00 01 04 01 20 01 22 01 32B8: 07 00 38 00 3F 00 C0 01 32C0: C7 01 F8 01 B6 01 FF 01 32C8: 00 00 00 00 00 00 00 00 ; ; Then, a big batch of zeroes... ; 37FC: 03 00 ; first picture (address of first frame = $0003) 37FE: E3 10 ; final picture (address of first frame = $10E3) -- Ben Quinn || "Druid in training, || My homepage is benquinn@cyberspace.org || Logger by trade" || not functional. ---------------------------------------------------------------------- Date: Wed, 24 Apr 1996 08:35:17 -0700 To: tghack-list@cpac.washington.edu From: Benjamin Quinn X-Software: MLF v2.1, Copyright 1995, 1996 X-Original-Id: <199604241333.JAA04639@grex.cyberspace.org> Subject: CD disassembly (3 of 3) NOTES on the Video: I am not posting any of the pictures from the disc, and as such it will be difficult to explain the format of the pictures' data. But that's not going to stop me from trying. 1: Overall Structure - At least in this video mode, the screen is broken down into different 'blocks', like a character-based video terminal would be. I found this out by substituting an ISO-9660 CDROM while the picture disc was playing. It showed a bunch of random data organized as blocks. Each block was 8x8 pixels, and there were 32 blocks across the screen, and 28 down. - Based on the size of the picture data, I figured that each pixel was only being represented by 4 bits. The extra data is hidden in the 'indirection palette' (see below, Palette #2). 1: Palette #1 - This is a pallette consisting of 256 entries, each 16 bits long, stored in LSB-MSB order. Each data item has a maximum value of 9 bits. Through further research, I have found that the bit-structure is as follows: most sig -> least sig 8 7 6 5 4 3 2 1 0 \ / \ / \ / green red blue So, there are a maximum of 512 available colors, at least in the video mode available under this program. 2: Palette #2 - This is an 'indirection palette'; each 'block' points to a sub-palette within the global palette. So, 4 bits of data per pixel represents an offset from a base palette. - This palette is $3C0 bytes in length, and consists of bytes containing only 4-bit values. - This palette is set in a strange kind of way; each block is numbered from the top left. The top left block = 0, the next one to its side = 1, etc. The first block on the following row = $20 (32), and so on. The way that this palette is set is to logically 'OR' the 4-bit value of the 'indirection palette' together with the block number, and set the video hardware area. So, to set block # $0288 to sub-palette #6, the video hardware would be set with $6288. - In summary, there are 16 mini-palettes with 16 values each which make up the global palette. 3: Pixel Colors - Pretty straightforward: find out what 'block' the pixel is in, and get its 'indirection palette' value. Then, get the pixel value. Then, look in the global palette for the color. For example, say the block's indirection palette value is 4. Now, say the pixel's value is $A. So, the pixel's color is in the global palette at $4A. - In summary, you can display up to 512 colors; 256 simultaneously, but only 16 in any 'block'. 4: Picture (and Pixel) Structure - Basically, the blocks are filled from top-left, row-by-row until the bottom-right of the screen is hit. - Each 'block' is made up of 32 bytes - The structure of bytes (and bits) within each block is a bit tough to explain, but here goes: across (col) 0 1 2 3 4 5 6 7 A B C DOWN D (row) E F G H The pixels of column 0 are represented by the most significant bit of 4 different bytes. Column 7 pixels are made up of the least significant bit of 4 different bytes. The pixels in row A are represented by bytes 0, 1, 16, and 17. Likewise, row H is represented by bytes 14, 15, 30, and 31. (The 0-1 and 16-17 relationships reinforce that this is a 16-bit video processor). The pixel has a 4-bit value, which I will show as: "a b c d" Bit "a" is derived from the final byte of the 4 (ie. byte 17), and bit "d" is derived from the first byte of the 4 (ie. byte 0). In the case of pixel A-0, if the value is "1 0 1 1", we can say that byte 0's most significant bit is high, byte 1's most significant bit is high, byte 16's most significant bit is low, and byte 17's most significant bit is high. I hope that I've been able to explain this adequately in English. -- Ben Quinn || "Druid in training, || My homepage is benquinn@cyberspace.org || Logger by trade" || not functional.