dewi@druca.UUCP (WilliamsD) (04/14/86)
Well, it's just been pointed out to me that the ansiecho source that I issued to the net a few weeks back was scrogged (I missed the comment to that effect on this newsgroup). It turned out that the public domain xmodem I *was* using dropped blocks on a NAK (!) so it never even made it unscathed to the local UNIX box. Long live kermit... So here it is, reposted. Also thrown in are two other CLI routines. A "touch" command, useful for taming a rogue make and getting rid of those "Future" dates. WARNING: due to a bug in the RAM: handler, it won't update the datestamp for ramdisk-resident files. It'll handle zero length files and checks for its argument being a directory. I've also added an assembler version of "list" called "alist", just in case anybody wants to build on it. It's the bare-bones list, it just prints out the file names. I originally wrote it as a response to a BBS discussion that claimed that list and dir were slow because they were written in a high-level language. In case you're curious, alist only turned out to be around 2-3 seconds faster than list on my C: directory. About the only advantage of this list command is its size, around 660 bytes (list is 8664 bytes long). Still, it shouldn't be that much work to add "ls -l" functionality. If somebody does it, please send me a copy! You'll need V1.1 of the Macro Assembler for these. Finally, for amusement only, here's a small CLI mis-feature: makedir junk echo >junk blow away the directory! Yes. If you do a list on junk, you'll find that it's changed to a text file! Before you panic, this only works with *empty* directories. But still... Dewi. ------------------------- snip it, snip it good ----------------------------- echo "creating source files:" echo "\ttouch.asm" cat <<"FUNKYSTUFF" >touch.asm * TOUCH.ASM: A recreation in assembler of the UN*X command of the same name. * Works by read & write rather than attempting to muck directly * with filesystem datestamps. * Perpetrator: Dewi Williams ..!ihnp4!druca!dewi * Unconditionally placed in the public domain. * Included equate files. * ----------------------------------------------------------------------- NOLIST INCLUDE "exec/types.i" INCLUDE "exec/libraries.i" INCLUDE "libraries/dos.i" INCLUDE "libraries/dosextens.i" INCLUDE "exec/memory.i" LIST * External references * ----------------------------------------------------------------------- EXTERN_LIB OpenLibrary EXTERN_LIB Read EXTERN_LIB Write EXTERN_LIB Output EXTERN_LIB Open EXTERN_LIB Close EXTERN_LIB Lock EXTERN_LIB UnLock EXTERN_LIB IoErr EXTERN_LIB Seek EXTERN_LIB AllocMem EXTERN_LIB FreeMem EXTERN_LIB Examine EXTERN_LIB DeleteFile * Local Equates * ------------------------------------------------------------------------ SysBase EQU 4 ; The one known address in the system. TRUE EQU -1 FALSE EQU 0 SEEKFAIL EQU -1 * Macros * ------------------------------------------------------------------------ * Calls to dos.library and exec library. Differing calls scrog different * registers. I chose to take no chances... callsys MACRO MOVEM.L D1-D7/A0-A6,-(SP) ; save registers CALLLIB _LVO\1 MOVEM.L (SP)+,D1-D7/A0-A6 ; and restore (AmigaDOS scrogs!). ENDM callexec MACRO MOVEM.L D1-D7/A0-A6,-(SP) ; save registers LINKLIB _LVO\1,SysBase MOVEM.L (SP)+,D1-D7/A0-A6 ; and restore (AmigaDOS scrogs!). ENDM * The print routine and argument setup PRINT MACRO lea.l \1(PC),A0 move.l D1,-(SP) move.l d5,d1 jsr print move.l (SP)+,D1 ENDM * General purpose close, print, and quit macro QUIT MACRO callsys IoErr ; return from IoErr will be exit code move.l D0,D6 ; save error return. Could use stack... PRINT \1 callsys Close ; assumes handle in D1 move.l d6,d0 ; restore error return bra FINISHED ENDM * The code segment * ------------------------------------------------------------------------- RORG 0 ; Relocatable code. jsr argtrim ; clean up command line ;------ get Exec's library base pointer: LEA.L DOSName(PC),A1 ; name of dos library move.l #LIBRARY_VERSION,d0 callexec OpenLibrary,SysBase MOVE.L D0,A6 ; Save library pointer (always in A6). BNE.S gotdos ; Should really issue an alert here... moveq #RETURN_FAIL,D0 ; give up bra FINISHED gotdos: * Obtain the output handle (needed for error prints). callsys Output MOVE.L D0,D5 ; Save for the write * Now check if we have an argument. cmpi.b #0,(A0) ; a null string for an argument ? beq noarg ; yes ; Actions differ depending on whether or not the file exists. If it ; does, we read and write the first byte (if it isn't empty!). If it ; doesn't, we create it as a zero length file. So we start off by ; attempting to lock the given name. move.l a0,a5 ; safe copy move.l a0,d1 ; supply the name move.l #ACCESS_READ,d2 ; only checking for existence callsys Lock move.l d0,d1 ; remember for later beq nolock ; did we get a lock? ; It exists: let's Examine it and see what it is. Need to deny a touch ; of a directory and do special actions for a zero length file. jsr CheckFile cmpi.l #0,D0 beq proceed cmpi.l #1,D0 beq unlinkit cmpi.l #2,D0 beq isadir ; touch a directory?! ; Get here if we encountered an error in CheckFile bra examerror ; It's a zero length file, so unlink it and re-create. unlinkit: callsys UnLock ; lock's still in D1 move.l a5,d1 ; the filename callsys DeleteFile ; blow it away bra nolock ; create a new zero length file ; It exists and is not empty, so unlock it and proceed with open read and ; write. proceed: callsys UnLock move.l a5,d1 ; the filename move.l #MODE_OLDFILE,d2 ; allows reading & writing, no creation. callsys Open move.l d0,d1 ; for read, write, seek and close beq noopen ; couldn't open, but it exists! lea.l char(pc),a4 ; where the single char read goes move.l a4,d2 moveq #1,d3 ; single character read callsys Read cmpi.l #1,d0 ; did it work? bne noread ; Seek back to the beginning. moveq #0,d2 move.l #OFFSET_BEGINNING,d3 ; seek mode -- 0 bytes from beginning callsys Seek cmpi.l #SEEKFAIL,D0 beq noseek ; seek failed for some reason move.l a4,d2 ; the character that was read moveq #1,d3 ; the count of characters callsys Write cmpi.l #1,d0 ; did it work? bne nowrite ; no callsys Close ; clean up move.l #RETURN_OK,D0 bra FINISHED ; finally nolock: ; It doesn't exist, so just create a zero length file. move.l a5,d1 ; the filename move.l #MODE_NEWFILE,d2 ; create callsys Open move.l d0,d1 ; for close beq nocreate callsys Close ; this should leave us with a zero length file move.l #RETURN_OK,D0 bra FINISHED * Error traps. isadir: callsys UnLock ; lock's still in D1 PRINT S_ISADIR move.l #RETURN_FAIL,D0 bra FINISHED examerror: PRINT S_EXAMERR move.l #RETURN_FAIL,D0 bra FINISHED noseek: QUIT S_NOSEEK nowrite: QUIT S_NOWRITE noread: QUIT S_NOREAD noopen: PRINT S_NOOPEN callsys IoErr ; return from IoErr will be exit code bra FINISHED nocreate: PRINT S_NOCREATE callsys IoErr ; return from IoErr will be exit code bra FINISHED noarg: PRINT S_NOARG move.l #RETURN_ERROR,D0 ; Fall through FINISHED: rts * Subroutines * ------------------------------------------------------------------------ * argtrim: a routine to trim the end of a command line and null terminate * it. This is achieved in the following manner: * Start at the end and run back until a non-whitespace (nl/blank) * character is encountered. If this is a quote, toss it. * If the *first* character is a quote, bump the start address by * one. No pretense of quote matching here. * Inputs: address in A0, length in D0 * Outputs: address in A0. argtrim: movem.l d1-d7/a1-a6,-(sp) ; save registers cmpi.b #'"',(a0) ; starts with a quote? bne.s checkline ; no subq #1,d0 ; yes - decrement count addq #1,a0 ; and bump start address checkline: cmpi.b #1,D0 ; length includes the newline bne.s isline ; yes - there is something there move.b #0,(a0) ; create null string bra.s argfini ; done isline: ; strip off any run of blanks on the end... move.l a0,a1 ; computing end address of line add.l d0,a1 ; subq #2,a1 ; toploop: cmp.l a0,a1 beq.s empty ; single char or run of blanks cmpi.b #' ',(a1) bne.s endloop ; finished the scan subq #1,a1 ; else back one more bra.s toploop ; and try again endloop: cmpi.b #'"',(a1) beq.s nullit nullnext: addq #1,a1 nullit: move.b #0,(a1) bra.s argfini empty: ; could be blanks or a single char cmpi.b #' ',(a1) bne.s endloop ; or maybe a single quote! move.b #0,(a0) argfini: movem.l (sp)+,d1-d7/a1-a6 ; restore registers rts * PRINT: Passed an EA in A0, and a handle in D1, print the string. print: MOVEM.L D0-D7/A0-A6,-(SP) ; Let's find out how long this string is... JSR strlen ; Addr in A0 returns len in D0 MOVE.L D0,D3 ; length MOVE.L A0,D2 ; Address callsys Write ; Handle already in D1 ; We don't check error returns from write, because there isn't a whole ; lot we can do if console writes fail. MOVEM.L (SP)+,D0-D7/A0-A6 ; Restore registers rts * STRLEN: How long is a piece of string? Pass a string in A0, the * result comes back in D0. strlen: MOVEM.L D1-D7/A0-A6,-(SP) ; Save registers A0, D1 on stack. MOVEQ #0,D0 ; Initialize count STRLOOP: ; Investigate current character. cmpi.b #0,(A0)+ ; Is it a NUL? beq.s STRFINI ADDQ #1,D0 ; Up the count bra STRLOOP STRFINI: MOVEM.L (SP)+,D1-D7/A0-A6 ; Restore registers rts ; Passed a lock in register D1 this routine checks out the file attributes. ; Returns 0 to proceed, 1 if empty, 2 if a directory, and 3 for error. CheckFile: MOVEM.L D1-D7/A0-A6,-(SP) ; Save registers on stack. move.l d1,d4 ; save it for future use ; A lock in D1 and a FileInfoBlock address in D2 are all that are needed. ; We allocmem the FileInfoBlock to assure alignment. move.l #fib_SIZEOF,D0 ; size of the block move.l #MEMF_PUBLIC,D1 ; memory requirement callexec AllocMem ; args D0, D1 returns into D0 move.l d0,a5 ; save it for later beq examnomem ; but did it work? move.l d0,d2 ; fileinfoblock address move.l d4,d1 ; the lock callsys Examine ; see what the lock corresponds to. cmpi.l #FALSE,d0 ; it should work... beq noexam ; but it didn't! ; We have an Examine structure. Now check out if it's a directory or not. ; fib_DirEntryType is >0 for a directory and < 0 for a file tst.l fib_DirEntryType(A5) bgt directory ; it's a directory ; It's just a filename. Let's see how large it is. tst.l fib_Size(A5) beq isempty ; nothing in it -- so we can't read moveq #0,D6 ; the flag character bra examfree * Error & cleanup for this routine examnomem: ; couldn't get memory moveq #3,D6 bra examfini noexam: ; couldn't Examine moveq #3,D6 bra examfree directory: ; it's a directory moveq #2,D6 ; set return code bra examfree ; release memory isempty: ; it's empty moveq #1,D6 ; fall through examfree: ; free the memory here move.l #fib_SIZEOF,D0 ; Address in A1, size in D0 move.l a5,a1 callexec FreeMem ; restore memory examfini: move.l d6,d0 ; set return code MOVEM.L (SP)+,D1-D7/A0-A6 ; Restore registers rts * Data declarations * ------------------------------------------------------------------------- DOSName DOSNAME char DS.B 1 ; scratch S_NOARG DC.B 'Usage: touch file',10,0 S_NOCREATE DC.B 'Failed to create file',10,0 S_NOOPEN DC.B 'Cannot open file',10,0 S_NOREAD DC.B 'Cannot read from file',10,0 S_NOWRITE DC.B 'Cannot write to file',10,0 S_NOSEEK DC.B 'Cannot rewind file',10,0 S_EXAMERR DC.B 'Error in examining file',10,0 S_ISADIR DC.B 'Cannot touch a directory!',10,0 END FUNKYSTUFF echo "\talist.asm" cat <<"FUNKYSTUFF" >alist.asm * ALIST.ASM: An assembler list command. Does the bare minimum directory * listing. Of no real utility, it was mainly written to compare * assembler and C implementations of "trivial" CLI programs. * It's merely the framework with which an assembler variant of * full-blown list could be written (but not by me!). * In case you're interested, it really isn't much faster than * list. I suspect the difference (2-3s on a large c: directory) * is due to list's formatting of the FileInfoBlock and * associated greater console driver use. * However, alist is ~660 bytes long, instead of 8664 !! * Perpetrator: Dewi Williams ..!ihnp4!druca!dewi * Unconditionally placed in the public domain. * (I could make money off of this?!) * Included equate files. * ----------------------------------------------------------------------- NOLIST INCLUDE "exec/types.i" INCLUDE "exec/libraries.i" INCLUDE "libraries/dos.i" INCLUDE "libraries/dosextens.i" INCLUDE "exec/memory.i" LIST * External references * ----------------------------------------------------------------------- EXTERN_LIB OpenLibrary EXTERN_LIB Write EXTERN_LIB Output EXTERN_LIB Examine EXTERN_LIB ExNext EXTERN_LIB Lock EXTERN_LIB UnLock EXTERN_LIB CurrentDir EXTERN_LIB AllocMem EXTERN_LIB FreeMem * Local Equates * ------------------------------------------------------------------------ SysBase EQU 4 ; The one known address in the system. TRUE EQU -1 FALSE EQU 0 * Macros * ------------------------------------------------------------------------ * Calls to dos.library and exec library callsys MACRO CALLLIB _LVO\1 ENDM callexec MACRO LINKLIB _LVO\1,SysBase ENDM * The print routine and argument setup PRINT MACRO lea.l \1(PC),A0 move.l d5,d1 jsr print ENDM * The newline print macro -- called after PRINT so D1 always set up. NL MACRO lea.l NLCHAR(PC),A0 jsr print ENDM * The code segment * ------------------------------------------------------------------------- RORG 0 ; Relocatable code. jsr argtrim ; clean up command line MOVEM.L D0/A0-A2,-(SP) ; Save command line (OpenLibrary trashes). ;------ get Exec's library base pointer: LEA.L DOSName(PC),A1 ; name of dos library move.l #LIBRARY_VERSION,d0 callexec OpenLibrary,SysBase MOVE.L D0,A6 ; Save library pointer (always in A6). BNE.S gotdos ; Should really issue an alert here... moveq #RETURN_FAIL,D0 ; give up bra FINISHED gotdos: ; REGISTER USAGE: ; A0 = scratch after command line used. ; A5 = FileInfoBlock address ; A6 = dos library base pointer ; D1-3 = AmigaDOS scratch argument registers ; D4 = current directory lock ; D5 = output handle ; D6 = root lock flag * Obtain the output handle (needed for write) callsys Output MOVE.L D0,D5 ; Save for the write MOVEM.L (SP)+,D0/A0-A2 ; restore command line etc. * Setup D6 as the restore directory flag moveq #FALSE,D6 * Now check if we have an argument. If we have, attempt to lock it. * Otherwise, go for current directory. cmpi.b #0,(A0) ; a null string for an argument ? beq CURDIR ; yes ; Lock to specified directory. Place name in D1, access mode in D2 move.l a0,d1 move.l #ACCESS_READ,d2 callsys Lock move.l d0,d4 ; remember for later beq nolock ; did we get a lock? bra uselock ; for the Examine CURDIR: ; Get lock to current directory. The most bulletproof way of doing this ; (due to bugs in the V1.1 RAM: handler) is to make the root directory the ; current one, because CurrentDir will return the old lock value. moveq #0,D1 callsys CurrentDir ; got the lock move.l d0,d4 ; for later moveq #TRUE,d6 ; remember to cd uselock: ; A lock in D1 and a FileInfoBlock address in D2 are all that are needed. ; We allocmem the FileInfoBlock to assure alignment. move.l #fib_SIZEOF,D0 ; size of the block move.l #MEMF_PUBLIC,D1 ; memory requirement MOVEM.L D2-D7/A0-A6,-(SP) ; in case AllocMem trashes (it does...) callexec AllocMem ; args D0, D1 returns into D0 MOVEM.L (SP)+,D2-D7/A0-A6 ; restore registers move.l d0,a5 ; save it for later beq nomem ; but did it work? move.l d0,d2 ; fileinfoblock address move.l d4,d1 ; the lock callsys Examine ; see what the lock corresponds to. cmpi.l #FALSE,d0 ; don't need to cd in cleanup code beq noexam ; We have an Examine structure. Now check out if it's a directory or not. ; Incredibly silly things happen if you call ExNext for a file. ; fib_DirEntryType is >0 for a directory and < 0 for a file lea.l fib_DirEntryType(A5),A0 tst.l (a0) bgt again ; It's just a filename. Emit it's name and finish. move.l d5,d1 lea.l fib_FileName(A5),A0 jsr print NL bra cleanup ; get next file again: move.l a5,d2 ; restore fileinfoblock address move.l d4,d1 ; and the lock callsys ExNext cmpi.l #FALSE,d0 beq cleanup ; Should really check with IoErr move.l d5,d1 ; restore output handle lea.l fib_FileName(A5),A0 jsr print NL bra again ; get next file nomem: PRINT S_NOMEM bra afterfree noexam: PRINT S_NOEXAM bra cleanup nolock: PRINT S_NLOCK bra FINISHED cleanup: ; start off by freeing allocated memory. move.l a5,a1 MOVEM.L D1-D7/A0-A6,-(SP) ; in case FreeMem trashes move.l #fib_SIZEOF,D0 callexec FreeMem MOVEM.L (SP)+,D1-D7/A0-A6 ; restore registers afterfree: ; May have to throw away two locks and do a cd here cmpi.l #FALSE,d6 beq nocd move.l d4,d1 callsys CurrentDir ; get back to where we were move.l d0,d1 callsys UnLock move.l #RETURN_OK,D0 bra FINISHED nocd: move.l d4,d1 callsys UnLock move.l #RETURN_OK,D0 FINISHED: rts * Subroutines * ------------------------------------------------------------------------ * argtrim: a routine to trim the end of a command line and null terminate * it. This is achieved in the following manner: * Start at the end and run back until a non-whitespace (nl/blank) * character is encountered. If this is a quote, toss it. * If the *first* character is a quote, bump the start address by * one. No pretense of quote matching here. * Inputs: address in A0, length in D0 * Outputs: address in A0. * This routine is probably overkill for flist. argtrim: movem.l d1-d7/a1-a6,-(sp) ; save registers cmpi.b #'"',(a0) ; starts with a quote? bne.s checkline ; no subq #1,d0 ; yes - decrement count addq #1,a0 ; and bump start address checkline: cmpi.b #1,D0 ; length includes the newline bne.s isline ; yes - there is something there move.b #0,(a0) ; create null string bra.s argfini ; done isline: ; strip off any run of blanks on the end... move.l a0,a1 ; computing end address of line add.l d0,a1 ; subq #2,a1 ; toploop: cmp.l a0,a1 beq.s empty ; single char or run of blanks cmpi.b #' ',(a1) bne.s endloop ; finished the scan subq #1,a1 ; else back one more bra.s toploop ; and try again endloop: cmpi.b #'"',(a1) beq.s nullit nullnext: addq #1,a1 nullit: move.b #0,(a1) bra.s argfini empty: ; could be blanks or a single char cmpi.b #' ',(a1) bne.s endloop ; or maybe a single quote! move.b #0,(a0) argfini: movem.l (sp)+,d1-d7/a1-a6 ; restore registers rts * PRINT: Passed an EA in A0, and a handle in D1, print the string. print: MOVEM.L D0-D7/A0-A6,-(SP) ; Let's find out how long this string is... JSR strlen ; Addr in A0 returns len in D0 MOVE.L D0,D3 ; length MOVE.L A0,D2 ; Address callsys Write ; Handle already in D1 ; We don't check error returns from write, because there isn't a whole ; lot we can do if console writes fail. MOVEM.L (SP)+,D0-D7/A0-A6 ; Restore registers rts * STRLEN: How long is a piece of string? Pass a string in A0, the * result comes back in D0. strlen: MOVEM.L D1-D7/A0-A6,-(SP) ; Save registers A0, D1 on stack. MOVEQ #0,D0 ; Initialize count STRLOOP: ; Investigate current character. cmpi.b #0,(A0)+ ; Is it a NUL? beq.s STRFINI ADDQ #1,D0 ; Up the count bra STRLOOP STRFINI: MOVEM.L (SP)+,D1-D7/A0-A6 ; Restore registers rts * Data declarations * ------------------------------------------------------------------------- DOSName DOSNAME NLCHAR DC.B 10,0 S_NLOCK DC.B 'directory or file name not found',10,0 S_NOEXAM DC.B 'Could not examine directory',10,0 S_NOMEM DC.B 'No memory',10,0 END FUNKYSTUFF echo "\techo.asm" cat <<"FUNKYSTUFF" >echo.asm * ANSIECHO.ASM: Extended echo that handles backslash constructs such as \t * etc. Unlike UNIX echo, it takes \3A hex constructs instead * of octal. I'd rather grow 6 new fingers than lose 2! * * The following backslash constructs are supported: * \t tab * \n newline * \f formfeed (clear screen) * \e escape character * \c on end of string, suppresses newline * \b backspace * \\ single backslash * \" explicit string quote, otherwise eaten. * \NN hexadecimal characters (*must* be upper case). * * As an example, try echo \e[37mhello world\e[0m * * Perpetrator: Dewi Williams ..!ihnp4!druca!dewi * Unconditionally placed in the public domain. * Included equate files. * ----------------------------------------------------------------------- NOLIST INCLUDE "exec/types.i" INCLUDE "exec/libraries.i" INCLUDE "libraries/dos.i" LIST * External references * ----------------------------------------------------------------------- EXTERN_LIB OpenLibrary EXTERN_LIB Write EXTERN_LIB Output * Local Equates * ------------------------------------------------------------------------ SysBase EQU 4 ; The one known address in the system. TRUE EQU -1 FALSE EQU 0 CMD_SIZE EQU 300 ; size of expansion area on stack * Macros * ------------------------------------------------------------------------ * Calls to dos.library and exec library callsys MACRO CALLLIB _LVO\1 ENDM * The code segment * ------------------------------------------------------------------------- RORG 0 ; Relocatable code. LINK A2,#(-CMD_SIZE) ; expand onto stack LEA.L -CMD_SIZE(A2),A1 ; start of expansion buffer jsr argtrim ; clean up command line MOVEM.L D0/A0-A2,-(SP) ; Save command line (OpenLibrary trashes). ;------ get Exec's library base pointer: move.l SysBase,a6 LEA.L DOSName(PC),A1 ; name of dos library move.l #LIBRARY_VERSION,d0 callsys OpenLibrary MOVE.L D0,A6 ; Save library pointer (always in A6). BNE.S gotdos ; Should really issue an alert here... moveq #RETURN_FAIL,D0 ; give up bra FINISHED * Set up loop addressing: A0 goes through command line, A1 references * stack build area, A2 is the frame pointer, D4 is the nonl flag and * D3 is a counter. The command line is terminated by a newline character. gotdos: * Obtain the output handle (needed for write) callsys Output MOVE.L D0,D5 ; Save for the write MOVEM.L (SP)+,D0/A0-A2 ; restore command line etc. MOVEQ #FALSE,D4 ; nonl assumed false as default CLR.L D3 ; zero characters so far mainloop: CMPI.B #0,(A0) ; Reached end of string yet? BEQ eos ; yes CMPI.B #'\',(A0) ; is it escaped? BNE notesc ; no CMPI.B #0,1(A0) ; at the end of the string? BEQ notesc ; not escaping anything, is itself ; Check out the \t \f etc. cases first ADDQ #1,A0 ; move to next character CMPI.B #'t',(A0) ; a tab? BNE next1 ; no MOVE.B #9,(A1)+ ; yes bra gotit next1: cmpi.b #'b',(A0) ; a backspace? bne next2 ; no move.b #8,(A1)+ ; yes bra gotit next2: cmpi.b #'n',(A0) ; a newline? bne next3 ; no move.b #10,(A1)+ ; yes bra gotit next3: cmpi.b #'e',(a0) ; an escape char (not backslash!) bne next4 ; no move.b #27,(A1)+ ; yes bra gotit next4: cmpi.b #'\',(a0) ; an escaped backslash? bne next5 ; no bra notesc ; yes next5: cmpi.b #'f',(a0) ; formfeed bne next6 ; no move.b #12,(A1)+ ; yes bra gotit next6: cmpi.b #'c',(a0) ; no newline construct bne next7 ; no cmpi.b #0,1(A0) ; A \c at the end of the string? bne notesc ; no moveq #TRUE,D4 ; set up the nonl flag bra eos ; know we've finished next7: cmpi.b #'"',(A0) ; a string quote? bne maybehex ; no move.b #'"',(A1)+ ; yes bra gotit ; get here -- maybe it's 2 hex digits. Call ishexdig to find out. maybehex: move.b (a0),D1 jsr ishexdig ; Check the first character beq notesc ; failed the test move.b 1(a0),D1 ; Now check the second jsr ishexdig beq notesc ; second one failed jsr hexstr ; translate it into a real character addq #1,a0 ; lose both characters move.b d0,(A1)+ ; and fall through to gotit. gotit: addq #1,a0 ; next input character addq #1,d3 ; bump output count bra mainloop ; round again notesc: ; A normal character move.b (a0)+,(a1)+ ; copy it over addq #1,d3 ; bump output count bra mainloop ; round again ; Left the loop. eos: TST.L D4 ; Check out the nonl flag BNE skipnl ; set move.b #10,(a1)+ ; add the newline. addq #1,d3 skipnl: ; All set up for printing out the gathered string. Start by recalculating ; the start address of the output buffer. lea.l -CMD_SIZE(A2),A0 move.l a0,d2 ; start address move.l d5,d1 ; restore handle callsys Write MOVEQ #0,D0 ; success FINISHED: UNLK A2 RTS * Subroutines * ------------------------------------------------------------------------ * ishexdig: passed a character in D1, will report as a boolean in D0. * Passes 0..9 and A..F (upper case). ishexdig: cmpi.b #'0',D1 blt hexlab cmpi.b #'9',D1 ble yes hexlab: cmpi.b #'A',D1 blt no cmpi.b #'F',D1 bgt no yes: moveq #TRUE,D0 bra endhex no: moveq #FALSE,D0 endhex: rts * hexstr: passed a hex string in A0, return its value in D0. * Stop either at length 2 or invalid hex character. * In this case, the string has already been validated. * CREDITS: Slightly hacked version of p430 of "Programming the 68000" * by Steve Williams. Sybex books. Recommended. hexstr: movem.l d1-d3/a0,-(sp) ; save starting registers clr.l d0 ; clear accumulator clr.l d3 ; count of digits processed loop: clr.l d1 ; zero out D1 cmpi.b #'9',(a0) ; upper bound bhi notdec ; not a decimal digit cmpi.b #'0',(a0) ; lower bound blt nothex ; not a hex digit move.b #'0',d1 ; correction factor bra gotdig ; accumulate notdec: cmpi.b #'A',(a0) ; check letters blt nothex ; not a hex digit cmpi.b #'F',(a0) ; upper case hex? bhi nothex ; no, and lower case is not allowed move.b #'A'-10,d1 ; correction factor / fall through to gotdig gotdig: clr.l d2 ; zero high byte move.b (a0)+,d2 ; get next digit sub.l d1,d2 ; convert to binary lsl.l #4,d0 ; multiply by 16 add.l d2,d0 ; add in digit cmpi.l #1,d3 ; reached the end yet? beq hexdone ; yes addq.l #1,d3 ; bump the count bra loop ; try another digit hexdone: nothex: movem.l (sp)+,d1-d3/a0 ; unsave registers rts ; return to caller * argtrim: a routine to trim the end of a command line and null terminate * it. This is achieved in the following manner: * Start at the end and run back until a non-whitespace (nl/blank) * character is encountered. If this is a quote, toss it. * If the *first* character is a quote, bump the start address by * one. No pretense of quote matching here. * Inputs: address in A0, length in D0 * Outputs: address in A0. argtrim: movem.l d1-d7/a1-a6,-(sp) ; save registers cmpi.b #'"',(a0) ; starts with a quote? bne.s checkline ; no subq #1,d0 ; yes - decrement count addq #1,a0 ; and bump start address checkline: cmpi.b #1,D0 ; length includes the newline bne.s isline ; yes - there is something there move.b #0,(a0) ; create null string bra.s argfini ; done isline: ; strip off any run of blanks on the end... move.l a0,a1 ; computing end address of line add.l d0,a1 ; subq #2,a1 ; toploop: cmp.l a0,a1 beq.s empty ; single char or run of blanks cmpi.b #' ',(a1) bne.s endloop ; finished the scan subq #1,a1 ; else back one more bra.s toploop ; and try again endloop: cmpi.b #'"',(a1) beq.s nullit nullnext: addq #1,a1 nullit: move.b #0,(a1) bra.s argfini empty: ; could be blanks or a single char cmpi.b #' ',(a1) bne.s endloop ; or maybe a single quote! move.b #0,(a0) argfini: movem.l (sp)+,d1-d7/a1-a6 ; restore registers rts * Data declarations * ------------------------------------------------------------------------- DOSName DOSNAME END FUNKYSTUFF echo "source file creation complete" exit 0 -- ---------------------------------------------------------------------- Dewi Williams. uucp: ..!ihnp4!druca!dewi phone: (303) 538 4884