/*
 * dynamite.h
 *
 * Interface to Dynamite SWIs
 *
 *  1997 Straylight
 */

/*----- Licensing note ----------------------------------------------------*
 *
 * This file is part of Straylight's Dynamite
 *
 * Dynamite is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2, or (at your option)
 * any later version.
 *
 * Dynamite is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with Dynamite.  If not, write to the Free Software Foundation,
 * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 */

#ifndef __dynamite_h
#define __dynamite_h

/*----- Notes -------------------------------------------------------------*
 *
 * The interfaces to Dynamite have been written in assembler.  This has the
 * benefit of making them very small and minimising procedure call overhead.
 * It also has the disadvantage of not setting _kernel_last_oserror()
 * properly.  If this is important, you should use _kernel_swi() directly.
 *
 * The SWI interface routines are safe to call from SVC mode (e.g. in a
 * C module).
 */

#ifdef __cplusplus
  extern "C" {
#endif

/*------ Important types --------------------------------------------------*/

/* --- dynamite_anchor --- *
 *
 * This is the type of a Dynamite anchor.  It should only be used within
 * these declarations, since it exists to keep the compiler happy and make
 * the declarations look neat.  Your actual anchors will have various types
 * depending on what you want to store in your Dynamite blocks.
 *
 * To avoid casts, we cheat horridly, and in the knowledge that we will only
 * be dealing with the *address* of an anchor we macro dynamite_anchor to
 * void, so that its address is a void *.
 */

#define dynamite_anchor void

/* --- dynamite_error --- *
 *
 * This is the type of error which Dynamite SWIs return.  Depending on
 * whether you're using RISC_OSLib or not, you may want these to return
 * os_errors or _kernel_oserrors, or its own special type.  All these error
 * structures have the same format and member names -- it's just a matter of
 * naming the structure.
 *
 * The way we sort all this out is by allowing the client to set up a macro
 * to tell us what to do.
 */

#if defined(dynamite_USE_OS_ERROR)

  #ifndef __os_h
    #include "os.h"
  #endif

  typedef os_error dynamite_error;

#elif defined(dynamite_USE_KERNEL_OSERROR)

  #ifndef __kernel_h
    #include "kernel.h"
  #endif

  typedef _kernel_oserror dynamite_error;

#elif !defined(dynamite_error)

  typedef struct dynamite_error
  {
    int errnum;                         /* Error number */
    char errmess[252];                  /* Error message text */
  }
  dynamite_error;

#endif

/* --- dynstr_blockInfo --- *
 *
 * This structure contains the information dynamite_blockInfo() returns.
 */

typedef struct dynstr_blockInfo
{
  int size;                             /* Block size in bytes */
  int blockID;                          /* Block's ID number */
}
dynstr_blockInfo;

/* --- dynstr_describe --- *
 *
 * This structure contains the information dynamite_describe() returns.
 */

typedef struct dynstr_describe
{
  int area;                             /* Dynamic area handle, or -1 */
  int size;                             /* Total size of Dynamite area */
  int unused;                           /* Space unused in Dynamite area */
}
dynstr_describe;

/*----- Interface functions -----------------------------------------------*
 *
 * Most of these return a pointer to a dynamite_error structure.  If the
 * call was successful, this pointer will be null.
 *
 * However, where errors are unlikely and a value return is more natural,
 * this is not the case.
 *
 * To check for the existance of Dynamite you should check the return value
 * of dynamite_describe() for an error.
 */

/* --- dynamite_alloc --- *
 *
 * Arguments:   anchor == address of the anchor to use
 *              size == the size to allocate, in bytes
 *              id == the id value to give to the block
 *
 * Returns:     Pointer to possible error
 *
 * Use:         Allocates memory from the Dynamite heap.  If successful,
 *              *anchor on exit contains the address of the block allocated.
 *              Note that the anchor must *not* be in application space --
 *              use dynamite_claimAnchor to get one from the RMA if you
 *              don't have other RMA data.
 */

extern dynamite_error *dynamite_alloc(dynamite_anchor */*anchor*/,
                                      int /*size*/,
                                      int /*id*/);

/* --- dynamite_free --- *
 *
 * Arguments:   anchor == the address of block's anchor
 *
 * Returns:     Pointer to possible error
 *
 * Use:         Frees memory in the Dynamite heap.  The memory is marked
 *              as being free, but no blocks are moved -- this makes freeing
 *              lots of blocks very fast.
 */

extern dynamite_error *dynamite_free(dynamite_anchor */*anchor*/);

/* --- dynamite_freeWithID --- *
 *
 * Arguments:   id == the ID of the blocks to free
 *
 * Returns:     Pointer to possible error
 *
 * Use:         Frees all blocks with the given ID value.  This allows
 *              applications and modules to free all their allocated blocks
 *              when they close down.
 */

extern dynamite_error *dynamite_freeWithID(int /*id*/);

/* --- dynamite_blockInfo --- *
 *
 * Arguments:   anchor == address of block's anchor
 *              info == address of structure to fill in
 *
 * Returns:     Pointer to possible error
 *
 * Use:         Returns information about a Dynamite block.
 */

extern dynamite_error *dynamite_blockInfo(dynamite_anchor */*anchor*/,
                                          dynstr_blockInfo */*info*/);

/* --- dynamite_changeID --- *
 *
 * Arguments:   anchor == address of block's anchor, or 0 for all blocks
 *              newID == new ID to set for block or blocks
 *              oldID == optional old ID of blocks to change, if anchor == 0
 *
 * Returns:     Pointer to possible error
 *
 * Use:         Changes the ID of a block or blocks.  If you specify a single
 *              block by passing a nonzero anchor, you don't have to specify
 *              an oldID value.
 */

extern dynamite_error *dynamite_changeID(dynamite_anchor */*anchor*/,
                                         int /*newID*/,...
                                      /* int oldID */);

/* --- dynamite_resize --- *
 *
 * Arguments:   anchor == address of block's anchor
 *              size == new size to make block
 *
 * Returns:     Pointer to possible error
 *
 * Use:         Changes a block's size.  If you make the block larger, the
 *              data will be unchanged.  If you reduce the size, data at the
 *              end will be deleted.  The block will usually move as a
 *              result of this operation.
 */

extern dynamite_error *dynamite_resize(dynamite_anchor */*anchor*/,
                                       int /*size*/);

/* --- dynamite_midExtend --- *
 *
 * Arguments:   anchor == address of block's anchor
 *              at == offset within block at which to insert or remove bytes
 *              by == (signed) number of bytes to insert
 *
 * Returns:     Pointer to possible error
 *
 * Use:         Inserts or removes bytes in a block at a given offset.
 *              See the manual for a complete description of this call.
 */

extern dynamite_error *dynamite_midExtend(dynamite_anchor */*anchor*/,
                                          int /*at*/,
                                          int /*by*/);

/* --- dynamite_save --- *
 *
 * Arguments:   value == value to save on the relocation stack
 *
 * Returns:     Pointer to possible error
 *
 * Use:         Saves a value on the Dynamite relocation stack.  You can
 *              only save one value at a time through this interface.
 */

extern dynamite_error *dynamite_save(void */*value*/);

/* --- dynamite_load --- *
 *
 * Arguments:   --
 *
 * Returns:     The value from the top of the relocation stack.
 *
 * Use:         Loads a value from Dynamite's relocation stack and returns
 *              it.  You can only restore one value at a time through this
 *              interface.
 */

extern void *dynamite_load(void);

/* --- dynamite_reduce --- *
 *
 * Arguments:   --
 *
 * Returns:     0 if heap couldn't be compacted, non-0 if it could
 *
 * Use:         Performs a partial compaction of the Dynamite heap.
 */

extern int dynamite_reduce(void);

/* --- dynamite_compact --- *
 *
 * Arguments:   --
 *
 * Returns:     Pointer to possible error.
 *
 * Use:         Fully compacts the Dynamite heap.  This is equivalent to
 *
 *                while (dynamite_reduce())
 *                  ;
 */

extern dynamite_error *dynamite_compact(void);

/* --- dynamite_lock --- *
 *
 * Arguments:   --
 *
 * Returns:     Pointer to a possible error
 *
 * Use:         Locks the heap, stopping any blocks not explicitly resized
 *              from being moved -- this basically just disables compaction.
 *              You *must* lock the heap while it is being used within a
 *              callback or SWI handler.
 */

extern dynamite_error *dynamite_lock(void);

/* --- dynamite_unlock --- *
 *
 * Arguments:   --
 *
 * Returns:     Pointer to a possible error
 *
 * Use:         Unlocks the heap, allowing compaction to take place again.
 */

extern dynamite_error *dynamite_unlock(void);

/* --- dynamite_claimAnchor --- *
 *
 * Arguments:   ancptr == where to store the address of the anchor
 *
 * Returns:     Pointer to a possible error
 *
 * Use:         Allocates an anchor from the RMA and returns its address.
 */

extern dynamite_error *dynamite_claimAnchor(dynamite_anchor **/*ancptr*/);

/* --- dynamite_releaseAnchor --- *
 *
 * Arguments:   anchor == address of anchor to release
 *
 * Returns:     Pointer to possible error
 *
 * Use:         Frees an anchor allocated by dynamite_claimAnchor.
 */

extern dynamite_error *dynamite_releaseAnchor(dynamite_anchor */*anchor*/);

/* --- dynamite_readSpriteSize --- *
 *
 * Arguments:   --
 *
 * Returns:     Actual size of sprite area in bytes
 *
 * Use:         Returns the real size of the sprite area -- before RISC OS
 *              3.5, Dynamite has to fake the return value from
 *              OS_ReadDynamicArea so that you can no longer work out how
 *              much memory is free in the system.  This call allows you
 *              to find the real sprite area size.
 */

extern int dynamite_readSpriteSize(void);

/* --- dynamite_describe --- *
 *
 * Arguments:   desc == address of structure to fill in, or 0
 *
 * Returns:     Pointer to possible error
 *
 * Use:         If desc is nonzero, this call will read some useful
 *              information about the Dynamite heap.  If desc is 0, it will
 *              return an error if Dynamite is not loaded -- you can
 *              therefore test for Dynamite's presence.
 */

extern dynamite_error *dynamite_describe(dynstr_describe */*desc*/);

/*----- That's all, folks -------------------------------------------------*/

#ifdef __cplusplus
  }
#endif

#endif
