[comp.sources.atari.st] v01i075: achlib -- Memory management and other routines for MadMac part02/02

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