; ----------------------------------------------------------- ; ; "Squeezed" files can be generated by the program SQ.COM, ; (SIG/M 58.26) and then restored by the program USQ.COM ; (SIG/M 58.27), 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 three or more times by a special code, namely ; <90H>. The squeezed file consists of a marker ; , checksum, the 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. ; ; It appears that the whole file is encoded, without regard ; to whether it is an ASCII file or not; if a real 90H forms ; part of the file, it is encoded with a count of zero, thus ; binary files can also be squeezed and unsqueezed. ; ; 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 0005H ;BDOS entry point 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: lxi sp,stak lda tfcb+1 ;CP/M's FCB cpi ' ' jz tuto ;type tutorial message lxi h,tfcb+10 ;CP/M's FCB mov a,m cpi 'Q' jz yext lxi d,nsqz ;'not a squeezed file' call mssg ;send message to console jmp gbye ;return to CP/M ; Initializations. yext: call opef ;open input file lxi h,0000 shld bctr ;byte counter - input buffer shld smck ;developing checksum shld pctr ;"program counter" lxi h,lbuf ;line buffer shld lptr ;line pointer mvi a,0 sta cflg ;z/nz=not/.COM mvi a,1 sta roco ;bit rotation counter mvi a,16 sta colu ;column counter sta rown ;row number lxi b,0000 ;byte counter, kept in BC ; 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 cpi 076H jnz cote ;mssg: 'not squeezed file' call rbyt ;fetch one byte from input stream cpi 0FFH jz rchk ;read checksum cote: lxi d,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 shld cksm ;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. loop: call rbyt ;fetch one byte from input stream ora a jz ldic ;load code dictionary cpi '.' jnz loos call cona ;type character at console call rbyt mov b,a call ucfo cpi 'C' mov a,b jnz loos call cona ;type character at console call rbyt mov b,a call ucfo cpi 'O' mov a,b jnz loos call cona ;type character at console call rbyt mov b,a call ucfo cpi 'M' mov a,b jnz loos lxi h,cflg ;z/nz=not/.COM mvi m,0FFH loos: call cona ;type character at console jmp loop ; 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 c,l mov b,h lxi d,code ;decoding table looq: call rbyt ;fetch one byte from input stream stax d inx d call rbyt ;fetch one byte from input stream stax d inx d call rbyt ;fetch one byte from input stream stax d inx d call rbyt ;fetch one byte from input stream stax d inx d dcx b mov a,c ora b jnz looq call crlf ;type CR,LF ; Type unsqueezed code. Beware of the [end] marker, ; and also the repeat marker 90H which occurs in the ; combination <90H>. When count is zero, ; 90H itself is intended; otherwise is to be ; repeated times, including the occurrence just ; before 90H was seen. tusq: call dnch ;decode next character jc chek ;verify the checksum cpi 090H ;repeat last character jnz tusu ;normal character call dnch ;decode next character ora a jnz tusr mvi a,090H call type jmp tusq tusr: dcr a ;get count, adjust it mov b,a ;set count aside tust: lda lach ;last character typed call type ;type char & add to checksum dcr b ;update count jnz tust ;repeat until exhausted jmp tusq ;type unsqueezed code tusu: sta lach ;last character typed call type ;type char & add to checksum jmp tusq ;type unsqueezed code ; Decode next character. dnch: lxi h,code ;decoding table dncr: call rbit ;read next bit jnc dncs ;skip for 1, stay for 0 inx h inx h dncs: mov e,m ;get next offset inx h mov d,m mov a,d cpi 0FEH ;FEFF means [end] jz dnct ora a jp dncu ;p means new offset mov a,e ;m means complemented char cma stc cmc ret dnct: stc ;flag [end] with carry bit ret ; Calculate +4*. dncu: lxi h,code ;decoding table dad d dad d dad d dad d 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: mvi c,11 ;(0B) console status call BDOS rrc rnc lxi d,dint ;'Dump interrupted.' call mssg ;send message to console mvi c,1 ;(01) read console call BDOS jmp gbye ;return to CP/M ; Type several spaces. four: call dubl dubl: call sngl ;type space sngl: mvi a,' ' jmp cona ;type character at console ; Type CR, LF. crlf: mvi a,CR call cona ;type character at console mvi a,LF jmp cona ;type character at console ; Type unsqueezed text and accumulate checksum. ; Don't send ^Z's to the console. Decompose into ; hexadecimal nibbles if it is a .COM file. type: push h push d push b push psw mov b,a lhld smck ;developing checksum add l mov l,a mov a,h aci 0 mov h,a shld smck ;developing checksum lda cflg ;z/nz=not/.COM ora a mov a,b jz typp call tyfo jmp tyqq typp: cpi 1AH cnz cona ;type character at console tyqq: call break ;check for interrupt request pop psw pop b pop d pop h ret ; Type nibbles, bytes, words according to entry point. twor: mov a,h call tbyt ;type byte mov a,l tbyt: push psw rrc rrc rrc rrc call tyoo pop psw tyoo: ani 0FH adi 90H daa aci 40H daa jmp cona ;type character at console ; Type in the format of a hexadecimal dump. tyfo: push psw lda colu ;column counter cpi 16 jnz tyfu lhld pctr ;"program" counter call twor ;type word lxi d,0010H dad d shld pctr ;"program" counter call sngl ;type space tyfu: call sngl ;type space pop psw lhld lptr ;line pointer mov m,a inx h shld lptr ;line pointer call tbyt ;type byte lxi h,colu ;column counter dcr m rnz mvi m,16 call tyal ;type ASCII line lxi h,rown ;row number dcr m jnz crlf ;type CR,LF mvi m,16 call crlf ;type CR,LF jmp crlf ;type CR,LF ; Type ASCII line. tyal: call four ;type four spaces mvi b,16 lxi h,lbuf ;line buffer shld lptr ;line pointer tyty: mov a,m call dorp call cona ;type character at console inx h dcr b jnz tyty ret ; Upper case fold. ucfo: cpi 'a' rc cpi '{' rnc ani 5FH ret ; Put a dot in place of non-printables. dorp: cpi ' ' jc dopp cpi 7FH rc dopp: mvi a,'.' ret ; Send character in accumulator to console. cona: push h push d push b mvi c,2 ;(02) char to console mov e,a call BDOS pop b pop d pop h ret ; Send message to the console. mssg: mvi c,9 ;(09) print buffer jmp BDOS ; Verify the checksum. chek: lhld cksm ;given checksum xchg lhld smck ;developing checksum mov a,l cmp e jnz chen ;checksum failed mov a,h cmp d jz exit ;return to CP/M chen: lxi d,chno ;'Checksum failure.' call mssg ;send message to console gbye: call crlf ;type CR,LF exit: jmp 0000 ; Open input file. opef: xra a sta tfcb+32 ;CP/M's FCB lxi d,tfcb ;CP/M's FCB mvi c,15 ;(0F) open file call BDOS inr a rnz lxi d,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 h lxi h,roco ;bit rotation counter dcr m jnz rbiu mvi m,8 call rbyt ;fetch one byte from input stream sta roby ;rotating byte rbiu: lda roby ;rotating byte rar sta roby ;rotating byte pop h ret ; Read one word. rwor: call rbyt ;fetch one byte from input stream push psw call rbyt ;fetch one byte from input stream pop h mov l,h mov h,a ret ; Read one byte, refill buffer as needed. rbyt: lhld bctr ;byte counter - input buffer mov a,l ora h jnz rbyu push b push d call rdsk lxi h,bufr shld bptr ;byte pointer - input buffer lxi h,0080H pop d pop b rbyu: dcx h shld bctr ;byte counter - input buffer lhld bptr ;byte pointer - input buffer mov a,m inx h shld bptr ;byte pointer - input buffer ret ; BDOS disk read of one sector. rdsk: push h push d push b lxi d,tfcb ;CP/M's FCB mvi c,20 ;(14) read one record call BDOS pop b pop d pop h ret tuto: lxi d,logo ;'ICUAP' logo call mssg ;send message to console jmp gbye ;return to CP/M 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.EXT',CR,LF,CR,LF db 'will unsqueeze FILE.EXT and type it at the',CR,LF db 'console. The convention for squeezed files',CR,LF db 'is that EXT has the form ?Q?. Press any key',CR,LF db 'to stop the display.',CR,LF db '$' ds 16 stak: ds 2 lach: ds 1 ;last character typed roco: ds 1 ;rotating bit counter roby: ds 1 ;rotating byte bctr: ds 2 ;byte counter - input buffer bptr: ds 2 ;byte pointer - input buffer cksm: ds 2 ;given checksum smck: ds 2 ;developing checksum cflg: ds 1 ;z/nz=not/.COM rown: ds 1 ;row counter pctr: ds 2 ;"program" counter colu: ds 1 ;column counter lptr: ds 2 ;line pointer lbuf: ds 16 ;line buffer code: ds 1024 ;decoding table end