; PROGRAM: TYPESQ86.A86 ; VERSION: 1.10 ; DATE: 15 October 1984 ; AUTHOR: Charlie Godet-Ceraolo ; ; DESCRIPTION: This is a version of TYPESQ for CP/M86 systems. ; It displays the contents of a (normal or squeezed) ; text file on the console. ; Entering nothing on the command line will display ; a brief summary of the above. ; ; USE: ASM86 TYPESQ86 $PZSZ ; GENCMD TYPESQ86 DATA[M200] ;------------------------------------------------------------ ; Release EQU 1 Version EQU 10 FALSE EQU 0 TRUE EQU NOT FALSE ; ; cpm86 equates ; CpmEof EQU 1AH ConIn EQU 1 ConOut EQU 2 ; write console character ConStat EQU 11 ; console Status OpenF EQU 15 ; open file ReadF EQU 20 ; read sequential function ; ; SQ equates ; RECOGNIZE EQU 0FF76H ; marks file as squeezed SPEOF EQU 0FEFFH ; sq special end of file DLE EQU 90H Tree_Size EQU 257 ; max bytes in Huffman code ; ; Ascii equates ; TAB EQU 09H CR EQU 0DH LF EQU 0AH SPACE EQU ' ' Ascii_Zero EQU '0' EOS EQU 0H ; DSEG ; image of CP/M86's basepage ; BASEPAGE EQU $ RB 5CH FCB EQU $ ; default fcb FCBDN RB 1 FCBFN RB 8 FCBFT RB 1 FCBQ RB 1 RB 1 FCBEX RB 1 FCBS1 RB 1 FCBS2 RB 1 FCBRC RB 1 FCB2 EQU $ ; second fcb FCB2DN RB 1 FCB2FN RB 8 FCB2FT RB 3 RB 4 FCBCR RB 1 FCBRNO RW 1 FCBR2 RB 1 BUFF RB 80H ; default buffer Buf_End EQU $ ; mark end of buffer ; CSEG ; ; Set up local stack ; TYPESQ: MOV DX,DS MOV SS,DX MOV SP,Offset LOCAL_STACK CLD ; direction flag = increment MOV SI,Offset SignOn CALL MsgOut ; ; check for a filename, send help if none CMP FCBFN,Space JNE got_name MOV SI,Offset UseMsg JMP Error_Exit ; ; try to open file, exit with message if none ; got_name: CALL Open_File CMP AL,0FFH JNE which_one ; MOV SI,Offset NoFileMsg JMP Error_Exit ; ; file open, begin ; which_one: CALL Read_Sector ; fill buffer MOV BX,(Offset Buff) ; reset pointer MOV Buf_Ptr,BX ; CMP FCBQ,'Q' ; is it a 'Q' file? JE read_sq ; yes, process JMP Normal_Loop ; else plain vanilla ; read_sq: CMP Buff,RECOGNIZE ; a 'real' squeezed file? JE read_header ; yes, process JMP Normal_Loop ; nope, do normal ; read_header: CALL Read_Word ; skip over Recognize CALL Read_Word ; and skip checksum ; read_name: CALL Read_Byte ; Ascii string OR AL,AL ; null-terminated JZ get_tree CALL Char_Out JMPS read_name ; ; Load code dictionary. First word is length. followed by a ; series of word pairs. ; get_tree: CALL CrLf CALL Read_Word CMP BX,Tree_Size JBE load_tree ; MOV SI,(Offset NoRoomMsg) ; tree too big JMP Error_Exit ; load_tree: SHL BX,1 ; times 4 SHL BX,1 MOV CX,BX MOV DI,(Offset Code_Tree) move_em: CALL Read_Byte STOSB LOOP move_em MOV Rot_Counter,1 ; force first read MOV Repeat_Count,0 ; zero repeat ; USQ_Loop: CALL Usq_Char ; get a decoded character CMP AL,CpmEof ; end of file? JE Main_Exit ; yes, leave CALL Char_Out ; else display it JMPS USQ_Loop ; and back for more ; Normal_Loop: CALL Read_Byte CMP AL,CpmEof ; end of file? JE Main_Exit ; yes, leave CALL Char_Out ; else display it JMPS Normal_Loop ; and back for more ; Error_Exit: CALL MsgOut Main_Exit: MOV CL,0 ; back to CP/M86, the easy way MOV DX,0 INT 224 ; ; SUBROUTINES ; ; Return decoded byte from input stream. ; Usq_Char: CMP Repeat_Count,0 ; finished repeating this one? JE get_one ; yes, get another DEC Repeat_Count ; else just bump count MOV AX,Last_Char ; and return last (again) RET get_one: CALL Decode ; get a new char JC is_eof ; carry = eof CMP AL,DLE ; if DLE JE is_DLE ; process it MOV Last_Char,AX ; else save new one RET ; and return it is_DLE: CALL Decode ; if next OR AL,AL ; is non-zero JNZ get_repeat ; it's a repeat count MOV AL,DLE ; else return DLE RET get_repeat: DEC AL DEC AL XOR AH,AH MOV Repeat_Count,AX ; get new repeat count MOV AX,Last_Char ; and return last char RET is_eof: MOV AL,CpmEof ; return end of file RET ; ; For each bit in the code, select the first or second (1) element. ; If the pair is positive, it's the table entry (code + 4*Index) at ; which to continue with the next bit. If it's negative, it is the ; complement of the ASCII character (lsb). ; Decode: MOV BX,(Offset Code_Tree) decode1: CALL Read_Bit ; get next bit JNC its_first ; first or second child? INC BX ; go to second child INC BX its_first: MOV AX,Word Ptr [BX] CMP AX,SPEOF ; SQ eof marker? JE do_eof ; yes, tell caller OR AH,AH ; else JNS new_node ; plus = new node NOT AL ; minus = complemented char CLC ; clear carry RET ; return character new_node: MOV BX,(Offset Code_Tree) ADD BX,AX ; Calculate +4* ADD BX,AX ADD BX,AX ADD BX,AX JMPS decode1 ; and decode it do_eof: STC ; set carry for SPEOF RET ; and return ; ; Read one bit at a time, and return it in CF ; Read_Bit: DEC Rot_Counter JZ get_new ; need a new byte RCR Rot_Byte,1 ; rotate bit 0 into CF RET get_new: MOV Rot_Counter,8 ; reset counter CALL Read_Byte ; get another byte RCR AX,1 ; rotate bit 0 into CF MOV Rot_Byte,AX ; save it RET ; Open_File: XOR AL,AL MOV FCBCR,AL MOV FCBEX,AL MOV FCBRC,AL MOV DX,Offset FCB MOV CL,OpenF CALL BDOS RET ; ; Read one byte, refill buffer as needed. ; Read_Byte: PUSH SI MOV SI,Buf_Ptr CMP SI,Offset Buf_End JNE no_read CALL Read_Sector MOV SI,Offset Buff no_read: LODSB XOR AH,AH ; zero msb MOV Buf_Ptr,SI POP SI RET ; Read_Word: CALL Read_Byte MOV BL,AL CALL Read_Byte MOV BH,AL RET ; Read_Sector: PUSH BX PUSH DX PUSH CX MOV DX,Offset FCB MOV CL,ReadF CALL BDOS POP CX POP DX POP BX RET ; ; Print null-terminated string pointed to by SI, saves all ; MsgOut: PUSH AX PUSH SI msgloop: LODSB ; get a character OR AL,AL ; machine zero? JZ msgexit ; yes, exit CALL Char_Out ; print it JMPS msgloop ; get another msgexit: POP SI POP AX RET ; CrLf: PUSH AX MOV AL,CR CALL Char_Out MOV AL,LF CALL Char_Out POP AX RET ; ; Send character in AL to console, check for abort ; Char_Out: PUSH BX PUSH DX PUSH CX MOV CL,ConOut MOV DL,AL AND DL,7FH ; strip hi-bit (ws files) CALL BDOS MOV CL,Constat ; check for abort CALL BDOS OR AL,AL ; key pressed? JNZ User_Abort POP CX POP DX POP BX RET ; User_Abort: MOV CL,ConIn ; be tidy, read the char CALL BDOS JMP Main_Exit ; and leave ; BDOS: PUSH ES INT 224 POP ES RET ; DSEG $ ; ; initialized data ; DB '(c) 1984 Charlie Godet-Ceraolo' SignOn DB 'TYPESQ86 v' DB Release + Ascii_Zero,'.' DB (Version/10) + Ascii_Zero DB (Version MOD 10) + Ascii_Zero DB CR,LF,EOS UseMsg DB CR,LF DB TAB,'USAGE: TYPESQ86 FileName',CR,LF DB TAB,'Pressing any key aborts program',CR,LF DB CR,LF,EOS NoFileMsg DB 'File not found',CR,LF,EOS NoRoomMsg DB 'No room for Huffman tree',CR,LF,EOS ; ; uninitialized data ; IF (Offset $ - Offset BASEPAGE) MOD 2 NE 0 RB 1 ; make sure all is even ENDIF Repeat_Count RW 1 ; repeat factor Rot_Counter RW 1 ; rotating bit counter Rot_Byte Rw 1 ; rotating byte Last_Char RW 1 ; last character read Buf_Ptr RW 1 ; pointer into input buffer RB 100 ; 100 bytes for stack LOCAL_STACK EQU $ Code_Tree RB Tree_Size * 4 ; Huffman decoding tree ; END