SECTION APPLEII,CODE EVEN ** NOTE ** Disk image is extraced from "Src_Disk_Buffer" (either buffer1 or buffer2)!!! ************************************** * * CompressDisk: * This function does an entire Decoding & Compression of a disk image, * using the "Dalton Disk Disintegrator 2.0" algorithm. (see DDD, $1b41-$1b86, $1bc6...) * * Inputs: Floppy Image in disk_Buffer memory * A0 -> Destination Buffer (at least 145,000 bytes to be safe) * * Output: Compressed Disk Image (DDD) in Destination Buffer w/ simple ProDos header * D0 = FileSize; Total length of compressedData (including header) (0 if failed) * CompressDisk: movem.l a2-a6/d2-d5,-(sp) move.l a0,a5 ;A5-> Base of Dest Buffer! moveq.l #0,d5 ;D5 = Dest buffer bit offset! lea ReverseByteTbl,a6 ;a6-> Reverse byte lookup tbl move.l #$00000000,(a5) ;Make blank 4 byte Dos3.3 header add.l #32,d5 move.l #0,.TrackNum .loop move.l .TrackNum,d0 ;trk num <----- main loop lea .TrackData,a0 ;dest buffer jsr ReadTrack tst d0 beq .Error lea .TrackData,a0 ;track data lea .FreqTable,a1 ;dest for Frequency Count Tbl jsr FrequencyCount tst.l .TrackNum bne .Not0 .Trk0 moveq.b #0,d0 ;val Encode DDD header (Three 0 bits) moveq.w #3,d1 ;# of bits bsr .WriteNBits moveq.l #0,d0 move.b SecVolumeNumB,d0 ;Encode Volume # of disk move.b (a6,d0.w),d0 ;ReverseByte & add 8 bits at once... bfins d0,(a5){d5:8} addq.l #8,d5 .Not0 lea .FreqTable,a0 ;freq count table lea .Top20List,a1 ;top 20 values list destination bsr Top20Search lea .Top20List,a2 move.w #19,d2 ;Encode the 20 top values... moveq.l #0,d0 .Encd20 move.b (a2)+,d0 move.b (a6,d0.w),d0 ;ReverseByte & add 8 bits at once... bfins d0,(a5){d5:8} addq.l #8,d5 dbf d2,.Encd20 ;------------------- .CompressTrackData ;The real compression algorithm, using top 20 values lea .TrackData,a2 ;A2 -> TrackData (4096 bytes) moveq.w #0,d2 ;D2 = main index into track data .CompLp move.b (a2,d2.w),d0 ;read 1st val move.w d2,d3 ;d3 = local index. How many times does D0 repeat? .Count cmp.b (a2,d3.w),d0 ;1st check against self to maintain bounds check & get >=1 bne.b 5$ addq.w #1,d3 cmp.w #4095,d3 ;still in range? bls.b .Count 5$ sub.w d2,d3 ;d3 = # of times byte D0 is there (at least 1)! cmp.w #4,d3 blo.b .Single .Multiple ;------- value D0 repeats D3 times... (D3 >= #4) cmp.w #$100,d3 bls.b 10$ move.w #$100,d3 ;limit of $100 repeat values (written as #$00) 10$ move.b d0,d1 ;save value temporarily... move.w #$97,d0 ;Encode code for "Repeat" move.b (a6,d0.w),d0 ;ReverseByte & add 8 bits at once... bfins d0,(a5){d5:8} addq.l #8,d5 move.b d1,d0 ;Encode data value move.b (a6,d0.w),d0 ;ReverseByte & add 8 bits at once... bfins d0,(a5){d5:8} addq.l #8,d5 move.b d3,d0 ;Encode # of times repeats move.b (a6,d0.w),d0 ;ReverseByte & add 8 bits at once... bfins d0,(a5){d5:8} addq.l #8,d5 add.w d3,d2 ;update main index into track data... bra 100$ .Single ;------- single value D0... move.w #19,d3 ;d3 = index to search Top20List with (19 -> 0) lea .Top20List,a0 ;a0 -> Top20List, UBYTES[20] 20$ cmp.b (a0,d3.w),d0 beq.b .SingleTop20 ;is D0 a top 20 value? dbf d3,20$ ;Not top20, so encode this byte by itself... move.b d0,-(sp) move.b #0,d0 moveq.b #1,d1 ;Encode "SingleByte" code bsr .WriteNBits moveq.l #0,d0 move.b (sp)+,d0 move.b (a6,d0.w),d0 ;ReverseByte & add 8 bits at once... bfins d0,(a5){d5:8} addq.l #8,d5 bra 99$ .SingleTop20 ;-- single value is a Top 20 value! UBYTE[d3.w] lea .Top20Codes,a0 move.b (a0,d3.w),d0 lea .Top20Lengths,a0 move.b (a0,d3.w),d1 bsr .WriteNBits ; bra 99$ (fall through!) 99$ addq.w #1,d2 100$ cmp.w #4095,d2 bls .CompLp ;Do all 4096 bytes of track data... ;--------------------- addq.l #1,.TrackNum cmp.l #34,.TrackNum ;Do all 35 tracks! bls .loop ;Disk image compressed, now report len in bytes ;D5 = Length in bits... addq.l #7,d5 ;we want any partial bytes... divu.l #8,d5 ;d5 = Length in bytes! move.l d5,d0 ;lenth of compression! movem.l (sp)+,a2-a6/d2-d5 rts .Error movem.l (sp)+,a2-a6/d2-d5 moveq.l #0,d0 rts .WriteNBits: ;Enter: d0 = Data to write (low order bits), d1 = # of bits to write ; a5 -> Base of destination memory, d5 = Offset in BITS... ;Output: Memory written, updated Offset (d5) ; (see DDD $1d28 - $1d54) subq.b #1,d1 and.w #$000f,d1 .lp bfins d0,(a5){d5:1} ;place low order bit in memory lsr.l d0 addq.l #1,d5 ;1 at a time until done... dbf d1,.lp rts CNOP 0,4 .Top20Codes dc.b $03,$09,$1f,$0f,$07,$1b,$0b,$0d,$15,$37 ;(see DDD $1d55-$1d7c) dc.b $3d,$25,$05,$b1,$11,$21,$01,$57,$5d,$1d .Top20Lengths dc.b $04,$04,$05,$05,$05,$05,$05,$05,$05,$06 dc.b $06,$06,$06,$06,$06,$06,$06,$07,$07,$07 .TrackNum ds.l 1 .TrackData ds.b 4096 .FreqTable ds.w 256 .Top20List ds.b 20 even ************************************** * * PlainDiskImage_Save: * This function takes a disk image in ram, and saved it to disk in the "plain image" * format, which is the normalilzed track/sector data (143,360 bytes) in order. * * Inputs: Floppy Image in disk_Buffer memory * A0 -> Destination Buffer (at least 143,360 bytes to be safe) * * Output: Plain Image Data in Destination Buffer w/ no headers/trailor/checksums * D0 = FileSize; Total length of data (should be 143,360) (0 if failed) * PlainDiskImage_Save: movem.l a2-a5/d2-d5,-(sp) move.l a0,a5 ;A5-> Base of Dest Buffer! moveq.l #0,d5 ;D5 = Dest buffer bit offset! move.l #0,.TrackNum .loop move.l .TrackNum,d0 ;trk num <----- main loop move.l d0,d1 mulu.w #4096,d1 lea (a5,d1.l),a0 ;A0 = dest buffer... jsr ReadTrack ;fix dest buffer tst d0 beq .Error addq.l #1,.TrackNum cmp.l #34,.TrackNum ;Do all 35 tracks! bls .loop ;Disk image compressed, now report len in bytes move.l #143360,d0 ;d0 = lenth of disk data movem.l (sp)+,a2-a5/d2-d5 rts .Error movem.l (sp)+,a2-a5/d2-d5 moveq.l #0,d0 rts CNOP 0,4 .TrackNum ds.l 1 .TrackData ds.b 4096 .FreqTable ds.w 256 .Top20List ds.b 20 EVEN ***************************************** * * ReadTrack: * This function will extract 1 track of normalized data from a disk image, * using Apple Dos's normal decoding algorithms. (uses ReadSector) * * Inputs: Floppy Image in disk_Buffer memory (normal) * D0 = Track number to extract from * A0 -> Destination for data (4096 bytes!) * * Output: 4096 bytes of sector data in dest. buffer * D0 = True (1) for Successful extraction * False (0) for ERROR * ReadTrack: movem.l a2/d2-d3,-(sp) cmp.w #34,d0 bhi.b .Error ;track out of range... move.l d0,d2 ;d2 = Track # moveq.l #0,d3 ;d3 = Sector # move.l a0,a2 ;a2 -> Ptr to destination buffer .loop move.l d2,d0 ;track num move.l d3,d1 ;sector num move.l a2,a0 ;dest buffer jsr ReadSector tst d0 ;did it work? beq.b .Error add.l #$100,a2 ;set dest to next page of mem addq.l #1,d3 ;inc sector cmp.b #15,d3 bls.b .loop ;read all 16 sectors! moveq.l #1,d0 movem.l (sp)+,a2/d2-d3 rts .Error moveq.l #0,d0 movem.l (sp)+,a2/d2-d3 rts ;---------------------------------------------------------------------------------------- ***************************************** * * ReadSector: * This function will extract 1 sector of normalized data from a disk image, * using Apple Dos's normal decoding algorithms. * * Inputs: Floppy Image in disk_Buffer memory (normal) * D0 = Track number (0 - 34) * D1 = Sector number (0 - 15) * A0 -> Destination for data (256 bytes) * * Output: 256 bytes of sector data in dest. buffer * D0 = True (1) for Successful extraction * False (0) for ERROR * ReadSector: movem.l a2-a5/d2-d4,-(sp) mulu.w #disk_TrackLen,d0 add.l Src_Disk_Buffer,d0 move.l d0,a2 ;a2 -> Start of raw track data move.l a2,a3 ;a3 -> CURRENT ptr to raw track data (a2 <= a3 <= a4) lea disk_TrackLen-1(a2),a4 ;a4 -> Last byte of raw track data move.l d1,d4 ;d4 = Sector # we want... move.l #disk_TrackLen+500,d3 ;d3 = Max "GetNext" searches allowed! bra.b .addr .NxtSec lea 345(a3),a3 cmp.l a4,a3 ;make sure addr still in range bls.b .addr sub.l #disk_TrackLen,a3 ;if not, reset it .addr jsr .GetNext cmp.b #$D5,d0 ;Search for D5 AA 96 address header bne .addr jsr .GetNext cmp.b #$AA,d0 bne .addr jsr .GetNext cmp.b #$96,d0 bne .addr ;***** Extract 4X4 address data ***** jsr .Get4X4 ;Volume Number move.b d0,SecVolumeNumB jsr .Get4X4 ;Track Number jsr .Get4X4 ;HARD sector num... cmp.b #$0f,d0 bhi.b .addr ;outside legal range? Keep searching... move.b (.HardToSoft.l,d0.w),d0 ;d0 = SOFT sector num... cmp.b d0,d4 bne .NxtSec ;doh! Not the right sector! jsr .Get4X4 ;Checksum (ignore for now) jsr .GetNext ;better get DE AA address epilog cmp.b #$DE,d0 bne .addr jsr .GetNext cmp.b #$AA,d0 bne .addr .data jsr .GetNext cmp.b #$D5,d0 ;Wait for D5 AA AD data header bne .data jsr .GetNext cmp.b #$AA,d0 bne .data jsr .GetNext cmp.b #$AD,d0 bne .data moveq.w #$55,d1 ;d1 = cntr... lea .NibbleHunk56+$56,a1 ;a1 -> destination moveq.l #0,d4 ;d4 == ACC == running EOR checksum lea .DecodeTable-$96,a5 ;a5 -> DecodeTable (-$96) moveq.l #0,d0 ;See $b8ff -> $b911 in DOS 3.3 .loop jsr .GetNext ;LDY $c0ec move.b (a5,d0.w),d0 ;eor $ba00,y eor.b d0,d4 ; ... move.b d4,-(a1) ;sta $bc00,y (y decrements) dbf d1,.loop move.w #$ff,d1 ;d1 = cntr lea .NibbleHunk100,a1 ;a1 -> dest .loop2 jsr .GetNext ;See $b915 -> $b923 move.b (a5,d0.w),d0 ;eor $ba00,y eor.b d0,d4 ; move.b d4,(a1)+ ;sta $bb00,y (y inc's) dbf d1,.loop2 .cksum jsr .GetNext ;See $b925 (checksum) cmp.b (a5,d0.w),d4 ;cmp $ba00,y bne .Error jsr .GetNext ;better get DE AA data epilog cmp.b #$DE,d0 bne .Error jsr .GetNext cmp.b #$AA,d0 bne .Error * DeNibble-ize: (see dos 3.3 code at $b800) * Takes 342 bytes of "6 & 2" disk encoded crap as returns 256 bytes of normal data. * * Nib1: Nib2: Dest: * $ff-+- o $ff-+- o * | ^ $55-+- vvo | ^ * | ^ | vvv | ^ * | ^ | vvv | ^ * $00-+- o $00-+- ovv $00-+- o * * Inputs: a0 = destination for 256 bytes of data * NibbleHunk100 & 56 = Contain nibble data ;at this point, all nibble data is read...! ;Now, DE-NIBBLIZE !!! (dos 3.3 $b8c2 - $b8db) ; d1 = YReg, d2 = XReg, d3 = ACC lea .NibbleHunk100,a2 ;a2-> NibbleHunk1[$00] ($100) lea .NibbleHunk56,a3 ;a3-> NibbleHunk2[$00] ($56) move.l a0,a4 ;a4-> Dest buffer (256 bytes) move.w #$00ff,d1 ;d1 = Byte Countdown .DeNibl .b8c4 move.w #$56,d2 ;LDX #$56 .b8c6 subq.b #1,d2 ;DEX bmi.b .b8c4 ;BMI $b8c4 move.b (a2)+,d3 ;LDA $bb00,Y move.b (a3,d2.w),d0 lsr.b d0 ;LSR $bc00,X roxl.b d3 ;ROL lsr.b d0 ;LSR $bc00,X roxl.b d3 ;ROL move.b d0,(a3,d2.w) move.b d3,(a4)+ ;store another byte of normal data! dbf d1,.b8c6 movem.l (sp)+,a2-a5/d2-d4 move.l #1,d0 ;success... rts .GetNext: ;a2 -> Start track data, a3 -> Current track data ;a4 -> Max track data ptr, d3=remaining tries ; Return: d0 = byte move.b (a3)+,d0 cmp.l a4,a3 ;make sure addr still in range bls.b .ok move.l a2,a3 ;if not, reset it .ok subq.l #1,d3 bmi .GNErr rts .GNErr move.l (sp)+,d0 ;Exceeded # of tries, POP STACK, and fall to error... .Error moveq.l #0,d0 ;Failed (d0 = False).... movem.l (sp)+,a2-a5/d2-d4 rts .Get4X4 ;a2 -> Start track data, a3 -> Current track data ;a4 -> Max track data ptr ;Return: d0 = byte moveq.l #0,d0 move.b (a3)+,d0 cmp.l a4,a3 ;make sure addr still in range bls.b .ok3 move.l a2,a3 ;if not, reset it .ok3 lsl.b d0 or.b #$01,d0 and.b (a3)+,d0 cmp.l a4,a3 ;make sure addr still in range bls.b .ok4 move.l a2,a3 ;if not, reset it .ok4 rts .HardToSoft dc.b $0,$7,$e,$6,$d,$5,$c,$4,$b,$3,$a,$2,$9,$1,$8,$f ;The lookup table used as $ba00,y ALWAYS had Y >= $96, ;so here is the pertinant portion of the table ($ba96-$baff) ;Make sure to subtract $96 when getting the address of it. .DecodeTable: dc.b $00,$01,$98,$99,$02,$03,$9C,$04,$05,$06,$A0,$A1,$A2,$A3,$A4 dc.b $A5,$07,$08,$A8,$A9,$AA,$09,$0A,$0B,$0C,$0D,$B0,$B1,$0E,$0F dc.b $10,$11,$12,$13,$B8,$14,$15,$16,$17,$18,$19,$1A,$C0,$C1,$C2 dc.b $C3,$C4,$C5,$C6,$C7,$C8,$C9,$CA,$1B,$CC,$1C,$1D,$1E,$D0,$D1 dc.b $D2,$1F,$D4,$D5,$20,$21,$D8,$22,$23,$24,$25,$26,$27,$28,$E0 dc.b $E1,$E2,$E3,$E4,$29,$2A,$2B,$E8,$2C,$2D,$2E,$2F,$30,$31,$32 dc.b $F0,$F1,$33,$34,$35,$36,$37,$38,$F8,$39,$3A,$3B,$3C,$3D,$3E,$3F .NibbleHunk100 ds.b $100 ;342 bytes of storage space... .NibbleHunk56 ds.b $56 .NibblePad dc.b $ff,$ff,$ff,$ff SecVolumeNumB ds.b 1 ;--------------------------------------------------------------------------------------- EVEN ***************************************** * * FrequencyCount: (see DDD $1bc6 - $1c2b) * This function will scan through 4096 bytes of data, counting occurances * of each byte value and will create a list of UWORDS[256]. * As in DDD, any Repeating value of 4 or more will be skipped. * * Inputs: A0 -> 4096 bytes of data to analyze * A1 -> Destination for UWORDS[256] result (512 bytes!) * * Output: list of UWORDS[256] frequency count in dest. buffer * FrequencyCount: ;---- Produce list of most popular bytes... movem.l a2-a5/d2-d5,-(sp) move.l a0,a2 ;a2 -> 4096 bytes of data move.l a1,a3 ;a3 -> Destination table of UWORDS[256] move.w #255,d0 .ClrLp move.w #0,(a1,d0.w*2) ;clear dest table dbf d0,.ClrLp move.w #0,d2 ;d2 = main index moveq.l #0,d0 .MnLoop move.b (a2,d2.w),d0 ;read 1st val move.w d2,d3 ;d3 = local index. How many times does D0 repeat? .repeat cmp.b (a2,d3.w),d0 bne.b 5$ addq.w #1,d3 cmp.w #4095,d3 bls.b .repeat 5$ sub.w d2,d3 ;d3 = # of times byte D0 is there (at least 1)! cmp.w #4,d3 bhs.b .NoCnt .Cnt addq.w #1,(a3,d0.w*2) move.w #1,d3 ;only inc 1 byte .NoCnt add.w d3,d2 cmp.w #4095,d2 bls.b .MnLoop movem.l (sp)+,a2-a5/d2-d5 rts ***************************************** * * Top20Search: (see DDD $1c2c - $1c75) * This function will scan through the frequency list UWORDS[256], create a list * of the top 20 values in UBYTE[20]. During each search, this function will examine * each entry, and if >= current high number, replace the current high number with it. * * Inputs: A0 -> Source of frequency count UWORDS[256] * A1 -> Destination for top 20 values UBYTES[20] (20 bytes!) * Output: list of most popular values UBYTES[20] in dest. buffer * Top20Search: ;---- Produce list of most popular bytes... movem.l a2-a5/d2-d5,-(sp) move.w #0,d1 ;D1 = value index # (0 -> 19) we are looking for .loop20 moveq.l #0,d0 ;D0 = max frequency number found so far... move.w #0,d2 ;D2 = search index (0 - 255) .search cmp.w (a0,d2.w*2),d0 bhi.b .nope move.w (a0,d2.w*2),d0 ;new max frequency number move.b d2,(a1,d1.w) ;temporarily (?) keep the number in the table... .nope addq.b #1,d2 ;search/compare all 256 values... bne.b .search move.b (a1,d1.w),d2 ;get that popular value... move.w #0,(a0,d2.w*2) ;and blank its entry out in the freq table... addq.b #1,d1 ;get 20 values total... cmp.b #19,d1 bls.b .loop20 movem.l (sp)+,a2-a5/d2-d5 rts