This is the open source Pawn interpreter ported to mbed. See here: http://www.compuphase.com/pawn/pawn.htm and here: http://code.google.com/p/pawnscript/

Dependents:   Pawn4Test

Some instructions:

  • Put the attached include folder next to your source, so when you compile you get all the proper definitions
  • Use the attached main.p as a starting point if you wish
  • Compile your main.p into main.amx - Put your main.amx on the mbed 'drive'
  • Reset and be amazed.

Important Compile Notes:

  • You should use the -S# option to define a smaller default stack size. Start with -S64 and go up from there if needed.
  • To use on the Cortex-M0 version of the mbed (LPC11U24), you MUST include the TARGET=3 command-line option as well, so the pin names are properly defined. In the future this may be handled on the native code side.

Known Issues:

  • At the moment it appears the kbhit() function is not working right - at least on my mac. Will continue testing on Windows. Working fine.

Todo:

  • Add more wrappers for the mbed peripherals
  • Add Pawn overlay support, to allow much larger scripts to run (even on the LPC11U24)

amxpool.c

Committer:
Lobo
Date:
2013-05-24
Revision:
3:185fdbb7ccf0
Parent:
0:3ab1d2d14eb3

File content as of revision 3:185fdbb7ccf0:

/*  Simple allocation from a memory pool, with automatic release of
 *  least-recently used blocks (LRU blocks).
 *
 *  These routines are as simple as possible, and they are neither re-entrant
 *  nor thread-safe. Their purpose is to have a standard implementation for
 *  systems where overlays are used and malloc() is not available.
 *
 *  The algorithm uses a first-fit strategy. It keeps all blocks in a single
 *  list (both used blocks and free blocks are in the same list). Every memory
 *  block must have a unique number that identifies the block. This unique
 *  number allows to search for the presence of the block in the pool and for
 *  "conditional allocation".
 *
 *
 *  Copyright (c) ITB CompuPhase, 2007-2012
 *
 *  Licensed under the Apache License, Version 2.0 (the "License"); you may not
 *  use this file except in compliance with the License. You may obtain a copy
 *  of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 *  WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 *  License for the specific language governing permissions and limitations
 *  under the License.
 *
 *  Version: $Id: amxpool.c 4731 2012-06-21 11:11:18Z thiadmer $
 */
#include <assert.h>
#include "amx.h"
#include "amxpool.h"

#if !defined NULL
  #define NULL  ((void*)0)
#endif

#define MIN_BLOCKSIZE 32
#define PROTECT_LRU   0xffff

typedef struct tagARENA {
  unsigned blocksize;
  short index;      /* overlay index, -1 if free */
  unsigned short lru;
} ARENA;

static void *pool_base;
static unsigned pool_size;
static unsigned short pool_lru;

static void touchblock(ARENA *hdr);
static ARENA *findblock(int index);

/* amx_poolinit() initializes the memory pool for the allocated blocks.
 * If parameter pool is NULL, the existing pool is cleared (without changing
 * its position or size).
 */
void amx_poolinit(void *pool, unsigned size)
{
  assert(pool!=NULL || pool_base!=NULL);
  if (pool!=NULL) {
    assert(size>sizeof(ARENA));
    /* save parameters in global variables, then "free" the entire pool */
    pool_base=pool;
    pool_size=size;
  } /* if */
  pool_lru=0;
  amx_poolfree(NULL);
}

/* amx_poolfree() releases a block allocated earlier. The parameter must have
 * the same value as that returned by an earlier call to amx_poolalloc(). That
 * is, the "block" parameter must point directly behind the arena header of the
 * block.
 * When parameter "block" is NULL, the pool is re-initialized (meaning that
 * all blocks are freed).
 */
void amx_poolfree(void *block)
{
  ARENA *hdr,*hdr2;
  unsigned sz;

  assert(pool_base!=NULL);
  assert(pool_size>sizeof(ARENA));

  /* special case: if "block" is NULL, create a single free space */
  if (block==NULL) {
    /* store an arena header at the start of the pool */
    hdr=(ARENA*)pool_base;
    hdr->blocksize=pool_size-sizeof(ARENA);
    hdr->index=-1;
    hdr->lru=0;
  } else {
    hdr=(ARENA*)((char*)block-sizeof(ARENA));
    assert((char*)hdr>=(char*)pool_base && (char*)hdr<(char*)pool_base+pool_size);
    assert(hdr->blocksize<pool_size);

    /* free this block */
    hdr->index=-1;

    /* try to coalesce with the next block */
    hdr2=(ARENA*)((char*)hdr+hdr->blocksize+sizeof(ARENA));
    if (hdr2->index==-1)
      hdr->blocksize+=hdr2->blocksize+sizeof(ARENA);

    /* try to coalesce with the previous block */
    if ((void*)hdr!=pool_base) {
      sz=pool_size;
      hdr2=(ARENA*)pool_base;
      while (sz>0 && (char*)hdr2+hdr2->blocksize+sizeof(ARENA)!=(char*)hdr) {
        assert(sz<=pool_size);
        sz-=hdr2->blocksize+sizeof(ARENA);
        hdr2=(ARENA*)((char*)hdr2+hdr2->blocksize+sizeof(ARENA));
      } /* while */
      assert((char*)hdr2+hdr2->blocksize+sizeof(ARENA)==(char*)hdr);
      if (hdr2->index==-1)
        hdr2->blocksize+=hdr->blocksize+sizeof(ARENA);
    } /* if */
  } /* if */
}

/* amx_poolalloc() allocates the requested number of bytes from the pool and
 * returns a header to the start of it. Every block in the pool is prefixed
 * with an "arena header"; the return value of this function points just
 * behind this arena header.
 *
 * The block with the specified "index" should not already exist in the pool.
 * In other words, parameter "index" should be unique for every of memory block,
 * and the block should not change in size. Use amx_poolfind() to verify whether
 * a block is already in the pool (and optionally amx_poolfree() to remove it).
 *
 * If no block of sufficient size is available, the routine frees blocks until
 * the requested amount of memory can be allocated. There is no intelligent
 * algorithm involved: the routine just frees the least-recently used block at
 * every iteration (without considering the size of the block or whether that
 * block is adjacent to a free block).
 */
void *amx_poolalloc(unsigned size,int index)
{
  ARENA *hdr,*hdrlru;
  unsigned sz;
  unsigned short minlru;

  assert(size>0);
  assert(index>=0 && index<=SHRT_MAX);
  assert(findblock(index)==NULL);

  /* align the size to a cell boundary */
  if ((size % sizeof(cell))!=0)
    size+=sizeof(cell)-(size % sizeof(cell));
  if (size+sizeof(ARENA)>pool_size)
    return NULL;  /* requested block does not fit in the pool */

  /* find a block large enough to get the size plus an arena header; at
   * the same time, detect the block with the lowest LRU
   * if no block of sufficient size can be found, the routine then frees
   * the block with the lowest LRU count and tries again
   */
  do {
    sz=pool_size;
    hdr=(ARENA*)pool_base;
    hdrlru=hdr;
    minlru=USHRT_MAX;
    while (sz>0) {
      assert(sz<=pool_size);
      assert((char*)hdr>=(char*)pool_base && (char*)hdr<(char*)pool_base+pool_size);
      if (hdr->index==-1 && hdr->blocksize>=size)
        break;
      if (hdr->index!=-1 && hdr->lru<minlru) {
        minlru=hdr->lru;
        hdrlru=hdr;
      } /* if */
      sz-=hdr->blocksize+sizeof(ARENA);
      hdr=(ARENA*)((char*)hdr+hdr->blocksize+sizeof(ARENA));
    } /* while */
    assert(sz<=pool_size);
    if (sz==0) {
      /* free up memory and try again */
      assert(hdrlru->index!=-1);
      amx_poolfree((char*)hdrlru+sizeof(ARENA));
    } /* if */
  } while (sz==0);

  /* see whether to allocate the entire free block, or to cut it in two blocks */
  if (hdr->blocksize>size+MIN_BLOCKSIZE+sizeof(ARENA)) {
    /* cut the block in two */
    ARENA *next=(ARENA*)((char*)hdr+size+sizeof(ARENA));
    next->blocksize=hdr->blocksize-size-sizeof(ARENA);
    next->index=-1;
    next->lru=0;
  } else {
    size=hdr->blocksize;
  } /* if */
  hdr->blocksize=size;
  hdr->index=(short)index;
  touchblock(hdr);    /* set LRU field */

  return (void*)((char*)hdr+sizeof(ARENA));
}

/* amx_poolfind() returns the address of the memory block with the given index,
 * or NULL if no such block exists. Parameter "index" should not be -1, because
 * -1 represents a free block (actually, only positive values are valid).
 * When amx_poolfind() finds the block, it increments its LRU count.
 */
void *amx_poolfind(int index)
{
  ARENA *hdr=findblock(index);
  if (hdr==NULL)
    return NULL;
  touchblock(hdr);
  return (void*)((char*)hdr+sizeof(ARENA));
}

int amx_poolprotect(int index)
{
  ARENA *hdr=findblock(index);
  if (hdr==NULL)
    return AMX_ERR_GENERAL;
  hdr->lru=PROTECT_LRU;
  return AMX_ERR_NONE;
}

static ARENA *findblock(int index)
{
  ARENA *hdr;
  unsigned sz;

  assert(index>=0);
  sz=pool_size;
  hdr=(ARENA*)pool_base;
  while (sz>0 && hdr->index!=index) {
    assert(sz<=pool_size);
    assert((char*)hdr>=(char*)pool_base && (char*)hdr<(char*)pool_base+pool_size);
    sz-=hdr->blocksize+sizeof(ARENA);
    hdr=(ARENA*)((char*)hdr+hdr->blocksize+sizeof(ARENA));
  } /* while */
  assert(sz<=pool_size);
  return (sz>0 && hdr->index==index) ? hdr : NULL;
}

static void touchblock(ARENA *hdr)
{
  assert(hdr!=NULL);
  if (++pool_lru >= PROTECT_LRU)
    pool_lru=0;
  hdr->lru=pool_lru;

  /* special case: if the overlay LRU count wrapped back to zero, set the
   * LRU count of all blocks to zero, but set the count of the block just
   * touched to 1 (skip blocks marked as protected, too)
   */
  if (pool_lru==0) {
    ARENA *hdr2;
    unsigned sz=pool_size;
    hdr2=(ARENA*)pool_base;
    while (sz>0) {
      assert(sz<=pool_size);
      if (hdr2->lru!=PROTECT_LRU)
        hdr2->lru=0;
      sz-=hdr2->blocksize+sizeof(ARENA);
      hdr2=(ARENA*)((char*)hdr2+hdr2->blocksize+sizeof(ARENA));
    } /* while */
    assert(sz==0);
    hdr->lru=++pool_lru;
  } /* if */
}