koreth@ssyx.ucsc.edu (Steven Grimm) (09/27/88)
Submitted-by: achowe@watmsg.waterloo.edu (Anthony C. Howe) Posting-number: Volume 1, Issue 75 Archive-name: achlib/part02 tst.b (a0) beq.s .X move.b (a0)+,(a1)+ subq.w #1,d0 bne .1 .X: clr.b (a1) rts ;void strcat( char* s1, char* s2 ) .text _strcat:: .cargs .str1.l, .str2.l move.l .str1(sp), a0 .1: tst.b (a0)+ bne .1 subq.l #1,a0 movea.l .str2(sp),a1 .2: move.b (a1)+,(a0)+ bne .2 rts ;long strlen( char* s ) .text _strlen:: moveq.l #-1,d0 movea.l 4(sp),a0 .1: addq.l #1,d0 tst.b (a0)+ bne .1 rts ;*** Find next token in string. ; ; e long pointer to string / NULL to continue with last string ; long pointer to delimeter string / NULL use default delimeters ; ; x long pointer to start of token / NULL if no tokens left ; ; Note: a) original string will be modified. ; b) leading spaces are skipped. .data DEFDELIM: .dc.b SPACE, ',', 0 .bss _NEXTTOK:: .ds.l 1 .text _STRTOK:: move.l 4(sp), d0 ;get string bne.s .1 ;if NULL then move.l _NEXTTOK, d0 ; continue with previous beq.s .X ; string, return NULL if EOS .1: movea.l d0, a0 .2: cmpi.b #SPACE, (a0)+ ;skip leading spaces beq .2 subq.l #1, a0 move.l a0, d0 ;start of token to return move.l 8(sp), d1 ;get delimeter string bne.s .3 ;if NULL then use default move.l #DEFDELIM, d1 .3: movea.l d1, a1 ;reset delimeter string move.b (a0)+, d2 ;get char in token .4: cmp.b (a1), d2 ;check against delims beq.s .5 tst.b (a1)+ bne .4 ;end of delim string? bra .3 .5: clr.b -1(a0) ;terminate token at delim tst.b d2 ;was it end of search string bne.s .6 suba.l a0, a0 ;save NULL for next search .6: move.l a0, _NEXTTOK ;remember current position .X: rts ;*** Find index of character in string ; ; e long pointer to string ; word character value ; ; x D0.L = index into string if character is found else -1 .text _FINDI:: move.l 4(sp),a0 ;pointer to string moveq.l #-1, d0 move.w 8(sp),d1 ;character .1: tst.b (a0) ;EOS? beq.s .X cmp.b (a0)+, d1 ;check character bne .1 move.l a0, d0 sub.l 4(sp), d0 ;current - original subq.l #1, d0 ;adjust overrun -1 .X: rts ;*** Find index of last occurence of character in string ; ; e long pointer to string ; word character value ; ; x D0.L = index into string if character is found else -1 .text _LFINDI:: movea.l 4(sp), a0 moveq.l #-1, d0 move.w 8(sp), d1 bra.s .2 .1: cmp.b (a0)+, d1 bne.s .2 move.l a0, d0 sub.l 4(sp), d0 subq.l #1, d0 .2: tst.b (a0) bne .1 rts ;*** Deque Rouitines ;*** Make Deque ; ; e long # of long words for deque ; ; x D0.L = ptr to deque structure / NULL unsuccessful .text _MKDEQ:: link a6, #0 movem.l a3-a4, -(sp) move.l 8(a6), d1 lsl.l #2, d1 addi.l #16, d1 move.l d1, -(sp) move.w #$48, -(sp) trap #1 addq.l #6, sp tst.l d0 beq.s .X movea.l d0, a0 movea.l d0, a1 adda.l #16, a1 ;base of deque movea.l a1, a2 ;left pointer movea.l a1, a3 ;right pointer movea.l a1, a4 adda.l d1, a4 ;end of deque +1 movem.l a1-a4, (a0) ;save deque structure .X: movem.l (sp), a3-a4 unlk a6 rts ;*** Remove Deque ; ; e long ptr to deque structure ; ; x D0.W = NULL for success / error code .text _RMDEQ:: move.l 4(sp), -(sp) move.w #$49, -(sp) trap #1 addq.l #6, sp rts ;*** Add an element to deque ; ; e long ptr to deque structure ; word 0 left / 1 right ; long value to store .text _DEQADD:: link a6, #0 movem.l a3-a4, -(sp) movea.l 8(a6), a0 ;ptr to deque structure movem.l (a0), a1-a4 ;fetch deque structure move.l 14(a6), d0 ;value move.w 12(a6), d1 ;if adding to the right bne.s .2 cmpa.l a1, a2 bhi.s .1 ;if begin < left then movea.l a4, a2 ;wrap deque to end .1: move.l d0, -(a2) ;save value bra.s .X .2: move.l d0, (a3)+ ;save value cmpa.l a3, a4 ;if right < end then bhi.s .X movea.l a1, a3 ;wrap deque to beginning .X: movem.l a1-a4, (a0) ;update deque structure movem.l (sp), a3-a4 unlk a6 rts ;*** Delete value from deque ; ; e long ptr to deque structure ; word 0 left / 1 right ; ; x D0.L value .text _DEQDEL:: link a6, #0 movem.l a3-a4, -(sp) movea.l 8(a6), a0 movem.l (a0), a1-a4 move.w 12(a6), d1 bne.s .1 move.l (a2)+, d0 ;get value cmpa.l a2, a4 ;if left < end then bhi.s .X movea.l a1, a2 ;wrap deque to beginning bra.s .X .1: cmpa.l a1, a3 ;if begin < right then bhi.s .2 movea.l a4, a3 ;wrap deque to end .2: move.l -(a3), d0 ;get value .X: movem.l a1-a4, (a0) ;update deque structure movem.l (sp), a3-a4 unlk a6 rts .globl _PUTDEC .globl PUTDEC ;*** Print Unsigned Decimal (0-65535) ; ; _PUTDEC e word value ; PUTDEC e D0.W value ; x none .text _PUTDEC: move.w 4(sp),d0 PUTDEC: ext.l d0 divu #10,d0 beq.s PUTDEC1 move.l d0,-(sp) bsr PUTDEC move.l (sp)+,d0 PUTDEC1: swap d0 addi.w #$30,d0 move.w d0,-(sp) move.w #CCONOUT,-(sp) trap #1 addq.l #4,sp rts ;*** Print Unsigned Decimal ; ; _PUTLONG e long value ; PUTLONG e D0.L value ; x none .text _PUTLONG:: move.l 4(sp),d0 PUTLONG:: move.l #10, -(sp) move.l d0, -(sp) bsr.s _ULD addq.l #8, sp tst.l d0 beq.s .1 movem.l d0-d1,-(sp) bsr PUTLONG movem.l (sp)+, d0-d1 .1: addi.w #$30, d1 move.w d1,-(sp) move.w #CCONOUT,-(sp) trap #1 addq.l #4,sp rts ;*** Unsigned Long to ASCII ; ; e long value ; long pointer to string buffer ; ; x ASCIIZ string in buffer .text _ULTOA:: move.l 4(sp),d0 movea.l 8(sp), a0 ULTOA:: move.l #10, -(sp) move.l d0, -(sp) bsr.s _ULD addq.l #8, sp tst.l d0 beq.s .1 movem.l d0-d1,-(sp) bsr ULTOA movem.l (sp)+, d0-d1 .1: addi.w #$30, d1 move.b d1, (a0)+ clr.b (a0) rts ;*** Unsigned long division ; ; e long dividend ; long divisor ; ; x D0.L = quoitent ; D1.L = remainder .text _ULD:: move.l 4(sp), d0 ;dividend move.l 8(sp), d2 ;divisor move.w d3, -(sp) move.w #31, d3 ;bit count clr.l d1 ;remainder .1: add.l d0, d0 addx.l d1, d1 sub.l d2, d1 bcc.s .2 add.l d2, d1 bra.s .3 .2: addq.l #1, d0 .3: dbf d3, .1 move.w (sp)+, d3 rts ;*** DOS Function Call (CP/M-68k) ; ; e word function number ; long parameter ; ; x D0.L = returned value .text ___BDOS:: move.w 4(sp),d0 ; get function number move.l 8(sp),d1 ; get parameter trap #2 ; call DOS rts .globl _gemdos ;*** GEM Dos Function Call ; ; e word function number ; word / long parameters ; ; x D0 = return value if any ; ; See GEM Dos notes .text _gemdos: move.l (sp)+, GEMRET trap #1 move.l GEMRET, -(sp) rts .bss GEMRET: .ds.l 1 .globl _xbios ;*** XBIOS Function Call ; ; e word function number ; word / long parameters ; ; x D0 = return value if any ; ; See XBIOS notes .text _xbios: move.l (sp)+, XBRET trap #14 move.l XBRET, -(sp) rts .bss XBRET: .ds.l 1 @\Rogue\Monster\ else echo "shar: Will not over write ACHSLIB.S" fi if `test ! -s LIST.C` then echo "x - LIST.C" cat > LIST.C << '@\Rogue\Monster\' /* line.c 880821 ACH */ #include "runtime.h" void InsBefore( list, curr, new ) cell* list; cell* curr; cell* new; { cell* prev; if( !new ) return; ++list->x; if( curr ){ prev = curr->prev; curr->prev = new; } else { prev = list->prev; list->prev = new; } new->prev = prev; new->next = curr; if( prev ) prev->next = new; else list->next = new; } void InsAfter( list, curr, new ) cell* list; cell* curr; cell* new; { cell* next; if( !new ) return; ++list->x; if( curr ){ next = curr->next; curr->next = new; } else { next = list->next; list->next = new; } new->prev = curr; new->next = next; if( next ) next->prev = new; else list->prev = new; } void DelCurr( list, curr ) cell* list; cell* curr; { cell* next; cell* prev; if( !curr ) return; --list->x; prev = curr->prev; next = curr->next; if( prev ) prev->next = next; else list->next = next; if( next ) next->prev = prev; else list->prev = prev; } @\Rogue\Monster\ else echo "shar: Will not over write LIST.C" fi if `test ! -s MAKEFILE` then echo "x - MAKEFILE" cat > MAKEFILE << '@\Rogue\Monster\' mtest.prg: achapp.o mtest.o list.o memory.o achslib.o aln -c d:mtest.lnk achapp.o: achapp.s mac -u -s -v -oachapp.o achapp.s achslib.o: achslib.s mac -u -s -v -oachslib.o achslib.s list.o: list.c cc -c list.c mtest.o: mtest.c cc -c mtest.c memory.o: memory.c cc -c memory.c @\Rogue\Monster\ else echo "shar: Will not over write MAKEFILE" fi if `test ! -s MEMORY.C` then echo "x - MEMORY.C" cat > MEMORY.C << '@\Rogue\Monster\' /* memory.c Anthony's Memory Management Routines 880824 ACH */ #include "runtime.h" /* BLOCK_SIZE is the size of heaps to be requested from the system. This value should be even and in the range 2 <= BLOCK_SIZE <= 2^n-1 -2. */ #define BLOCK_SIZE 65534 #define BLOCK_SIZE_L 65534L cell Avail = { 0, NIL, NIL }; unsigned CellsUsed = 0; unsigned TotalHeaps = 0; static bool Mavail( curr ) /* Return TRUE if cell is available. */ cell* curr; { if( !curr ) return( FALSE ); return( !(curr->x & 1) ); } static cell* Madjcent( curr ) /* Return address of right adjcent cell. */ cell* curr; { if( curr ) return( (cell*) ((char*) curr + (curr->x & ~1)) ); return( NIL ); } static cell* Mcore() /* Allocate another heap block from the system and add to free list. Return pointer to new block or NIL if out of system memory. */ { cell* p; p = (cell*) SysAlloc( BLOCK_SIZE_L ); if( p ){ p->x = BLOCK_SIZE | 1; ++CellsUsed; Mdispose( (cell*) ((char*) p + sizeof( unsigned )) ); ++TotalHeaps; } return( p ); } static bool Mrelease( curr ) /* Return TRUE is a heap block was released. */ cell* curr; { if( TotalHeaps && curr && curr->x == BLOCK_SIZE ){ DelCurr( &Avail, curr ); SysFree( curr ); --TotalHeaps; return( TRUE ); } return( FALSE ); } bool Mfini() /* We free up all that is left of the heaps. We assume that the programmer did all the necessary clean-up so that all the cells merged into heaps. Return TRUE if all heaps and cells where released. */ { cell* p; for( p = Avail.next; p; p = p->next ) Mrelease( p ); return( !(CellsUsed && TotalHeaps) ); } void* Mnew( request ) /* Return pointer to requested memory. If NIL returned then unsuccessful memory request. The range for requests is 1 to BLOCK_SIZE. */ unsigned request; { cell* p; unsigned amount; unsigned left; /* You don't get anything for nothing. */ if( !request ) return( NIL ); /* Word align our request, so we always ask for even sizes. */ amount = (request + sizeof( unsigned ) +1) & ~1; /* Check for overflow. */ if( amount <= request || amount > BLOCK_SIZE ) return( NIL ); /* Min request possible since we have to be able to store pointers back into the block when it is later freed. */ if( amount < sizeof( cell ) ) amount = sizeof( cell ); /* assert: sizeof( cell ) <= amount <= BLOCK_SIZE */ /* Traverse free list for first fit. */ for( p = Avail.next; p; p = p->next ){ if( Mrelease( p ) ) continue; /* assert: sizeof( cell ) <= p->x <= BLOCK_SIZE */ if( p->x >= amount ){ allocate: left = p->x - amount; if( left < sizeof( cell ) ) DelCurr( &Avail, p ); else { /* Split block and return upper half. */ p->x = left; p = Madjcent( p ); p->x = amount; } /* Mark block as used. */ p->x |= 1; ++CellsUsed; return( (void*) ((char*) p + sizeof( unsigned )) ); } } p = Mcore(); if( p ){ /* assert: p->x = BLOCK_SIZE >= amount */ goto allocate; } return( NIL ); } void Mdispose( curr ) /* Free cell, return to free list and try and merge with neighbors. */ cell* curr; { cell* p; cell* q; if( !curr ) return; curr = (cell*) ((char*) curr - sizeof( unsigned )); /* Mark block as available. */ curr->x &= ~1; --CellsUsed; for( p = Avail.next; p < curr && p; p = p->next ); InsBefore( &Avail, p, curr ); if( Madjcent( curr ) == p && curr->x != BLOCK_SIZE ){ /* Merge right neighbor. */ curr->x += p->x; DelCurr( &Avail, p ); } q = curr->prev; if( Madjcent( q ) == curr && q->x != BLOCK_SIZE ){ /* Merge left neighbor. */ q->x += curr->x; DelCurr( &Avail, curr ); } } @\Rogue\Monster\ else echo "shar: Will not over write MEMORY.C" fi if `test ! -s MTEST.C` then echo "x - MTEST.C" cat > MTEST.C << '@\Rogue\Monster\' #include <stdio.h> #include "runtime.h" void main( argc, argv ) int argc; char** argv; { char** p; char* a; char* b; char* c; char* d; int i; printf( "Check upper limit (max should be 65532)\n" ); a = Mnew( (unsigned) 65531 ); b = Mnew( (unsigned) 65532 ); c = Mnew( (unsigned) 65533 ); d = Mnew( (unsigned) 65534 ); printf( "a= %lx, b= %lx, c= %lx, d= %lx. C and D should be zero.\n", a,b,c,d ); Mdispose( a ); Mdispose( b ); printf( "Copy Argv Test\n\nAllocating p-array\n" ); p = (char*) Mnew( sizeof( char* ) * argc ); printf( "p = %lx\n", p ); for( i = 0; i < argc; ++i ){ printf( "Allocating space for '%s'\n", argv[ i ] ); p[ i ] = (char*) Mnew( strlen( argv[i] ) +1 ); strcpy( p[ i ], argv[ i ] ); printf( "Our copy p[%d] = %lx '%s'\n", i, p[ i ], p[ i ] ); } for( i = 0; i < argc; ++i ){ printf( "Releasing p[%d]\n", i ); Mdispose( p[i] ); } printf( "Releasing p-array\n" ); Mdispose( p ); Mfini(); } @\Rogue\Monster\ else echo "shar: Will not over write MTEST.C" fi if `test ! -s MTEST.LNK` then echo "x - MTEST.LNK" cat > MTEST.LNK << '@\Rogue\Monster\' -s -v -u -o mtest.prg achapp.o mtest.o list.o memory.o achslib.o gemlib @\Rogue\Monster\ else echo "shar: Will not over write MTEST.LNK" fi if `test ! -s README` then echo "x - README" cat > README << '@\Rogue\Monster\' readme 88.09.20 files in archive: achacc.s Anthony's MADMAC Assembler Library for Acylon C achapp.s achslib.h achslib.s list.c Doubly Link List Routines - InsBefore, InsAfter, DelCurr memory.c Memory Management Routines - Mnew, Mdispose, Mfini runtime.h Data types and funtion prototypes. makefile Make file for mtest.prg mtest.c Memory management test program. mtest.lnk Aln link file readme This file. I've provided my assembler library for those who still use Acylon C (like me). It has been modified since the last release. Basically adopting standard C library names and parameter conventions. List.c provides generic doubly link list routines that are used by Memory.c to mantain the free list. Runtime.h contains all the data types used and function prototypes. Reference for that helped in the development of Memory.c: 1) Knuth vol 1, pg 435-451 2) Kernighan & Ritchie "The C Progamming Language" pg 173-177 Memory.c uses unsigned int which limits memory requests to Mnew in the range 1-65534. This is because Acylon C doesn't support unsigned long. Also the application for which this was written did not require large memory blocks. The source could be modified to fix this limitation if compiled under MWC or GNU CC. Notes: 1) All source used tabstops = 4 2) Development site was Colour Mega ST 2 3) Development software: BDT Micro C-Shell, BDT Make, LV, Acylon C, MadMac Assembler, Aln Linker Any inquires send mail to: achowe@watmsg.waterloo.edu Disclaimer: The software provided has been written out of need and a desire to learn. Everything has been tested, yet things can go wrong (ie. typos). I refuse to take any responsablity for damage that could have resulted from the use of this code. I have enough trouble finding a decent pizza at 1 am. PLEASE PLEASE test code independantly till you get that warm feeling like when you p__s in a pool :) Anthony C. Howe @\Rogue\Monster\ else echo "shar: Will not over write README" fi if `test ! -s RUNTIME.H` then echo "x - RUNTIME.H" cat > RUNTIME.H << '@\Rogue\Monster\' /* runtime.h 880821 ACH */ #undef ANSI #define ATARI #define ACYLON #define SCREEN_WIDTH 80 #define TAB_WIDTH 4 #ifndef ANSI typedef int void; #endif #ifdef ATARI #include <gembind.h> #include <gemdefs.h> #include <osbind.h> #include <tosdefs.h> #include <vdibind.h> #include "achslib.h" #define putch(c) (Cconout(c)) #define Key() (Cconin()) #define SysAlloc(x) (Malloc(x)) #define SysFree(x) (Mfree(x)) #define READ 0 #define WRITE 1 #define READ_WRITE 2 typedef struct dta { char reserved[21]; char attr; short time; short date; long size; char name[14]; } dta; #else #define READ "r" #define WRITE "w" #define READ_WRITE "rw" #endif #undef FALSE #define FALSE 0 #undef TRUE #define TRUE (!FALSE) #undef NULL #define NULL 0 #undef NIL #define NIL 0L typedef int bool; typedef struct cell { unsigned x; struct cell* prev; struct cell* next; } cell; #ifdef ANSI /* list.c */ extern void InsBefore( cell* list, cell* curr, cell* new ); extern void InsAfter( cell* list, cell* curr, cell*, new ); extern void DelCurr( cell* list, cell* curr ); /* memory.c */ extern bool Mfini(); extern void* Mnew( unsigned request ); extern void Mdispose( cell* curr ); #else /* list.c */ extern void InsBefore(); extern void InsAfter(); extern void DelCurr(); extern cell* Lalloc(); extern void Lfree(); extern void Lfini(); /* memory.c */ extern bool Mfini(); extern void* Mnew(); extern void Mdispose(); #endif @\Rogue\Monster\ else echo "shar: Will not over write RUNTIME.H" fi # to concatenate archives, remove anything after this line exit 0