; UUDECODE.MAC VERS: 01.27 DATE: 06/10/87 TIME: 20:55 ; COPYRIGHT 1987 William P.N. Smith - Commercial use prohibited! ; decode files from uuencoded to binary format. See ; UUENCODE program for more details. ; ; USAGE: ; A> UUDECODE infile.UUE ; where infile.UUE is an ASCII file produced by UUENCODE. ; note that this program will extract the output file name ; from the uuencoded file. ; ; Note that to simplify programming, we are assuming not too many surprises ; in the input file. If someone doesn't cut the mail header off of a file, ; and that header contains 'begin ' at the start of a line, (for instance), ; a very bizarre output file is likely to result... ; ; resolved ` (60H) conflict (was protocol bug) 06/08/87 WPNS CR EQU 0DH LF EQU 0AH NULL EQU 00H DEFDMA EQU 0080H ;CP/M default dma area ZERO EQU 00H BOOT EQU 0000H ;CP/M reboot jump DEFCB1 EQU 005CH ;first auto-parsed FCB EOF EQU 1AH UUDECODE:: LD SP,MYSTAK ;set up the stack pointer LD DE,HELLO ;initial message CALL STROUT## ;send to console JR CHKIN ;and go do something useful HELLO: DB CR,LF,LF DB 'UUDECODE rev 1.2 06/10/87 WPNS' DB CR,LF,NULL,'$' ;check the input line CHKIN: LD A,(DEFDMA) ;get the command tail length CP ZERO ;anything there? JP NZ,CHKWLD ;yup, look at it NOTAIL: LD DE,USAGE ;tell him what's going on CALL STROUT## JP BOOT ;and restart CP/M USAGE: DB CR,LF DB 'Copyright 1987 William P.N. Smith, all rights reserved.' DB CR,LF,LF DB 'Follow command with the name of the uuencoded file,' DB CR,LF DB ' for example A> UUENCODE FILENAME.UUE' DB CR,LF,NULL,'$' CHKWLD: LD DE,DEFCB1 ;point at the default file control block LD BC,16 ;search first parameter only LD A,'?' ;for wild card character CPIR JR NZ,MOVEM ;no wild card, still more to do. WLDTRP: LD DE,WLDERR ;tell the console CALL STROUT## JP BOOT ;and abort WLDERR: DB CR,LF DB 'No Wild Cards Allowed!' DB CR,LF,NULL,'$' MOVEM: LD DE,DEFCB1 ;point at the first file given CALL IOPEN## ;open it up as input file ;OK, now read the file till we get to 'begin ', at which time we assume ;we have found the header line. RDTOHDR:JR NEWLINE ;start scanning immediately SKTONEW:CALL GETBYT## ;strip another byte from the file JP Z,ENDFOUND ;if file end encountered, that;s a bug! CP EOF ;same if we find a control-Z JP Z,ENDFOUND CP CR ;if we get a newline... JR Z,NEWLINE ;see if it's the right line CP LF ;CR or LF count as newline JR Z,NEWLINE JR SKTONEW ;else keep searching ; each time we get a new line (CR or LF), jump here.. NEWLINE:CALL GETBYT## ;get Y.A. input byte JP Z,ENDFOUND ;too late, panic! CP CR ;another newline already? JR Z,NEWLINE CP LF JR Z,NEWLINE CP 'b' ;start of 'begin '? JR NZ,SKTONEW ;nope, try the next line ; we have a 'b', do I hear an 'e'? CALL GETBYT## ;get another JP Z,ENDFOUND ;too late, panic! CP CR ;another newline already? JR Z,NEWLINE CP LF JR Z,NEWLINE CP 'e' ;GIMME AN _E_! JR NZ,SKTONEW CALL GETBYT## ;get another JP Z,ENDFOUND ;too late, panic! CP CR ;another newline already? JR Z,NEWLINE CP LF JR Z,NEWLINE CP 'g' ;GIMME A _G_! JR NZ,SKTONEW CALL GETBYT## ;get another JP Z,ENDFOUND ;too late, panic! CP CR ;another newline already? JR Z,NEWLINE CP LF JR Z,NEWLINE CP 'i' ;GIMME AN _I_! JR NZ,SKTONEW CALL GETBYT## ;get another JP Z,ENDFOUND ;too late, panic! CP CR ;another newline already? JR Z,NEWLINE CP LF JR Z,NEWLINE CP 'n' ;GIMME AN _N_! JR NZ,SKTONEW CALL GETBYT## ;get another JP Z,ENDFOUND ;too late, panic! CP CR ;another newline already? JR Z,NEWLINE CP LF JR Z,NEWLINE CP ' ' ;GIMME A SPACE! JP NZ,SKTONEW ; WHAT DOES IT SPELL?! ; HEADER!!! ; OK, if we got here, we found (after a newline of some kind), 'begin ' ; so now we strip bytes out (ignore the mode) till the next space. LKFRSPC:CALL GETBYT## ;get a byte JP Z,ENDFOUND ;error if EOF CP ' ' ;next delimiter found? JR NZ,LKFRSPC ;nope, LooK FoR SPaCe ; We should now be able to read out the file name, and build an FCB! LD HL,OUTNAME ;point at the output file name LD B,9 ;8 char file name max (9 is error) FNLP: CALL GETBYT## ;get next byte JR Z,ENDFOUND ;error handler CP '.' ;delimiter? JR Z,FILTYP ;yup, get the type byte LD (HL),A ;else must be part of the file name INC HL ;bump the pointer DJNZ FNLP ;and do it again LD DE,NODELIM ;tell him no delimiter CALL STROUT## JP BOOT ;and reboot CP/M NODELIM:DB CR,LF DB 'Fatal error - file name in header line too long!' DB CR,LF,NULL,'$' FILTYP: LD HL,OUTTYPE ;point at the file type CALL GETBYT## ;next 3 bytes are type JR Z,ENDFOUND LD (HL),A INC HL ;bump the pointer CALL GETBYT## JR Z,ENDFOUND LD (HL),A INC HL CALL GETBYT## JR Z,ENDFOUND LD (HL),A ; OK, the file control block (well the first 16 bytes) has been built, open it! LD DE,OUTFILE ;point at the output file name CALL OOPEN## ;set that up as output file JR DOIT ;and start decoding the file OUTFILE:DB 00H ;drive is default OUTNAME:DB ' ' ;output file name OUTTYPE:DB ' ' ;output file type DB 0,0,0,0 ;zero the other stuff ENDFOUND: ;end of file encountered during header! LD DE,FOUNDEND CALL STROUT## JP BOOT FOUNDEND: DB CR,LF DB 'Fatal error - end of input file encountered during header parse!' DB CR,LF,NULL,'$' ;------------------------------------------------------------------------------ ; DOIT this is where we finally get to do the transform ; ;this routine uses 2 external routines GETBYT and PUTBYT. ; ; GETBYT will get another byte from the input file every time it is ;called. When the end of file is encountered, it will return an FF byte, with ;the ZERO flag true. ; ; PUTBYT will put another byte into the output file buffer and will flush ;the buffer when it fills. DOIT:: CALL GETBYT## ;get input byte JP Z,EARLYEND ;end of file not expected here CP CR ;start the line over for newline JR Z,DOIT CP LF JR Z,DOIT ;else we have a byte count (not a char count!) SUB ' ' ;remove the offset to get binary byte count JR Z,BODYEND ;zero count line terminates 'body' JP M,RANGERR ;if it was less than space, it's under range CP 40H ;check for phantom space JR Z,BODYEND ;if so, end of body CP 40H ;overrange? JP M,DOLINE ;nope, do the line RANGERR:LD DE,PROERR ;tell him something strange happened CALL STROUT## JR BODYEND ;and close out the file PROERR: DB CR,LF DB 'Strange protocol error, converted byte out of range!' DB CR,LF,NULL,'$' BODYEND: GOTEOF: CALL OCLOSE## ;finish write and close output file CALL ICLOSE## ;close input file JP BOOT ;and restart CP/M DOLINE: LD (BYTCNT),A ;stuff byte count into storage DECODE_LOOP: LD HL,0000H ;clear work registers LD DE,0000H CALL GETBYT## ;get first byte JP Z,EARLYEND ;end of file not expected here SUB ' ' ;remove offset AND 00111111B ;mask to 6 bits (phantom space) LD H,A ;stuff into 'result' register CALL GETBYT## ;get second byte JP Z,EARLYEND ;end of file not expected here SUB ' ' ;remove offset AND 00111111B ;mask to 6 bits (phantom space) SLA A ;shift to upper six bits SLA A LD L,A ;move to shift_input register ;now first byte is in HL, but offset by 2 bits SLA L ;shift HL up one bit RL H SLA L RL H ;now 2 bits up. ;now the H reg magically holds the first output byte and the upper nibble of the ;L reg holds the 'extra', left over 4 bits. LD A,H ;get byte CALL PUTBYT## ;stuff into output file CALL DECCNT ;decrement output byte count from this line SLA L ;move 4 leftover bits to H RL H SLA L RL H SLA L RL H SLA L RL H ;now 4 leftovers in lower nibble of H ;get another, move it up adjacent to the leftover 4 bits, and shift up 4 bits CALL GETBYT## ;get third input byte JP Z,EARLYEND ;end of file not expected here SUB ' ' ;remove offset AND 00111111B ;mask to 6 bits (phantom space) SLA A ;shift to upper six bits SLA A LD L,A ;move to shift_input register ;now second byte is in HL, but offset by 4 bits SLA L ;shift HL up four bits RL H SLA L RL H SLA L RL H SLA L RL H ;now the H reg magically holds the second output byte and the upper 2 bits ;of the L reg holds the 'extra', left over 2 bits. LD A,H ;get byte CALL PUTBYT## ;stuff into output file CALL DECCNT ;decrement output byte count from this line SLA L ;move 2 leftover bits to H RL H SLA L RL H ;now H holds leftover 2 bits ;get the fourth character and mung in the 6 bits from it with the 2 left over ;from the previous operation, and you have the final output byte! CALL GETBYT## ;get fourth input byte JR Z,EARLYEND ;end of file not expected here SUB ' ' ;remove offset AND 00111111B ;mask to 6 bits (phantom space) SLA A ;shift to upper six bits SLA A LD L,A ;move to shift_input register ;now second byte is in HL, but offset by 6 bits ;note that now it's easier to shift down by 2 bits SRL H ;shift HL down RR L SRL H RR L ;now output byte is in L reg ;third output byte in L register, no leftover bits. LD A,L ;get byte CALL PUTBYT## ;stuff into output file CALL DECCNT ;decrement output byte count JP DECODE_LOOP ;and go mash up another set DECCNT: LD A,(BYTCNT) ;get the byte count DEC A ;one more has been done LD (BYTCNT),A ;put it back! RET NZ ;if not done, go back to what you were doing POP AF ;else do not return to where you came from ; instead skip to end of line DCLP: CALL GETBYT## ;get another input byte JR Z,EARLYEND ;if no more, something's wrong CP CR JP Z,DOIT ;if newline found, start another line CP LF JP Z,DOIT JR DCLP ;else keep looking! EARLYEND: LD DE,WANTMORE ;point at message CALL STROUT## ;send to console JP GOTEOF ;and attempt to close file WANTMORE: DB CR,LF DB 'Fatal error - Unexpected end of file during decode!' DB CR,LF,NULL,'$' BYTCNT: DB 00H ;how many bytes are represented in this line? DS 256 MYSTAK: ;private stack .REQUEST strout,ifileops,ofileops END UUDECODE