; Written by Dan Norstedt, Stockholm, Sweden Last edit 17-Feb-1990 ; ; This program is placed in the public domain. ; ; Use this program to generate MSBPCT.COM for MS-DOS with the commands: ; ; C>MASM MSBPCG; ; C>LINK MSBPCG; ; C>MSBPCG >MSBPCT.COM ; ; The generated MSBPCT.COM file is trick coded, and only contains ; printable 7-bit ASCII characters. Thus, it can be sent as text ; without modifications, just capture it to disk, trim off excess ; text, and execute it. It takes one input argument, the input file. ; If no valid file is given, a Usage: message is given. ; ; The program will decodes about 10Kbyte/sec on my AT hard disk. ; ; The loader and builder portions of this program may be useful for other ; programs, too. CODE SEGMENT PUBLIC ASSUME CS:CODE,DS:CODE ORG 100H START: JMP GENCOM ORG 100H+72+1 ; Address where loader starts loading CODARE: ;----------------------- beginning of MSBPCT code area ----------------------- INBUF EQU ((Offset MSBEND-Offset START+1FFH) AND 0FF00H) INBSIZ EQU 400H OUTBUF EQU INBUF+INBSIZ ; CLD ; Already done in the loader MOV DX,Offset MEMTXT CMP SP,0FF00H ; Enough memory? JB OUTMSG MOV SI,80H ; Point at line length byte XOR AH,AH LODSB ; Get it XCHG AX,BX MOV Word Ptr [SI+BX],0 ; Clear end of command string LEA DX,[SI+1] MOV AX,3D00H ; Try to open the file INT 21H MOV DX,Offset USETXT ; Tell usage if we can't open input file JNC OPENOK OUTMSG: MOV AX,901H ; Output error text EXIT: PUSH AX INT 21H POP AX ; Set up exit code MOV AH,4CH INT 21H OPENOK: XCHG BX,AX ; Save input handle MOV DX,Offset FILTXT ; Output header MOV AH,9 MOV SI,OUTBUF ; Temorary buffer for output filename RDMORE: INT 21H MOV AH,3FH ; Read a character MOV CX,1 MOV DX,SI INT 21H ERROR: MOV DX,Offset ERRTXT JC OUTMSG ; Error if failure here DEC AX JNZ OUTMSG LODSB XCHG AX,DX MOV AH,2 ; Prepare for output CMP DL,20H JA RDMORE ; Printable character -> save and output MOV AH,3CH ; Prepare for file create XOR CX,CX MOV [SI-1],CL ; Make input filename buffer NUL terminated MOV DX,OUTBUF ; Pointer to file name MOV DI,DX INT 21H ; Create output file JC ERROR XCHG AX,BX ; Save file output file handle in BX XCHG AX,BP ; Input file handle to BP READBU: PUSH DX ; Save shift count (initial value in DL = 0) MOV DX,OUTBUF MOV CX,DI SUB CX,DX ; Compute size of output buffer JZ NOWRIT ; Zero, skip write MOV AH,40H ; Write it out INT 21H JC ERROR SUB AX,CX ; Write failed? JC ERROR NOWRIT: MOV DI,DX ; No, reset output buffer pointer MOV DX,INBUF ; Load input buffer pointer MOV SI,DX ; Set up for later use MOV CX,INBSIZ ; Read one buffer of appropriate size MOV AH,3FH XCHG BX,BP ; Swap input and output file handles INT 21H XCHG BX,BP JC ERROR XCHG AX,CX MOV AX,3E00H ; Load good close function code, just in case JCXZ EXIT ; End of input file, do the good exit POP DX ; Regain shift count (in DL) INLOOP: LODSB ; Get a character SUB AL,30H JL CHRDON ; Junk, throw away MOV AH,DH ; Good one, get old saved char to AH MOV DH,AL ; Save current instead CMP AH,4EH ; Old character flags expansion of NULs? JZ NULLS SHL AL,1 ; No merge low bits of old char with new char SHL AL,1 XCHG CX,DX ; Get current shift amount (0, 2, 4 or 6) SHR AX,CL ; Do shift XCHG CX,DX SUB DL,2 ; Adjust for new shift factor,old = 0 -> skip? JS INLOOR ; Yes, skip STOSB ; No, save result of shift operation INLOOR: AND DL,6 ; Adjust shift count if DL was = 0 JMP Short CHRDON ; End loop NULLS: CBW ; Expand NUL count CWD ; Reset shift count in DL and NULL flag in DH PUSH CX ; Save input character left counter XCHG AX,CX MOV AL,0 REP STOSB ; Store selected amount of NULs POP CX ; Restore counter CHRDON: LOOP INLOOP ; Loop for rest of input block JMP Short READBU ; Try to read another buffer USETXT DB 'Usage: MSBPCT file.BOO$' FILTXT DB 'Unpacking to file: $' ERRTXT DB 'Error during file I/O$' MEMTXT DB 'Not enough memory$' MSBEND: ;-------------------------- end of MSBPCT code area -------------------------- CODEND: ;-- Loader starts here; don't touch unless you REALLY know what's going on -- FIXUP MACRO LABL,OFFS,DAT,DA2,DAS ORG $+(OFFS) LABL Label Byte IFNB IFNB DB (((DAS) OR 65H)-((DAS) AND 65H)) AND 0FFH,0FFH-(DA2) ORG $-1 ELSE DB ((DAS)+65H) AND 0FFH ENDIF ELSE DB 0FFH-((DAT) AND 0FFH) IFNB DB 0FFH-(DA2) ORG $-1 ENDIF ENDIF ORG $-(OFFS)-1 ENDM FIXBYT MACRO ADDR ; Generate "XOR [BX+ADDR-0FFH],AL" with 8-bit offset DB 30H,47H,(Offset ADDR-Offset LOADER+100H)-0FFH ENDM FIXBYH MACRO ADDR ; Generate "XOR [BX+ADDR-0FFH],AH" with 8-bit offset DB 30H,67H,(Offset ADDR-Offset LOADER+100H)-0FFH ENDM FIXWRD MACRO ADDR ; Generate "XOR [BX+ADDR-0FFH],AX" with 8-bit offset DB 31H,47H,(Offset ADDR-Offset LOADER+100H)-0FFH ENDM FIXSUB MACRO ADDR ; Generate "SUB [BX+ADDR-0FFH],AL" with 8-bit offset DB 28H,47H,(Offset ADDR-Offset LOADER+100H)-0FFH ENDM LOADER: POP AX PUSH AX DEC AX ; Load AX with 0FFFFH PUSH AX ; Now FF FF 00 00 on stack INC SP POP BX ; Load BX with 00FF FIXBYT XORBXX ; Fix up end of loader code FIXBYT XORB1 FIXBYT XORB2 FIXWRD XORW1 FIXWRD XORW2 DAA ; Load AL with 65H FIXSUB SUBB1 FIXSUB SUBB2 JNZ J0JMP ; Break pipeline J0DST: FIXBYH XORB3 FIXSUB SUBB3 FIXSUB SUBB4 FIXWRD XORX1 ; FC CLD ; Set LODSB direction FIXUP SUBB1,-1,,,0FCH ; 8D7749 LEA SI,[BX+DATA-0FFH] ; Point at 100H+72 DB 8DH,77H,(Offset DATA-Offset LOADER+100H)-0FFH FIXUP XORB1,-3,8DH ; 56 PUSH SI ; Copy SI -> DI ; 5F POP DI ; 46 INC SI J2DST: ; 2AC2 SUB AL,DL ; Compute real code/data byte ; AA STOSB ; Save it FIXUP XORW1,-2,0C2H,0AAH J1DST: J3DST: ; AC LODSB ; Get an encoded data byte FIXUP XORB2,-1,0ACH ; 40 INC AX ; 3C31 CMP AL,31H ; Printable and >= '0'? ; 7CFA JL J1DST J1SRC: FIXUP SUBB2,-1,,,J1DST-J1SRC ; 2C35 SUB AL,'4'+1 ; Yes, '0'-'3' (or '4' = exit code) ? ; 77F3 JA J2DST ; No, store with current prefix code J2SRC: FIXUP SUBB3,-1,,,J2DST-J2SRC ; B102 MOV CL,2 FIXUP XORB3,-2,0B1H ; D2C8 ROR AL,CL FIXUP XORX1,-3,,0D2H,2 ; 92 XCHG DX,AX ; Yes, just save shifted value FIXUP XORW2,-2,0C8H,92H ; 75EF JNZ J3DST ; No, contine loop J3SRC: FIXUP SUBB4,-1,,,J3DST-J3SRC ; 75D7 J0JMP: JNZ J0DST ; (Dummy branch used to clear prefetch queue) J0SRC: FIXUP XORBXX,-1,J0DST-J0SRC DB 34H ; Skip over next byte (34H = XOR AL,nn opcode) DATA: DB "$" ; CRLF data to make sure JL J1DST taken first time GENCOM: PUSH CS ; Allow use without EXE2BIN POP DS MOV DX,Offset LOADER ; Output LOADER code (!) MOV AH,9 INT 21H CLD MOV SI,Offset CODARE ; Pointer to real MSBPCT code XOR BP,BP ; Reset columns left counter MOV BH,17 ; Assure that BH not in range '0'-'3' BYTLOP: MOV AX,0C01H SUB AL,[SI] ; Convert MSBPCT code to loader format INC SI MOV CL,2 SHL AX,CL ; First byte is top 2 bits of byte+43H NOT AL SHR AL,CL ; Second byte is low 6 bits of -byte-3 ADD AL,35H ; Based value is '5' for second byte CMP AH,BH ; Same prefix as previous byte? MOV BH,AH XCHG DX,AX OUTBYT: XCHG DH,DL ; Swap output order JZ OUTNHI ; Skip unnecessary prefix byte DEC BP JG OUTNCR ; Not 72 chars on the line yet PUSH DX MOV DX,Offset CRLF ; 72 chars on line, add CR LF MOV AH,9 INT 21H MOV BP,72 ; Restart line pointer POP DX OUTNCR: MOV AH,2 ; Output a byte INT 21H OUTNHI: XOR DL,DL ; Clear out used code byte AND DH,DH ; Anything more to print? JNZ OUTBYT CMP SI,Offset CODEND ; End of area? JNZ BYTLOP MOV DX,Offset ENDTXT ; Yes, add trailer: 34H and CR LF MOV AH,9 INT 21H MOV AH,4CH ; End GENCOM program section INT 21H ENDTXT DB 34H ; End of file marker for loader CRLF DB 13,10,"$" CODE ENDS END START