#! /usr/local/postforth/pf : BTOA ' ARGC , ' 2 , ' > , IF ( infile specified) ' 0 , ' CLOSEFILE , ' 2 , ' ARGV , ' 0 , ' 0 , ' OPEN , IF ( nonzero handle returned) " Cannot redirect STDIN" ' 2 , ' TYPE , ' 2 , ' CR , ' 1 , ' EXIT , ENDIF ENDIF ' ARGC , ' LIT , 16# 3 , ' > , IF ( outfile specified) ' 1 , ' CLOSEFILE , ' LIT , 16# 3 , ' ARGV , ' LIT , 16# 1FF , ' UMASK , ' ~ , ' & , ' FCREATE , ' 1 , ' = , ' ~ , IF ( handle not 1) " Cannot redirect STDOUT" ' 2 , ' TYPE , ' 2 , ' CR , ' 1 , ' EXIT , ENDIF ENDIF ' CONVERT_BTOA , " ~>" ' 1 , ' TYPE , ' 1 , ' CR , ' 0 , ' EXIT , ' ;S , ( Ascii85 conversion is the process of changing a 32-bit word [4 bytes] into a 5-digit base-85 character, where "!" is a zero and "u" is the highest possible value. In both cases, the leftmost byte [lowest addressed] is taken as the most significant bits. That is, if the bytes at offset 0 through 3 are, in order, 00 00 00 01, then btoa translation will make it !!!!" where '!' is zero and '"' is 1. 00 00 00 55 will translate to !!!"! because 55 [85 decimal] is a "ten" in this base, so the '"' [one] is in the second position from the right. A special case is when the 32-bit number is 0, in which case it translates to a single 'z'. Another special case is when less than 4 characters are being converted, as can happen at the end of a file. In this case, N input characters are padded with 4-N null bytes and converted into N+1 Ascii85 characters. Padding is done at the end, that is, the LSBs, since the first characters received are more significant. Also, if a group of chars is all null bytes and is padded, it does not become 'z' because 'z' stands for 4 nulls. N+1 '!' characters must be output instead. Some filters, such as postscript's, expect the Ascii85 stream to end in ~> or it will not process it correctly. Strictly speaking it should also begin with <~ but for some reason the postscript interpreters I've used don't enforce this.) VARIABLE ASCII85 16# 55 DUP DUP DUP DUP * * * ( 85 ^ 4) , DUP DUP DUP * * ( 85 ^ 3) , DUP DUP * ( 85 ^ 2) , ( 85 ^ 1) , 1 ( 85 ^ 0) , : SWAPS ( n -n2) ( swap 16-bit words in a 32-bit word) ' DUP , ' LIT , 16# 10 , ' >> , ( make a copy, and shift it down) ' SWAP , ' LIT , 16# 10 , ' << , ( shift original up) ' + , ' ;S , ( merge them with + or OR) : NEXT4 ( - d count) ' 0 , ' SP@ , ' >R , ( buffer on stack) ' 0 , ' LIT , 16# 4 , ' R> , ' READ , ' DUP , ' 0< , IF " Read error" ' 2 , ' TYPE , ' 2 , ' CR , ' 1 , ' EXIT , ENDIF ' >R , ' SWAB , ' SWAPS , ' SWAB , ' R> , ( reorder) ' ;S , : 85DIGIT ( n a - n' a') ( output next ascii85 digit, advance pointer into table and return remainder for next digit conversion) ' SWAP , ' OVER , ' @ , ' 0 , ' SWAP , ' U/M , ( divide n by *a) ' LIT , ASCII ! , ' + , ' 1 , ' EMIT , ( convert to ASCII85 and output) ' SWAP , ' LIT , 16# 4 , ' + , ' ;S , ( replace n with remainder, update a) : 85WORD ( n1 n2 -) ( output n1, count n2 as ASCII85 equivalent) ' >R , ' R , ' LIT , 16# 4 , ' = , ( count=4 and n=0, output z) ' OVER , ' 0= , ' & , IF ' LIT , ASCII z , ' 1 , ' EMIT , ELSE ( any count, any other value) ' ASCII85 , BEGIN ' 85DIGIT , ' R> , ' 1- , ' >R , ' R , ' 0< , UNTIL ' DROP , ( table addr) ENDIF ' R> , ' DROP , ( count) ' DROP , ' ;S , : CONVERT_BTOA BEGIN ' NEXT4 , ' DUP , ' >R , ' R , IF ' 85WORD , ENDIF ' R> , ' 0= , UNTIL ' DROP , ' DROP , ' ;S , BTOA ( run BTOA - comment out until debugged)