Please consider supporting The Cutting Room Floor on Patreon. Thanks for all your support!

Crystal Quest (Apple IIGS)

From The Cutting Room Floor
Jump to: navigation, search

Title Screen

Crystal Quest

Developer: Casady & Greene
Publisher: Casady & Greene
Platform: Apple IIGS
Released in US: June 1989


SourceIcon.png This game has uncompiled source code.
GraphicsIcon.png This game has unused graphics.


Alternate Title Graphics

Unused (MAIN.PIC.X) Used (MAIN.PIC)
Crystal Quest Apple IIGS Alt Title.png Crystal Quest (Apple IIGS)-title.png

Inside the floppy disk is a version of the title version with a different publisher logo, predating the used title screen by four days.

Uncompiled Source Code

Hmmm...
To do:
Get the Huffman Compressor extracted out of the disk somehow.

Scattered throughout many of the later blocks of data in the game's data is this code and executable for a Huffman compressor for the Apple IIGS, which can be found at Block 1442.

 LST OFF
 TR ON
 XC
 XC

 REL

 
 EXT LOADINPUTFILE,PARSEOUTPUT
 EXT WRITEOUTPUT,SWITCHES
 EXT INITEXE,KILLEXE
 EXT TEXT,ALLOCMEM,PRWORD

 ENT IFILENAME,OFILENAME
 ENT INPUTLENGTH,INPUTPOINTER
 ENT OUTPUTLENGTH,OUTPUTPOINTER

 DUM $60
 SCREENAD DS 4
 SCREENAD2 DS 4
 ENDADR DS 4
 BITCOUNT DS 1
 BITTEMP DS 1
 LO DS 3
 ZPAGEEND
 DEND

 HOFFSIZE = 20

*
* Load a file and pack it
*

MX %00
ENTRY
 JSR INITEXE ;Do the housekeeping for an EXE file
 BCS :OK ;Oops! Wrong shell!

 STZ HEADERENABLE
 LDA #SWITCHTABLE
 JSR SWITCHES ;Check for soft switches

 JSR LOADINPUTFILE ;Parse a filename and load it
 BCS :ERROR ;Bad input?

 LDA #APPENDSTRING ;If no filename then append this to input
                   ;file

 JSR PARSEOUTPUT ;Parse an output file.
 CLC
 LDA INPUTLENGTH ;Request input memory plus a 4K safety
 ADC #4000
 TAX
 LDA INPUTLENGTH+2
 ADC #^4000
 TAY
 LDA #$C000 ;Fixed memory
 JSR ALLOCMEM ;Allocate data memory
 BCS :ERROR
 STX OUTPUTPOINTER ;64K data
 STY OUTPUTPOINTER+2
 JSR HUFFMAN ;Pack the data
 JSR WRITEOUTPUT ;Write the result
 BCS :ERROR
:OK JSR KILLEXE ;Kill all memory
 LDA #$0000
 CLC
 RTL

*
* Error occured!
*

:ERROR PHA
 JSR TEXT
 HEX 8D
 ASC "Usage : HUFFMAN Infile (.H) [Outfile]"8d
 ASC "[Switchs: -N No header -W Word header "
 ASC "(-L) Long word header]"8D00
 JSR KILLEXE ; Kill all memory
 PLA
 CMP #$1
 BLT :AA9
 PHA
 JSR PRWORD
 PLA
:AA9 RTL ;Return error

APPENDSTRING STR '.H'

SWITCHTABLE ASC 'N'
 DA HEADERENABLE,$FFFF
 ASC 'W'
 DA HEADERENABLE,$0001
 ASC 'L'
 DA HEADERENABLE,$0000
 DB 0

*
* Do the Huffman/Repeat compaction
*

HUFFMAN SEP #$30
 JSR COUNTBITS ;Count the number of repeat bytes
 JSR SQUISHEM ;Compress the data
 LDA BITCOUNT
 CMP #7
 BEQ :P
 LDA #0
 LDX #8
 JSR PUTBITS
:P REP #$30
 SEC
 LDA PUTBYTE+1 ;Get the length of my data
 SBC OUTPUTPOINTER
 STA OUTPUTLENGTH
 LDA PUTBYTE+3 ;Get the length of my data
 AND #$00FF
 SBC OUTPUTPOINTER+2
 STA OUTPUTLENGTH+2
 RTS

*
* Create a byte count table and the Hoffman tree
*

COUNTBITS
 PHP
 REP #$30
 LDX #$3FE
 ]A STZ DATA,X ;Zero out the count
 DEX
 DEX
 BPL ]A

 LDA INPUTPOINTER ;Get the start address
 STA SCREENAD
 LDA INPUTPOINTER+2
 STA SCREENAD+2
 LDA INPUTLENGTH ;Get the length
 STA SCREENAD2
 LDA INPUTLENGTH+2
 STA SCREENAD2+2

]A LDA SCREENAD2 ;Done counting?
 ORA SCREENAD2+2
 BEQ :MAKEHOFF
 BIT SCREENAD2+2 ;High bit clear?
 BMI :MAKEHOFF
 LDA SCREENAD2 ;Dec count
 BNE :CC
 DEC SCREENAD2+2

:CC DEC SCREENAD2

 SEP #$30
 LDY #3
 LDA [SCREENAD]

 ]B CMP [SCREENAD],Y
 BNE :OK
 DEY
 BNE ]B

 INY
]B CMP [SCREENAD],Y
 BNE :ADDIT
 INY
 BNE ]B

LDA SCREENAD2+1 ;Sub $100 from length
 BNE :CS
 DEC SCREENAD2+2
:CS DEC SCREENAD2+1
 BRA :X100 ;Add $100 to address

:ADDIT TYA
 CLC
 ADC SCREENAD
 STA SCREENAD
 BCC :XS
:X100 INC SCREENAD+1
 BNE :XS
 INC SCREENAD+2
:XS STY :SELF+1 ;Save length
 REP #$30
 SEC
 LDA SCREENAD2
:SELF SBC #0
 STA SCREENAD2
 LDA SCREENAD2+2
 SBC #0
 STA SCREENAD2+2
 BRA ]A
:OK REP #$30
 AND #$FF ;Make word pointer
 ASL
 TAX
 INC SCREENAD ;Next address
 BNE :J
 INC SCREENAD+2
:J INC DATA,X ;Count up
 BNE ]A
 INC DATA+$200,X
 BRA ]A

:MAKEHOFF
 LDY #0 ;I need 20 bytes for the tree
]A LDX #0
 STZ SCREENAD2 ;Find the most used byte then work my
 STZ SCREENAD2+2 ;way down
]B LDA DATA,X ;Already used?
 AND DATA+$200,X
 CMP #$FFFF
 BEQ :SKIP ;Yep, skip
 LDA DATA,X
 CMP SCREENAD2 ;Bigger?
 LDA DATA+$200,X
 SBC SCREENAD2+2
 BLT :SKIP ;Nope
 STX :VALUE ;Save Byte responsible
 LDA DATA,X ;Save new byte count
 STA SCREENAD2
 LDA DATA+$200,X
 STA SCREENAD2+2
:SKIP INX
 INX
 CPX #$200
 BLT ]B

 LDA :VALUE ;Save this value into the Hoffman table
 LSR
 SEP #$20
 STA HOFFTABLE,Y
 REP #$20
 LDX :VALUE
 LDA #$FFFF ;Delete this byte from the count table
 STA DATA,X
 STA DATA+$200,X
 INY
 CPY #HOFFSIZE ;Table full?
 BLT ]A
 PLP
 RTS

:VALUE DS 2

*
* Save packed data
*

SQUISHEM
 PHP
 SEP #$30

 CLC
 LDA INPUTPOINTER
 STA SCREENAD
 ADC INPUTLENGTH
 STA ENDADR
 LDA INPUTPOINTER+1
 STA SCREENAD+1 ;Grab bytes here!
 ADC INPUTLENGTH+1
 STA ENDADR+1
 LDA INPUTPOINTER+2
 STA SCREENAD+2
 ADC INPUTLENGTH+2
 STA ENDADR+2

 LDA OUTPUTPOINTER
 STA PUTBYTE+1
 LDA OUTPUTPOINTER+1
 STA PUTBYTE+2 ;Save bytes here!
 LDA OUTPUTPOINTER+2
 STA PUTBYTE+3
 STZ BITTEMP ;Init the Bit slice routine
 LDA #7
 STA BITCOUNT

 LDA HEADERENABLE
 BMI :NOHEADER
 LDA INPUTLENGTH
 LDX #8
 JSR PUTBITS
 LDA INPUTLENGTH+1
 LDX #8
 JSR PUTBITS
 LDA HEADERENABLE
 BNE :NOHEADER
 LDA INPUTLENGTH+2
 LDX #8
 JSR PUTBITS
 LDA INPUTLENGTH+3
 LDX #8
 JSR PUTBITS

:NOHEADER LDY #0
]A LDA HOFFTABLE,Y
 LDX #8 ;Send the entire Hoffman table (8 Bits)
 JSR PUTBITS
 INY
 CPY #HOFFSIZE
 BLT ]A

]A
 LDX #HOFFSIZE-1
 LDA [SCREENAD]
]B CMP HOFFTABLE,X
 BEQ :GOTIT
 DEX
 BPL ]B
 BMI :MIN
:GOTIT LDA HOFFREPEAT,X
 HEX 2C
:MIN LDA #3
 STA LO
 LDY #0
 LDA [SCREENAD],Y ;Repeater?
]B INY
 CMP [SCREENAD],Y
 BNE :ASIS
 CPY LO
 BLT ]B
 CLC
 LDA SCREENAD ;Check if out of bounds
 ADC LO
 STA SCREENAD2
 LDA SCREENAD+1
 ADC #0
 STA SCREENAD2+1
 LDA SCREENAD+2
 ADC #0
 STA SCREENAD2+2
 LDA SCREENAD2
 CMP ENDADR
 LDA SCREENAD2+1
 SBC ENDADR+1
 LDA SCREENAD2+2
 SBC ENDADR+2
 BGE :ASIS ;Too far
 JSR REPEATER
 BRA :CHK

:ASIS LDY #HOFFSIZE-1
 LDA [SCREENAD] ;Check if byte can be Hoffman'd
]X CMP HOFFTABLE,Y
 BEQ :TINY ;Yep
 DEY
 BPL ]X
 LDA #%0 ;Save header bit
 LDX #1 ;000 Means 8 bit value
 JSR PUTBITS
 LDA [SCREENAD]
 LDX #8
 JSR PUTBITS ;Save byte
 JMP :NEXT

:TINY
 LDX HOFFLENGTH,Y ;Save Hoffman Skew factor
 LDA HOFFCODEH,Y
 JSR PUTBITS
:NEXT INC SCREENAD ;Move to the next byte.
 BNE :CHK
 INC SCREENAD+1
 BNE :CHK
 INC SCREENAD+2
:CHK LDA SCREENAD ;Done Yet?
 CMP ENDADR
 LDA SCREENAD+1
 SBC ENDADR+1
 LDA SCREENAD+2
 SBC ENDADR+2
 BGE :PP
 JMP ]A
:PP PLP
 RTS ;End

REPEATER
 LDA [SCREENAD]
 STA :BYTE
 LDY #1
]A CMP [SCREENAD],Y
 BNE :GOTLEN
 INY
 BNE ]A
:GOTLEN TYA ;256 bytes?
 BEQ :100
 CLC
 ADC SCREENAD
 STA SCREENAD ;Move the pointer
 BCC :N100
:100 INC SCREENAD+1
 BNE :N100
 INC SCREENAD+2
:N100 SEC
 LDA SCREENAD
 SBC ENDADR
 STA SCREENAD2
 LDA SCREENAD+1
 SBC ENDADR+1
 STA SCREENAD2+1
 LDA SCREENAD+2
 SBC ENDADR+2
 STA SCREENAD2+2
 BLT :FINE
; SEC
 TYA
 SBC SCREENAD2
 TAY ;Use this length
:FINE
 LDX #7
 LDA #%1111111
 JSR PUTBITS
 TYA ;Save the count
 LDX #8
 JSR PUTBITS
 LDA :BYTE
 LDX #8
 JMP PUTBITS

:BYTE DS 1

HOFFLENGTH
 DFB 4,4
 DFB 5,5,5,5,5,5,5
 DFB 6,6,6,6,6,6,6,6
 DFB 7,7,7

HOFFREPEAT DFB 6,6
 DFB 5,5,5,5,5,5,5
 DFB 4,4,4,4,4,4,4,4
 DFB 4,4,4

HOFFCODEH
 DFB %1000,%1001
 DFB %10100,%10101,%10110,%10111,%11000,%11001,%11010
 DFB %110110,%110111,%111000,%111001,%111010,%111011
 DFB %111100,%111101

 DFB %1111100,%1111101,%1111110

HOFFTABLE DS HOFFSIZE

*
* Save "X" amount of bits
*

PUTBITS
 STX :COUNT
]A CPX #8
 BEQ :OK
 ASL
 INX
 BNE ]A

:OK ASL ;Shave a bit
 ROL BITTEMP ;Save it
 DEC BITCOUNT ;Bit bucket full?
 BPL :SLICE ;Nope
 PHA ;Save
 LDA BITTEMP ;Empty the bit bucket
 JSR PUTBYTE
 STZ BITTEMP ;Reset pointers
 LDA #7
 STA BITCOUNT
 PLA ;Restore
:SLICE DEC :COUNT ;Done yet?
 BNE :OK
 RTS

:COUNT DS 1

*
* Save a byte
*

PUTBYTE STAL $FFFFFF
 INC PUTBYTE+1
 BNE :A
 INC PUTBYTE+2
 BNE :A
 INC PUTBYTE+3
:A RTS

HEADERENABLE DS 2

DATA DS $400

IFILENAME DS 64
OFILENAME DS 64
INPUTLENGTH DS 4
INPUTPOINTER DS 4
OUTPUTPOINTER DS 4
OUTPUTLENGTH DS 4

SAV HUFFMAN.L