; ----------------------------------------------------------- ; ; "Squeezed" files are generated by the program SQ.COM, ; and restored by the program USQ.COM (SIG/M xxx), both of ; which appear to have been compiled from BDS "C". The scheme ; is to generate a Huffman code, after a preliminary pass ; which replaces all characters repeated four or more times ; by a special code - <90H>. The squeezed file ; consists of a marker , checksum, name of the original ; file, decoding dictionary, and finally an encoded copy of ; the original file. ; ; The squeezed file terminates with the encoded version of an ; end marker (non-ASCII 100H) which forms part of the bit ; stream. The tail end of the original file, on past the ^Z, ; is encoded with the rest; the tail of the squeezed file is ; generally trash that is discarded after the [end] marker is ; encountered. Being both coded and part of the bit stream, ; its presence is not visible in a Hex dump of the squeezed ; file. ; ; TYSQ.ASM will read a squeezed file, typing it on the console. ; Executing TYSQ.COM with a null command line will produce a ; tutorial, describing its operation. ; ; TYSQ.ASM Copyright (C) 1984 ; Universidad Autonoma de Puebla ; July 1, 1984 ; ; [Harold V. McIntosh, 1 July 1984] ; ----------------------------------------------------------- BDOS equ 224 ;BDOS interrupt tfcb equ 5CH ;CP/M's FCB bufr equ 80H ;input buffer address ; Non-graphic characters CR equ 0DH ;carriage return LF equ 0AH ;line feed ; ------------- org 0100H ; ------------- begn: mov bx,(offset tfcb+1) ;CP/M's FCB cmp byte ptr [bx],' ' jnz notu mov dx,(offset logo) ;'ICUAP' logo call mssg ;send message to console jmp gbye ;return to CP/M notu: mov bx,(offset tfcb+10) ;CP/M's FCB cmp byte ptr [bx],'Q' jz yext mov dx,(offset nsqz) ;'not a squeezed file' call mssg ;send message to console jmp gbye ;return to CP/M yext: call opef ;open input file mov cx,0000 ;byte counter, kept in bx mov bctr,cx ;byte counter - input buffer mov smck,cx ;developing checksum mov roco,1 ;bit rotation counter ; Set up code table. Squeezed files seem to begin with the ; hexadecimal word (FF76) stored in Intel byte order, which ; would not be likely to start an unsqueezed file. cota: call rbyt ;fetch one byte from input stream cmp al,076H jnz cote ;mssg: 'not squeezed file' call rbyt ;fetch one byte from input stream cmp al,0FFH jz rchk ;read checksum cote: mov dx,(offset nsqz) ;'not a squeezed file' call mssg ;send message to console jmp gbye ;return to CP/M ; The "squeezed" marker is followed by a two-byte checksum, ; which is the simple sum of all the one-byte characters in ; the source file, carried as a two byte sum modulo 2**16. rchk: call rwor ;fetch two bytes from input stream mov cksm,bx ;given checksum ; Unsqueezed file name. It is an ASCII sequence, may be lower ; case if SQ.COM received it in response to a prompt, ending ; with a zero byte. Some trash may be present if SQ.COM wasn't ; used correctly. luup: call rbyt ;fetch one byte from input stream or al,al jz ldic ;load code dictionary call cona ;type character at console jmp luup ; Load code dictionary. It is preceded by its two-byte length, ; and consists of a series of pairs of two-byte addresses. For ; each bit in the code, select the first element (0) or the ; second (1) element of the pair. If the pair is positive, it ; is the table entry (code + 4*index) at which to continue with ; the next bit. If the pair is negative, it is the complement ; of the coded ASCII character (low order byte except for [end]). ldic: call rwor ;fetch two bytes from input stream mov cx,bx mov si,(offset code) ;decoding table looq: call rbyt ;fetch one byte from input stream mov [si],al inc si call rbyt ;fetch one byte from input stream mov [si],al inc si call rbyt ;fetch one byte from input stream mov [si],al inc si call rbyt ;fetch one byte from input stream mov [si],al inc si loop looq call crlf ;type CR,LF ; Start typing unsqueezed code. tusq: call dnch ;decode next character jnc G6 ! jmp chek ! G6: ;verify the checksum cmp al,090H ;repeat last character jnz tusu ;normal character call dnch ;decode next character dec al ;get count, adjust it mov ch,al ;set count aside tust: mov al,lach ;last character typed call tpye ;type char & add to checksum dec ch ;update count jnz tust ;repeat until exhausted jmp tusq ;type unsqueezed code tusu: mov lach,al ;last character typed call tpye ;type char & add to checksum jmp tusq ;type unsqueezed code ; Decode next character. dnch: mov bx,(offset code) ;decoding table dncr: call rbit ;read next bit jnc dncs ;skip for 1, stay for 0 inc bx inc bx dncs: mov ax,[bx] ;get next offset cmp ax,0FEFFH ;FEFF means [end] jz dnct or ax,ax jns dncu ;p means new offset not al ;m means complemented char stc cmc ret dnct: stc ;flag [end] with carry bit ret ; Calculate +4*. dncu: mov bx,(offset code) ;decoding table add ax,ax add ax,ax add bx,ax jmp dncr ; Check for interrupt request. ; Clear out interrupting character if present. ; Can't avoid CP/M echoing it when cleared, but at ; least it won't be waiting to prefix the command ; line after exit. break: mov cl,11 ;(0B) console status int BDOS ror al,1 jc G12 ! ret ! G12: mov dx,(offset dint) ;'Dump interrupted.' call mssg ;send message to console mov cl,1 ;(01) read console int BDOS jmp gbye ;return to CP/M crlf: mov al,CR call cona ;type character at console mov al,LF jmp cona ;type character at console ; Type unsqueezed text and accumulate checksum. ; Don't send ^Z's to the console. tpye: push bx push dx push cx mov ch,al mov bx,smck ;developing checksum add al,bl mov bl,al mov al,bh adc al,0 mov bh,al mov smck,bx ;developing checksum mov al,ch cmp al,1AH jz G13 ! call cona ! G13: ;type character at console call break ;check for interrupt request pop cx pop dx pop bx ret ; Send character in accumulator to console. cona: push bx push dx push cx mov cl,2 ;(02) char to console mov dl,al int BDOS pop cx pop dx pop bx ret mssg: mov cl,9 ;(09) print buffer int BDOS ret ; Verify the checksum. chek: mov bx,cksm ;given checksum cmp bx,smck ;developing checksum jnz chen ;checksum failed jmp gbye ;return to CP/M chen: mov dx,(offset chno) ;'Checksum failure.' call mssg ;send message to console gbye: call crlf ;type CR,LF mov dl,00 mov cl,00 int BDOS ; Open input file. opef: mov bx,(offset tfcb+32) mov byte ptr [bx],00 mov dx,(offset tfcb) ;CP/M's FCB mov cl,15 ;(0F) open file int BDOS inc al jz G16 ! ret ! G16: mov dx,(offset nfil) ;'requested file not found' call mssg ;send message to console jmp gbye ;return to CP/M ; Read one bit at a time. rbit: push bx dec roco ;bit rotation counter jnz rbiu mov roco,8 ;bit rotation counter call rbyt mov roby,al ;rotating byte rbiu: rcr roby,1 ;rotating byte pop bx ret ; Read one word. rwor: call rbyt ;fetch one byte from input stream push ax call rbyt ;fetch one byte from input stream pop bx mov bh,al ret ; read one byte, refill buffer as needed rbyt: cmp bctr,0000 ;byte counter - input buffer jnz rbyu push cx push dx call rdsk mov bptr,(offset bufr) ;byte pointer - input buffer mov bctr,0080H pop dx pop cx rbyu: dec bctr ;byte counter - input buffer mov bx,bptr ;byte pointer - input buffer mov al,(byte ptr[bx]) inc bptr ;byte pointer - input buffer ret rdsk: push bx push dx push cx mov dx,(offset tfcb) ;CP/M's FCB mov cl,20 ;(14) read one record int BDOS pop cx pop dx pop bx ret nfil db CR,LF,'Requested file not present.$' dint db CR,LF,'Dump interrupted.$' nsqz db CR,LF,'Not a squeezed file.$' chno db CR,LF,'Checksum failure.$' logo db CR,LF,'Type Squeezed File/ICUAP/July 1, 1984.',CR,LF,CR,LF db ' TYSQ [D:]FILE.?Q?',CR,LF,CR,LF db 'will unsqueeze FILE.?Q? and type it at the',CR,LF db 'console. Press any key to stop the display.',CR,LF db '$' rb 16 stak rw 1 lach rb 1 ;last character typed roco rb 1 ;rotating bit counter roby rb 1 ;rotating byte bctr rw 1 ;byte counter - input buffer bptr rw 1 ;byte pointer - input buffer cksm rw 1 ;given checksum smck rw 1 ;developing checksum code rb 1024 ;decoding table end