* 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