code:
#include "malloc_opts.h"
#include "ram.h"

/*
  Default header file for malloc-2.8.x, written by Doug Lea
  and released to the public domain, as explained at
  http://creativecommons.org/licenses/publicdomain.

  last update: Mon Aug 15 08:55:52 2005  Doug Lea  (dl at gee)

  This header is for ANSI C/C++ only.  You can set any of
  the following #defines before including:

  * If USE_DL_PREFIX is defined, it is assumed that malloc.c
    was also compiled with this option, so all routines
    have names starting with "dl".

  * If HAVE_USR_INCLUDE_MALLOC_H is defined, it is assumed that this
    file will be #included AFTER <malloc.h>. This is needed only if
    your system defines a struct mallinfo that is incompatible with the
    standard one declared here.  Otherwise, you can include this file
    INSTEAD of your system system <malloc.h>.  At least on ANSI, all
    declarations should be compatible with system versions

  * If MSPACES is defined, declarations for mspace versions are included.
*/

#ifndef MALLOC_280_H
#define MALLOC_280_H

#ifdef __cplusplus
extern "C" {
#endif

#include <stddef.h>   /* for size_t */

#if !ONLY_MSPACES

#ifndef USE_DL_PREFIX
#define dlcalloc               calloc
#define dlfree                 free
#define dlmalloc               malloc
#define dlmemalign             memalign
#define dlrealloc              realloc
#define dlvalloc               valloc
#define dlpvalloc              pvalloc
#define dlmallinfo             mallinfo
#define dlmallopt              mallopt
#define dlmalloc_trim          malloc_trim
#define dlmalloc_stats         malloc_stats
#define dlmalloc_usable_size   malloc_usable_size
#define dlmalloc_footprint     malloc_footprint
#define dlindependent_calloc   independent_calloc
#define dlindependent_comalloc independent_comalloc
#endif /* USE_DL_PREFIX */

/*
  malloc(size_t n)
  Returns a pointer to a newly allocated chunk of at least n bytes, or
  null if no space is available, in which case errno is set to ENOMEM
  on ANSI C systems.

  If n is zero, malloc returns a minimum-sized chunk. (The minimum
  size is 16 bytes on most 32bit systems, and 32 bytes on 64bit
  systems.)  Note that size_t is an unsigned type, so calls with
  arguments that would be negative if signed are interpreted as
  requests for huge amounts of space, which will often fail. The
  maximum supported value of n differs across systems, but is in all
  cases less than the maximum representable value of a size_t.
*/
void* dlmalloc(size_t);

/*
  free(void* p)
  Releases the chunk of memory pointed to by p, that had been previously
  allocated using malloc or a related routine such as realloc.
  It has no effect if p is null. If p was not malloced or already
  freed, free(p) will by default cuase the current program to abort.
*/
void  dlfree(void*);

/*
  calloc(size_t n_elements, size_t element_size);
  Returns a pointer to n_elements * element_size bytes, with all locations
  set to zero.
*/
void* dlcalloc(size_t, size_t);

/*
  realloc(void* p, size_t n)
  Returns a pointer to a chunk of size n that contains the same data
  as does chunk p up to the minimum of (n, p's size) bytes, or null
  if no space is available.

  The returned pointer may or may not be the same as p. The algorithm
  prefers extending p in most cases when possible, otherwise it
  employs the equivalent of a malloc-copy-free sequence.

  If p is null, realloc is equivalent to malloc.

  If space is not available, realloc returns null, errno is set (if on
  ANSI) and p is NOT freed.

  if n is for fewer bytes than already held by p, the newly unused
  space is lopped off and freed if possible.  realloc with a size
  argument of zero (re)allocates a minimum-sized chunk.

  The old unix realloc convention of allowing the last-free'd chunk
  to be used as an argument to realloc is not supported.
*/

void* dlrealloc(void*, size_t);

/*
  memalign(size_t alignment, size_t n);
  Returns a pointer to a newly allocated chunk of n bytes, aligned
  in accord with the alignment argument.

  The alignment argument should be a power of two. If the argument is
  not a power of two, the nearest greater power is used.
  8-byte alignment is guaranteed by normal malloc calls, so don't
  bother calling memalign with an argument of 8 or less.

  Overreliance on memalign is a sure way to fragment space.
*/
void* dlmemalign(size_t, size_t);

/*
  valloc(size_t n);
  Equivalent to memalign(pagesize, n), where pagesize is the page
  size of the system. If the pagesize is unknown, 4096 is used.
*/
void* dlvalloc(size_t);

/*
  mallopt(int parameter_number, int parameter_value)
  Sets tunable parameters The format is to provide a
  (parameter-number, parameter-value) pair.  mallopt then sets the
  corresponding parameter to the argument value if it can (i.e., so
  long as the value is meaningful), and returns 1 if successful else
  0.  SVID/XPG/ANSI defines four standard param numbers for mallopt,
  normally defined in malloc.h.  None of these are use in this malloc,
  so setting them has no effect. But this malloc also supports other
  options in mallopt:

  Symbol            param #  default    allowed param values
  M_TRIM_THRESHOLD     -1   2*1024*1024   any   (-1U disables trimming)
  M_GRANULARITY        -2     page size   any power of 2 >= page size
  M_MMAP_THRESHOLD     -3      256*1024   any   (or 0 if no MMAP support)
*/
int dlmallopt(int, int);

#define M_TRIM_THRESHOLD     (-1)
#define M_GRANULARITY        (-2)
#define M_MMAP_THRESHOLD     (-3)

/*
  malloc_footprint();
  Returns the number of bytes obtained from the system.  The total
  number of bytes allocated by malloc, realloc etc., is less than this
  value. Unlike mallinfo, this function returns only a precomputed
  result, so can be called frequently to monitor memory consumption.
  Even if locks are otherwise defined, this function does not use them,
  so results might not be up to date.
*/
size_t dlmalloc_footprint();

#ifndef NO_MALLINFO
/*
  mallinfo()
  Returns (by copy) a struct containing various summary statistics:

  arena:     current total non-mmapped bytes allocated from system
  ordblks:   the number of free chunks
  smblks:    always zero.
  hblks:     current number of mmapped regions
  hblkhd:    total bytes held in mmapped regions
  usmblks:   the maximum total allocated space. This will be greater
                than current total if trimming has occurred.
  fsmblks:   always zero
  uordblks:  current total allocated space (normal or mmapped)
  fordblks:  total free space
  keepcost:  the maximum number of bytes that could ideally be released
               back to system via malloc_trim. ("ideally" means that
               it ignores page restrictions etc.)

  Because these fields are ints, but internal bookkeeping may
  be kept as longs, the reported values may wrap around zero and
  thus be inaccurate.
*/
#ifndef HAVE_USR_INCLUDE_MALLOC_H
#ifndef _MALLOC_H
#ifndef MALLINFO_FIELD_TYPE
#define MALLINFO_FIELD_TYPE size_t
#endif /* MALLINFO_FIELD_TYPE */
struct mallinfo {
  MALLINFO_FIELD_TYPE arena;    /* non-mmapped space allocated from system */
  MALLINFO_FIELD_TYPE ordblks;  /* number of free chunks */
  MALLINFO_FIELD_TYPE smblks;   /* always 0 */
  MALLINFO_FIELD_TYPE hblks;    /* always 0 */
  MALLINFO_FIELD_TYPE hblkhd;   /* space in mmapped regions */
  MALLINFO_FIELD_TYPE usmblks;  /* maximum total allocated space */
  MALLINFO_FIELD_TYPE fsmblks;  /* always 0 */
  MALLINFO_FIELD_TYPE uordblks; /* total allocated space */
  MALLINFO_FIELD_TYPE fordblks; /* total free space */
  MALLINFO_FIELD_TYPE keepcost; /* releasable (via malloc_trim) space */
};
#endif  /* _MALLOC_H */
#endif  /* HAVE_USR_INCLUDE_MALLOC_H */

struct mallinfo dlmallinfo(void);
#endif  /* NO_MALLINFO */

/*
  independent_calloc(size_t n_elements, size_t element_size, void* chunks[]);

  independent_calloc is similar to calloc, but instead of returning a
  single cleared space, it returns an array of pointers to n_elements
  independent elements that can hold contents of size elem_size, each
  of which starts out cleared, and can be independently freed,
  realloc'ed etc. The elements are guaranteed to be adjacently
  allocated (this is not guaranteed to occur with multiple callocs or
  mallocs), which may also improve cache locality in some
  applications.

  The "chunks" argument is optional (i.e., may be null, which is
  probably the most typical usage). If it is null, the returned array
  is itself dynamically allocated and should also be freed when it is
  no longer needed. Otherwise, the chunks array must be of at least
  n_elements in length. It is filled in with the pointers to the
  chunks.

  In either case, independent_calloc returns this pointer array, or
  null if the allocation failed.  If n_elements is zero and "chunks"
  is null, it returns a chunk representing an array with zero elements
  (which should be freed if not wanted).

  Each element must be individually freed when it is no longer
  needed. If you'd like to instead be able to free all at once, you
  should instead use regular calloc and assign pointers into this
  space to represent elements.  (In this case though, you cannot
  independently free elements.)

  independent_calloc simplifies and speeds up implementations of many
  kinds of pools.  It may also be useful when constructing large data
  structures that initially have a fixed number of fixed-sized nodes,
  but the number is not known at compile time, and some of the nodes
  may later need to be freed. For example:

  struct Node { int item; struct Node* next; };

  struct Node* build_list() {
    struct Node** pool;
    int n = read_number_of_nodes_needed();
    if (n <= 0) return 0;
    pool = (struct Node**)(independent_calloc(n, sizeof(struct Node), 0);
    if (pool == 0) die();
    // organize into a linked list...
    struct Node* first = pool[0];
    for (i = 0; i < n-1; ++i)
      pool[i]->next = pool[i+1];
    free(pool);     // Can now free the array (or not, if it is needed later)
    return first;
  }
*/
void** dlindependent_calloc(size_t, size_t, void**);

/*
  independent_comalloc(size_t n_elements, size_t sizes[], void* chunks[]);

  independent_comalloc allocates, all at once, a set of n_elements
  chunks with sizes indicated in the "sizes" array.    It returns
  an array of pointers to these elements, each of which can be
  independently freed, realloc'ed etc. The elements are guaranteed to
  be adjacently allocated (this is not guaranteed to occur with
  multiple callocs or mallocs), which may also improve cache locality
  in some applications.

  The "chunks" argument is optional (i.e., may be null). If it is null
  the returned array is itself dynamically allocated and should also
  be freed when it is no longer needed. Otherwise, the chunks array
  must be of at least n_elements in length. It is filled in with the
  pointers to the chunks.

  In either case, independent_comalloc returns this pointer array, or
  null if the allocation failed.  If n_elements is zero and chunks is
  null, it returns a chunk representing an array with zero elements
  (which should be freed if not wanted).

  Each element must be individually freed when it is no longer
  needed. If you'd like to instead be able to free all at once, you
  should instead use a single regular malloc, and assign pointers at
  particular offsets in the aggregate space. (In this case though, you
  cannot independently free elements.)

  independent_comallac differs from independent_calloc in that each
  element may have a different size, and also that it does not
  automatically clear elements.

  independent_comalloc can be used to speed up allocation in cases
  where several structs or objects must always be allocated at the
  same time.  For example:

  struct Head { ... }
  struct Foot { ... }

  void send_message(char* msg) {
    int msglen = strlen(msg);
    size_t sizes[3] = { sizeof(struct Head), msglen, sizeof(struct Foot) };
    void* chunks[3];
    if (independent_comalloc(3, sizes, chunks) == 0)
      die();
    struct Head* head = (struct Head*)(chunks[0]);
    char*        body = (char*)(chunks[1]);
    struct Foot* foot = (struct Foot*)(chunks[2]);
    // ...
  }

  In general though, independent_comalloc is worth using only for
  larger values of n_elements. For small values, you probably won't
  detect enough difference from series of malloc calls to bother.

  Overuse of independent_comalloc can increase overall memory usage,
  since it cannot reuse existing noncontiguous small chunks that
  might be available for some of the elements.
*/
void** dlindependent_comalloc(size_t, size_t*, void**);

/*
  pvalloc(size_t n);
  Equivalent to valloc(minimum-page-that-holds(n)), that is,
  round up n to nearest pagesize.
 */
void*  dlpvalloc(size_t);

/*
  malloc_trim(size_t pad);

  If possible, gives memory back to the system (via negative arguments
  to sbrk) if there is unused memory at the `high' end of the malloc
  pool or in unused MMAP segments. You can call this after freeing
  large blocks of memory to potentially reduce the system-level memory
  requirements of a program. However, it cannot guarantee to reduce
  memory. Under some allocation patterns, some large free blocks of
  memory will be locked between two used chunks, so they cannot be
  given back to the system.

  The `pad' argument to malloc_trim represents the amount of free
  trailing space to leave untrimmed. If this argument is zero, only
  the minimum amount of memory to maintain internal data structures
  will be left. Non-zero arguments can be supplied to maintain enough
  trailing space to service future expected allocations without having
  to re-obtain memory from the system.

  Malloc_trim returns 1 if it actually released any memory, else 0.
*/
int  dlmalloc_trim(size_t);

/*
  malloc_usable_size(void* p);

  Returns the number of bytes you can actually use in
  an allocated chunk, which may be more than you requested (although
  often not) due to alignment and minimum size constraints.
  You can use this many bytes without worrying about
  overwriting other allocated objects. This is not a particularly great
  programming practice. malloc_usable_size can be more useful in
  debugging and assertions, for example:

  p = malloc(n);
  assert(malloc_usable_size(p) >= 256);
*/
size_t dlmalloc_usable_size(void*);

/*
  malloc_stats();
  Prints on stderr the amount of space obtained from the system (both
  via sbrk and mmap), the maximum amount (which may be more than
  current if malloc_trim and/or munmap got called), and the current
  number of bytes allocated via malloc (or realloc, etc) but not yet
  freed. Note that this is the number of bytes allocated, not the
  number requested. It will be larger than the number requested
  because of alignment and bookkeeping overhead. Because it includes
  alignment wastage as being in use, this figure may be greater than
  zero even when no user-level chunks are allocated.

  The reported current and maximum system memory can be inaccurate if
  a program makes other calls to system memory allocation functions
  (normally sbrk) outside of malloc.

  malloc_stats prints only the most commonly interesting statistics.
  More information can be obtained by calling mallinfo.
*/
void  dlmalloc_stats();

#endif /* !ONLY_MSPACES */

#if MSPACES

/*
  mspace is an opaque type representing an independent
  region of space that supports mspace_malloc, etc.
*/
typedef void* mspace;

/*
  create_mspace creates and returns a new independent space with the
  given initial capacity, or, if 0, the default granularity size.  It
  returns null if there is no system memory available to create the
  space.  If argument locked is non-zero, the space uses a separate
  lock to control access. The capacity of the space will grow
  dynamically as needed to service mspace_malloc requests.  You can
  control the sizes of incremental increases of this space by
  compiling with a different DEFAULT_GRANULARITY or dynamically
  setting with mallopt(M_GRANULARITY, value).
*/
mspace create_mspace(size_t capacity, int locked);

/*
  destroy_mspace destroys the given space, and attempts to return all
  of its memory back to the system, returning the total number of
  bytes freed. After destruction, the results of access to all memory
  used by the space become undefined.
*/
size_t destroy_mspace(mspace msp);

/*
  create_mspace_with_base uses the memory supplied as the initial base
  of a new mspace. Part (less than 128*sizeof(size_t) bytes) of this
  space is used for bookkeeping, so the capacity must be at least this
  large. (Otherwise 0 is returned.) When this initial space is
  exhausted, additional memory will be obtained from the system.
  Destroying this space will deallocate all additionally allocated
  space (if possible) but not the initial base.
*/
mspace create_mspace_with_base(void* base, size_t capacity, int locked);

/*
  mspace_malloc behaves as malloc, but operates within
  the given space.
*/
void* mspace_malloc(mspace msp, size_t bytes);

/*
  mspace_free behaves as free, but operates within
  the given space.

  If compiled with FOOTERS==1, mspace_free is not actually needed.
  free may be called instead of mspace_free because freed chunks from
  any space are handled by their originating spaces.
*/
void mspace_free(mspace msp, void* mem);

/*
  mspace_realloc behaves as realloc, but operates within
  the given space.

  If compiled with FOOTERS==1, mspace_realloc is not actually
  needed.  realloc may be called instead of mspace_realloc because
  realloced chunks from any space are handled by their originating
  spaces.
*/
void* mspace_realloc(mspace msp, void* mem, size_t newsize);

/*
  mspace_calloc behaves as calloc, but operates within
  the given space.
*/
void* mspace_calloc(mspace msp, size_t n_elements, size_t elem_size);

/*
  mspace_memalign behaves as memalign, but operates within
  the given space.
*/
void* mspace_memalign(mspace msp, size_t alignment, size_t bytes);

/*
  mspace_independent_calloc behaves as independent_calloc, but
  operates within the given space.
*/
void** mspace_independent_calloc(mspace msp, size_t n_elements,
                                 size_t elem_size, void* chunks[]);

/*
  mspace_independent_comalloc behaves as independent_comalloc, but
  operates within the given space.
*/
void** mspace_independent_comalloc(mspace msp, size_t n_elements,
                                   size_t sizes[], void* chunks[]);

/*
  mspace_footprint() returns the number of bytes obtained from the
  system for this space.
*/
size_t mspace_footprint(mspace msp);

#ifndef NO_MALLINFO
/*
  mspace_mallinfo behaves as mallinfo, but reports properties of
  the given space.
*/
struct mallinfo mspace_mallinfo(mspace msp);
#endif /* NO_MALLINFO */

/*
  mspace_malloc_stats behaves as malloc_stats, but reports
  properties of the given space.
*/
void mspace_malloc_stats(mspace msp);

/*
  mspace_trim behaves as malloc_trim, but
  operates within the given space.
*/
int mspace_trim(mspace msp, size_t pad);

/*
  An alias for mallopt.
*/
int mspace_mallopt(int, int);

#endif  /* MSPACES */

// NDS-specific interface
// Kevin Vance
bool nds_ram_init(RAM_TYPE type);
void *nds_ext_malloc(size_t bytes);
void *nds_ext_realloc(void *mem, size_t bytes);
void nds_ext_free(void *mem);
void nds_ext_unlock(void);
void nds_ext_lock(void);
void nds_ext_copy(void *dest, void *src, size_t size);

// These functions take a pointer to a pointer to the memory region to swap,
// e.g. (&foo) for (char *foo).
bool nds_ext_in(void *normal_buffer, size_t size);
bool nds_ext_out(void *extra_buffer, size_t size);

#ifdef __cplusplus
};  /* end of extern "C" */
#endif

#endif /* MALLOC_280_H */

repo_name: pgimeno/megazeux

license: mit


code:
/* MegaZeux
 *
 * Copyright (C) 2004 Gilead Kutnick <exophase@adelphia.net>
 * Copyright (C) 2007-2009 Kevin Vance <kvance@kvance.com>
 *
 * This program 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 of
 * the License, or (at your option) any later version.
 *
 * This program 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 this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 */

#include "../../src/event.h"
#include "../../src/platform.h"
#include "../../src/graphics.h"

#include <nds/arm9/keyboard.h>
#include "render.h"
#include "evq.h"

void nds_update_input(void);
static bool process_event(NDSEvent *event);

extern struct input_status input;

bool __update_event_status(void)
{
  bool retval = false;
  NDSEvent event;

  while(nds_event_poll(&event))
    retval |= process_event(&event);

  return retval;
}

void __wait_event(void)
{
  NDSEvent event;

  while(!nds_event_poll(&event))
    swiWaitForVBlank();
  process_event(&event);
}

void real_warp_mouse(Uint32 x, Uint32 y)
{
  // Since we can't warp a touchscreen stylus, focus there instead.
  focus_pixel(x, y);
}

void initialize_joysticks(void)
{
  // stub (we have a hardcoded 0-axis, 8-button joystick)
}

static void convert_nds_internal(int key, int *internal_code, int *unicode)
{
  // Actually ASCIIish, but close enough.
  if(key > 0)
    *unicode = key;
  else
    *unicode = 0;

  // Uppercase letters
  if(key >= 65 && key <= 90)
    *internal_code = key + 32;

  // Arrow keys
  else if(key == -17)
    *internal_code = IKEY_UP;
  else if(key == -19)
    *internal_code = IKEY_DOWN;
  else if(key == -20)
    *internal_code = IKEY_LEFT;
  else if(key == -18)
    *internal_code = IKEY_RIGHT;

  // Other keys
  else if(key == -23)
    *internal_code = IKEY_ESCAPE;
  else if(key == -15)
    *internal_code = IKEY_CAPSLOCK;
  else if(key == 10)
    *internal_code = IKEY_RETURN;
  else if(key == -14)
    *internal_code = IKEY_LSHIFT;
  else if(key == -16)
    *internal_code = IKEY_LCTRL;
  else if(key == -26)
    *internal_code = IKEY_LALT;
  else if(key == -5)
    *internal_code = IKEY_MENU;

  // ASCIIish keys
  else if(key > 0 && key < 512)
    *internal_code = key;

  // Unknown key
  else
    *internal_code = IKEY_UNKNOWN;
}

// Send a key up/down event to MZX.
static void do_unicode_key_event(struct buffered_status *status, bool down,
 enum keycode code, int unicode)
{
  if(down)
  {
    key_press(status, code, unicode);
  }
  else
  {
    status->keymap[code] = 0;
    if(status->key_repeat == code)
    {
      status->key_repeat = IKEY_UNKNOWN;
      status->unicode_repeat = 0;
    }
    status->key_release = code;
  }
}

static void do_key_event(struct buffered_status *status, bool down,
 enum keycode code)
{
  do_unicode_key_event(status, down, code, code);
}

// Send a joystick button up/down event to MZX.
static void do_joybutton_event(struct buffered_status *status, bool down,
 int button)
{
  // Look up the keycode for this joystick button.
  enum keycode stuffed_key = input.joystick_button_map[0][button];
  do_key_event(status, down, stuffed_key);
}

static bool process_event(NDSEvent *event)
{
  struct buffered_status *status = store_status();
  bool key_down = false;
  bool retval = true;

  switch(event->type)
  {
    // Hardware key pressed
    case NDS_EVENT_KEY_DOWN:
    {
      if(event->key == KEY_R)
      {
          nds_subscreen_switch();
          retval = false;
          break;
      }
      key_down = true;
    }

    // Continuing from NDS_EVENT_KEY_DOWN...
    case NDS_EVENT_KEY_UP:
    {
      switch(event->key)
      {
        case KEY_UP:
          do_key_event(status, key_down, IKEY_UP);
          break;
        case KEY_DOWN:
          do_key_event(status, key_down, IKEY_DOWN);
          break;
         case KEY_LEFT:
          do_key_event(status, key_down, IKEY_LEFT);
          break;
        case KEY_RIGHT:
          do_key_event(status, key_down, IKEY_RIGHT);
          break;

        case KEY_A:
          do_joybutton_event(status, key_down, 0);
          break;
        case KEY_B:
          do_joybutton_event(status, key_down, 1);
          break;
        case KEY_X:
          do_joybutton_event(status, key_down, 2);
          break;
        case KEY_Y:
          do_joybutton_event(status, key_down, 3);
          break;

        case KEY_L:
          do_joybutton_event(status, key_down, 4);
          break;

        case KEY_SELECT:
          do_joybutton_event(status, key_down, 6);
          break;
        case KEY_START:
          do_joybutton_event(status, key_down, 7);
          break;

        default:
          retval = false;
          break;
      }

      break;
    }

    // Software key down
    case NDS_EVENT_KEYBOARD_DOWN:
    {
      key_down = true;
    }

    // Continuing from NDS_EVENT_KEYBOARD_UP...
    case NDS_EVENT_KEYBOARD_UP:
    {
      int internal_code, unicode;
      convert_nds_internal(event->key, &internal_code, &unicode);
      do_unicode_key_event(status, key_down, internal_code, unicode);
      break;
    }

    // Touchscreen stylus down
    case NDS_EVENT_TOUCH_DOWN:
    {
      int button = MOUSE_BUTTON_LEFT;
      status->mouse_button = button;
      status->mouse_repeat = button;
      status->mouse_button_state |= MOUSE_BUTTON(button);
      status->mouse_repeat_state = 1;
      status->mouse_drag_state = -1;
      status->mouse_time = get_ticks();
      break;
    }

    // Touchscreen stylus up
    case NDS_EVENT_TOUCH_UP:
    {
      int button = MOUSE_BUTTON_LEFT;
      status->mouse_button_state &= ~MOUSE_BUTTON(button);
      status->mouse_repeat = 0;
      status->mouse_drag_state = 0;
      status->mouse_repeat_state = 0;
      nds_mouselook(false);
      break;
    }

    // Touchscreen stylus drag
    case NDS_EVENT_TOUCH_MOVE:
    {
      int mx = event->x * 640 / 256;
      int my = event->y * 350 / 192;

      // Update the MZX mouse state.
      status->real_mouse_x = mx;
      status->real_mouse_y = my;
      status->mouse_x = mx / 8;
      status->mouse_y = my / 14;
      status->mouse_moved = true;

      // Update our internal mouselook state.
      nds_mouselook(false);
      focus_pixel(mx, my);
      nds_mouselook(true);
      break;
    }

    // Unknown event?
    default:
    {
      retval = false;
      break;
    }
  };

  return retval;
}

static void nds_update_hw_keys(void)
{
  static int last_keys_mask = 0;
  int keys = keysHeld();
  int check[] = { KEY_A, KEY_B, KEY_SELECT, KEY_START, KEY_RIGHT, KEY_LEFT,
                  KEY_UP, KEY_DOWN, KEY_R, KEY_L, KEY_X, KEY_Y };
  NDSEvent event;

  for(size_t i = 0; i < sizeof(check)/sizeof(*check); i++)
  {
    int keybit = check[i];
    if(keybit & keys)
    {
      if(!(keybit & last_keys_mask))
      {
        // Key pressed
        nds_event_fill_key_down(&event, keybit);
        nds_event_push(&event);
        last_keys_mask |= keybit;
      }
    }
    else if(keybit & last_keys_mask)
    {
      // Key released
      nds_event_fill_key_up(&event, keybit);
      nds_event_push(&event);
      last_keys_mask &= ~keybit;
    }
  }
}

static void nds_update_sw_keyboard(void)
{
  static int last_key = -1;
  int c = keyboardUpdate();
  NDSEvent event;

  // Release last key
  if(last_key != -1)
  {
    nds_event_fill_keyboard_up(&event, last_key);
    nds_event_push(&event);
    last_key = -1;
  }

  // Press new key
  if(c != -1)
  {
    nds_event_fill_keyboard_down(&event, c);
    nds_event_push(&event);
    last_key = c;
  }
}

static void nds_update_mouse(void)
{
  static bool last_touch_press = false;
  static int last_touch_x = -1;
  static int last_touch_y = -1;
  NDSEvent new_event;

  if(is_scaled_mode(subscreen_mode))
  {
    // Inject mouse motion events.
    if(keysHeld() & KEY_TOUCH)
    {
      touchPosition touch;
      touchRead(&touch);

      if(touch.px != last_touch_x || touch.py != last_touch_y)
      {
        // Add the event.
        nds_event_fill_touch_move(&new_event, touch.px, touch.py);
        nds_event_push(&new_event);

        last_touch_x = touch.px;
        last_touch_y = touch.py;
      }

      if(!last_touch_press)
      {
        nds_event_fill_touch_down(&new_event);
        nds_event_push(&new_event);

        last_touch_press = true;
      }
    }
    else if(last_touch_press)
    {
      nds_event_fill_touch_up(&new_event);
      nds_event_push(&new_event);

      last_touch_press = false;
    }
  }
}

void nds_update_input(void)
{
  scanKeys();
  nds_update_hw_keys();
  nds_update_sw_keyboard();
  nds_update_mouse();
}


code:
/* MegaZeux
 *
 * Copyright (C) 2004 Gilead Kutnick <exophase@adelphia.net>
 * Copyright (C) 2009 Kevin Vance <kvance@kvance.com>
 *
 * This program 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 of
 * the License, or (at your option) any later version.
 *
 * This program 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 this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 */

/* Simple event queue for NDS input events */

#include "compat.h"
#include "evq.h"

static NDSEvent *evlist_front = NULL;
static NDSEvent *evlist_back = NULL;

bool nds_event_poll(NDSEvent *dest)
{
  NDSEvent *curr_event;
  bool retval = false;

  // Mask new input events while modifying the queue.
  irqDisable(IRQ_TIMER2);

  curr_event = evlist_front;
  if(curr_event)
  {
    // Copy the event to the destination.
    memcpy(dest, curr_event, sizeof(NDSEvent));
    dest->_next = NULL;

    // Advance to the next event.
    if(evlist_front != evlist_back)
    {
      evlist_front = evlist_front->_next;
    }
    else
    {
      evlist_front = NULL;
      evlist_back = NULL;
    }

    // Dispose of the old event.
    free(curr_event);
    retval = true;
  }

  // Allow new events again.
  irqEnable(IRQ_TIMER2);

  return retval;
}

void nds_event_push(NDSEvent *src)
{
  // Copy the source event.
  NDSEvent *new_event = cmalloc(sizeof(NDSEvent));
  memcpy(new_event, src, sizeof(NDSEvent));
  new_event->_next = NULL;

  // Add it to the list.
  if(evlist_back)
  {
    evlist_back->_next = new_event;
    evlist_back = new_event;
  }
  else
  {
    evlist_front = new_event;
    evlist_back = new_event;
  }
}

// Fill in a key down event.
void nds_event_fill_key_down(NDSEvent *dest, int key)
{
  dest->type = NDS_EVENT_KEY_DOWN;
  dest->key = key;
}

// Fill in a key up event.
void nds_event_fill_key_up(NDSEvent *dest, int key)
{
  dest->type = NDS_EVENT_KEY_UP;
  dest->key = key;
}

// Fill in a software keyboard key down event.
void nds_event_fill_keyboard_down(NDSEvent *dest, int key)
{
  dest->type = NDS_EVENT_KEYBOARD_DOWN;
  dest->key = key;
}

// Fill in a software keyboard key up event.
void nds_event_fill_keyboard_up(NDSEvent *dest, int key)
{
  dest->type = NDS_EVENT_KEYBOARD_UP;
  dest->key = key;
}

// Fill in a touch event.
void nds_event_fill_touch_down(NDSEvent *dest)
{
  dest->type = NDS_EVENT_TOUCH_DOWN;
}

// Fill in a touch event.
void nds_event_fill_touch_move(NDSEvent *dest, int x, int y)
{
  dest->type = NDS_EVENT_TOUCH_MOVE;
  dest->x = x;
  dest->y = y;
}

// Fill in a touch event.
void nds_event_fill_touch_up(NDSEvent *dest)
{
  dest->type = NDS_EVENT_TOUCH_UP;
}


code:
/* MegaZeux
 *
 * Copyright (C) 2004 Gilead Kutnick <exophase@adelphia.net>
 * Copyright (C) 2009 Kevin Vance <kvance@kvance.com>
 *
 * This program 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 of
 * the License, or (at your option) any later version.
 *
 * This program 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 this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 */

/* Simple event queue for NDS input events */

#ifndef __ARCH_NDS_EVQ_H
#define __ARCH_NDS_EVQ_H

#include "compat.h"

enum NDSEventType
{
  NDS_EVENT_NOP = 0,
  NDS_EVENT_KEY_DOWN,           // Hardware key down
  NDS_EVENT_KEY_UP,             // Hardware key up
  NDS_EVENT_KEYBOARD_DOWN,      // Software key down
  NDS_EVENT_KEYBOARD_UP,        // Software key up
  NDS_EVENT_TOUCH_DOWN,         // Touchscreen pen down
  NDS_EVENT_TOUCH_MOVE,         // Touchscreen pen move
  NDS_EVENT_TOUCH_UP,           // Touchscreen pen up
};

typedef struct NDSEvent_tag
{
  enum NDSEventType type;

  // On NDS_EVENT_KEY_DOWN and NDS_EVENT_KEY_UP, a keyboard bit e.g. KEY_LEFT.
  // On NDS_EVENT_KEYBOARD_PRESSED, the ASCII character pressed (not held).
  int key;

  // NDS_EVENT_TOUCH_MOVE touch coordinates.
  int x;
  int y;

  // Implementation: next event in the list.
  struct NDSEvent_tag *_next;
} NDSEvent;

// Return true if an event is available, fill dest with this event.
bool nds_event_poll(NDSEvent *dest);

// Push a copy of this event onto the queue.
void nds_event_push(NDSEvent *src);

// Fill in different event structures.
void nds_event_fill_key_down(NDSEvent *dest, int key);
void nds_event_fill_key_up(NDSEvent *dest, int key);
void nds_event_fill_keyboard_down(NDSEvent *dest, int key);
void nds_event_fill_keyboard_up(NDSEvent *dest, int key);
void nds_event_fill_touch_down(NDSEvent *dest);
void nds_event_fill_touch_move(NDSEvent *dest, int x, int y);
void nds_event_fill_touch_up(NDSEvent *dest);

#endif /* __ARCH_NDS_EVQ_H */


code:
/*---------------------------------------------------------------------------------
  $Id: gurumeditation.c,v 1.6 2006/08/03 09:35:36 wntrmute Exp $

  Copyright (C) 2005
  	Dave Murphy (WinterMute)

  This software is provided 'as-is', without any express or implied
  warranty.  In no event will the authors be held liable for any
  damages arising from the use of this software.

  Permission is granted to anyone to use this software for any
  purpose, including commercial applications, and to alter it and
  redistribute it freely, subject to the following restrictions:

  1. The origin of this software must not be misrepresented; you
     must not claim that you wrote the original software. If you use
     this software in a product, an acknowledgment in the product
     documentation would be appreciated but is not required.
  2. Altered source versions must be plainly marked as such, and
     must not be misrepresented as being the original software.
  3. This notice may not be removed or altered from any source
     distribution.

  $Log: gurumeditation.c,v $
  Revision 1.6  2006/08/03 09:35:36  wntrmute
  fix storing pc

  Revision 1.5  2006/07/06 02:14:33  wntrmute
  read r15 in enterException
  add return to bios

  Revision 1.4  2006/06/21 20:12:39  wntrmute
  thumb ldrsh has register offset

  Revision 1.3  2006/06/19 19:12:01  wntrmute
  correct prototypes again
  add defaultHandler function to install default handler

  Revision 1.2  2006/06/19 18:22:25  wntrmute
  completed thumb address calculations

  Revision 1.1  2006/06/19 14:09:37  wntrmute
  split default exception handler into separate file
  correct prototypes

---------------------------------------------------------------------------------*/

// Modified by kvance 2007-09-13
// Disable DMA and reset scrolling as soon as the exception handler is called.

#include "exception.h"
#include "../../src/config.h"
#include "../../src/compat.h"

#ifdef CONFIG_NDS

#include <stdio.h>

//---------------------------------------------------------------------------------
static unsigned long ARMShift(unsigned long value,unsigned char shift) {
//---------------------------------------------------------------------------------
	int index, i;
	bool isN;

	// no shift at all
	if (shift == 0x0B) return value ;
	if (shift & 0x01) {
		// shift index is a register
		index = exceptionRegisters[(shift >> 4) & 0x0F];
	} else {
		// constant shift index
		index = ((shift >> 3) & 0x1F) ;
	} ;
	switch (shift & 0x06) {
		case 0x00:
			// logical left
			return (value << index) ;
		case 0x02:
			// logical right
			return (value >> index) ;
		case 0x04:
			// arithmetical right
			isN = (value & 0x80000000) ;
			value = value >> index ;
			if (isN) {
				for (i=31;i>31-index;i--) {
					value = value | (1 << i) ;
				} ;
			} ;
			return value ;
		case 0x06:
			// rotate right
			index = index & 0x1F;
			value = (value >> index) | (value << (32-index));
			return value;
	};
	return value;
}

//---------------------------------------------------------------------------------
static u32 getExceptionAddress( u32 opcodeAddress, u32 thumbState) {
//---------------------------------------------------------------------------------

	int Rf, Rb, Rd, Rn, Rm;

	if (thumbState) {
		// Thumb

		unsigned short opcode = *(unsigned short *)opcodeAddress ;
		// ldr r,[pc,###]			01001ddd ffffffff
		// ldr r,[r,r]				0101xx0f ffbbbddd
		// ldrsh					0101xx1f ffbbbddd
		// ldr r,[r,imm]			011xxfff ffbbbddd
		// ldrh						1000xfff ffbbbddd
		// ldr r,[sp,###]			1001xddd ffffffff
		// push						1011x10l llllllll
		// ldm						1100xbbb llllllll

		if ((opcode & 0xF800) == 0x4800) {
			// ldr r,[pc,###]
			s8 offset = opcode & 0xff;
			return exceptionRegisters[15] + offset;
		} else if ((opcode & 0xF200) == 0x5000) {
			// ldr r,[r,r]
			Rb = (opcode >> 3) & 0x07 ;
			Rf = (opcode >> 6) & 0x07 ;
			return exceptionRegisters[Rb] + exceptionRegisters[Rf];

		} else if ((opcode & 0xF200) == 0x5200) {
			// ldrsh
			Rb = (opcode >> 3) & 0x07;
			Rf = (opcode >> 6) & 0x03;
			return exceptionRegisters[Rb] + exceptionRegisters[Rf];

		} else if ((opcode & 0xE000) == 0x6000) {
			// ldr r,[r,imm]
			Rb = (opcode >> 3) & 0x07;
			Rf = (opcode >> 6) & 0x1F ;
			return exceptionRegisters[Rb] + (Rf << 2);
		} else if ((opcode & 0xF000) == 0x8000) {
			// ldrh
			Rb = (opcode >> 3) & 0x07 ;
			Rf = (opcode >> 6) & 0x1F ;
			return exceptionRegisters[Rb] + (Rf << 2);
		} else if ((opcode & 0xF000) == 0x9000) {
			// ldr r,[sp,#imm]
			s8 offset = opcode & 0xff;
			return exceptionRegisters[13] + offset;
		} else if ((opcode & 0xF700) == 0xB500) {
			// push/pop
			return exceptionRegisters[13];
		} else if ((opcode & 0xF000) == 0xC000) {
			// ldm/stm
			Rd = (opcode >> 8) & 0x07;
			return exceptionRegisters[Rd];
		}
	} else {
		// arm32
		unsigned long opcode = *(unsigned long *)opcodeAddress ;

		// SWP			xxxx0001 0x00nnnn dddd0000 1001mmmm
		// STR/LDR		xxxx01xx xxxxnnnn ddddffff ffffffff
		// STRH/LDRH	xxxx000x x0xxnnnn dddd0000 1xx1mmmm
		// STRH/LDRH	xxxx000x x1xxnnnn ddddffff 1xx1ffff
		// STM/LDM		xxxx100x xxxxnnnn llllllll llllllll

		if ((opcode & 0x0FB00FF0) == 0x01000090) {
			// SWP
			Rn = (opcode >> 16) & 0x0F;
			return exceptionRegisters[Rn];
		} else if ((opcode & 0x0C000000) == 0x04000000) {
			// STR/LDR
			Rn = (opcode >> 16) & 0x0F;
			if (opcode & 0x02000000) {
				// Register offset
				Rm = opcode & 0x0F;
				if (opcode & 0x01000000) {
					unsigned short shift = (unsigned short)((opcode >> 4) & 0xFF) ;
					// pre indexing
					long Offset = ARMShift(exceptionRegisters[Rm],shift);
					// add or sub the offset depending on the U-Bit
					return exceptionRegisters[Rn] + ((opcode & 0x00800000)?Offset:-Offset);
				} else {
					// post indexing
					return exceptionRegisters[Rn];
				}
			} else {
				// Immediate offset
				unsigned long Offset = (opcode & 0xFFF) ;
				if (opcode & 0x01000000) {
					// pre indexing
					// add or sub the offset depending on the U-Bit
					return exceptionRegisters[Rn] + ((opcode & 0x00800000)?Offset:-Offset);
				} else {
					// post indexing
					return exceptionRegisters[Rn];
				}
			}
		} else if ((opcode & 0x0E400F90) == 0x00000090) {
			unsigned short shift;
			long Offset;
			// LDRH/STRH with register Rm
			Rn = (opcode >> 16) & 0x0F;
			Rd = (opcode >> 12) & 0x0F;
			Rm = opcode & 0x0F;
			shift = (unsigned short)((opcode >> 4) & 0xFF);
			Offset = ARMShift(exceptionRegisters[Rm],shift);
			// add or sub the offset depending on the U-Bit
			return exceptionRegisters[Rn] + ((opcode & 0x00800000)?Offset:-Offset);
		} else if ((opcode & 0x0E400F90) == 0x00400090) {
			unsigned long Offset;
			// LDRH/STRH with immediate offset
			Rn = (opcode >> 16) & 0x0F;
			Rd = (opcode >> 12) & 0x0F;
			Offset = (opcode & 0xF) | ((opcode & 0xF00)>>8) ;
			// add or sub the offset depending on the U-Bit
			return exceptionRegisters[Rn] + ((opcode & 0x00800000)?Offset:-Offset) ;
		} else if ((opcode & 0x0E000000) == 0x08000000) {
			// LDM/STM
			Rn = (opcode >> 16) & 0x0F;
			return exceptionRegisters[Rn];
		}
	}
	return 0;
}

static const char *registerNames[] =
	{	"r0","r1","r2","r3","r4","r5","r6","r7",
		"r8 ","r9 ","r10","r11","r12","sp ","lr ","pc " };

extern const char __itcm_start[];
//---------------------------------------------------------------------------------
static void mzxExceptionHandler() {
//---------------------------------------------------------------------------------
	u32 *stack, currentMode, thumbState, codeAddress, exceptionAddress = 0;
	int i, offset = 8;

	DMA0_CR = 0;
	DMA1_CR = 0;
	DMA2_CR = 0;
	DMA3_CR = 0;
	REG_BG0HOFS_SUB = 0;
	REG_BG0VOFS_SUB = 0;

	videoSetMode(0);
	videoSetModeSub(MODE_0_2D | DISPLAY_BG0_ACTIVE);
	vramSetBankC(VRAM_C_SUB_BG);

	REG_BG0CNT_SUB = BG_MAP_BASE(31);

	BG_PALETTE_SUB[0] = RGB15(31,0,0);
	BG_PALETTE_SUB[255] = RGB15(31,31,31);

	consoleDemoInit();

	iprintf("\x1b[5CGuru Meditation Error!\n");
	currentMode = getCPSR() & 0x1f;
	thumbState = ((*(u32*)0x027FFD90) & 0x20);

	if ( currentMode == 0x17 ) {
		iprintf ("\x1b[10Cdata abort!\n\n");
		codeAddress = exceptionRegisters[15] - offset;
		if (	(codeAddress > 0x02000000 && codeAddress < 0x02400000) ||
				(codeAddress > (u32)__itcm_start && codeAddress < (u32)(__itcm_start + 32768)) )
			exceptionAddress = getExceptionAddress( codeAddress, thumbState);
		else
			exceptionAddress = codeAddress;

	} else {
		if (thumbState)
			offset = 2;
		else
			offset = 4;
		iprintf("\x1b[5Cundefined instruction!\n\n");
		codeAddress = exceptionRegisters[15] - offset;
		exceptionAddress = codeAddress;
	}

	iprintf("  pc: %08X addr: %08X\n\n",codeAddress,exceptionAddress);

	for ( i=0; i < 8; i++ ) {
		iprintf(	"  %s: %08X   %s: %08X\n",
					registerNames[i], (unsigned int)exceptionRegisters[i],
					registerNames[i+8], (unsigned int)exceptionRegisters[i+8]);
	}
	iprintf("\n");
	stack = (u32 *)exceptionRegisters[13];
	for ( i=0; i<10; i++ ) {
		iprintf( "\x1b[%d;2H%08X: %08X %08X", i + 14, (u32)&stack[i*2],stack[i*2], stack[(i*2)+1] );
	}
	while(1);

}

//---------------------------------------------------------------------------------
void setMzxExceptionHandler() {
//---------------------------------------------------------------------------------
	setExceptionHandler(mzxExceptionHandler) ;
}

#endif // CONFIG_NDS


code:
/* MegaZeux
 *
 * Copyright (C) 2007 Kevin Vance <kvance@kvance.com>
 *
 * This program 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 of
 * the License, or (at your option) any later version.
 *
 * This program 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 this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 */

void setMzxExceptionHandler(void);


code:
/* MegaZeux
 *
 * Copyright (C) 2007 Kevin Vance <kvance@kvance.com>
 *
 * This program 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 of
 * the License, or (at your option) any later version.
 *
 * This program 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 this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 */

/* This file is only used on NDS, and is not even compiled on
 * other systems. It handles transferral of main-memory data structures
 * into slower external memory to reduce memory pressure.
 */

#include "extmem.h"
#include "dlmalloc.h"
#include "robot.h"

// Move the robot's memory from normal RAM to extra RAM.
static void store_robot_to_extram(struct robot *robot)
{
  if(!robot)
    return;

  // Store the stack in extram.
  if(!nds_ext_in(&robot->stack, robot->stack_size * sizeof(int))) {}
    // TODO: handle out-of-memory

  // Store the program in extram, noting the address change.
  if(!nds_ext_in(&robot->program_bytecode, robot->program_bytecode_length)) {}
    // TODO: handle out-of-memory

  if(robot->used)
  {
    clear_label_cache(robot->label_list, robot->num_labels);
    robot->label_list = cache_robot_labels(robot, &robot->num_labels);
  }
}

// Move the robot's memory from extra RAM to normal RAM.
static void retrieve_robot_from_extram(struct robot *robot)
{
  if(!robot)
    return;

  if(!nds_ext_out(&robot->stack, robot->stack_size * sizeof(int))) {}
    // TODO: handle out-of-memory

  if(!nds_ext_out(&robot->program_bytecode, robot->program_bytecode_length)) {}
    // TODO: handle out-of-memory

  if(robot->used)
  {
    clear_label_cache(robot->label_list, robot->num_labels);
    robot->label_list = cache_robot_labels(robot, &robot->num_labels);
  }
}

// Move the board's memory from normal RAM to extra RAM.
void store_board_to_extram(struct board *board)
{
  size_t i, num_fields;
  int size, j;

  char **ptr_list[] =
  {
    &board->level_id, &board->level_param, &board->level_color,
    &board->level_under_id, &board->level_under_param,
    &board->level_under_color, &board->overlay, &board->overlay_color
  };

  // Skip the last 2 pointers if overlays are disabled.
  num_fields = sizeof(ptr_list)/sizeof(*ptr_list);
  if(!board->overlay_mode)
    num_fields -= 2;

  size = board->board_width * board->board_height;
  for(i = 0; i < num_fields; i++)
    if(!nds_ext_in((void**)ptr_list[i], size)) {}
      // TODO: handle out-of-memory

  // Copy the robots if necessary.
  if(board->robot_list)
    for(j = 1; j <= board->num_robots; j++)
      store_robot_to_extram(board->robot_list[j]);
}

// Move the board's memory from normal RAM to extra RAM.
void retrieve_board_from_extram(struct board *board)
{
  size_t i, num_fields;
  int j, size;

  char **ptr_list[] =
  {
    &board->level_id, &board->level_param, &board->level_color,
    &board->level_under_id, &board->level_under_param,
    &board->level_under_color, &board->overlay, &board->overlay_color
  };

  // Skip the last 2 pointers if overlays are disabled.
  num_fields = sizeof(ptr_list)/sizeof(*ptr_list);
  if(!board->overlay_mode)
    num_fields -= 2;

  size = board->board_width * board->board_height;
  for(i = 0; i < num_fields; i++)
    if(!nds_ext_out((void**)ptr_list[i], size)) {}
      // TODO: handle out-of-memory

  // Copy the robots if necessary.
  if(board->robot_list)
    for(j = 1; j <= board->num_robots; j++)
      retrieve_robot_from_extram(board->robot_list[j]);
}

// Set the current board to cur_board, in regular memory.
void set_current_board(struct world *mzx_world, struct board *cur_board)
{
  if(mzx_world->current_board)
    store_board_to_extram(mzx_world->current_board);
  mzx_world->current_board = cur_board;
}

// Set the current board to cur_board, in extra memory.
void set_current_board_ext(struct world *mzx_world, struct board *cur_board)
{
  set_current_board(mzx_world, cur_board);
  retrieve_board_from_extram(cur_board);
}


code:
#ifndef NDS_RAM_OPTS_H
#define NDS_RAM_OPTS_H

#define INSECURE	1
#define ONLY_MSPACES    1
#define HAVE_MORECORE	0
#define HAVE_MMAP	0
#define MSPACES		1
#define malloc_getpagesize	4096
#undef  DEBUG

#endif


code:
# Axis1:   -left +right
# Axis2:   -up   +down
#
# Button1: A
# Button2: B
# Button3: X
# Button4: Y
# Button5: Left trigger
# Button6: Right trigger
# Button7: Select
# Button8: Start

# Enter:   13
# Escape:  27
# Space:   32
# Delete:  127
# p:       112
# F3:      284
# F4:      285
# 0 - 9:   48 - 57
# Up:      273
# Down:    274
# Left:    276
# Right:   275

# We're not faking a joystick axis anymore
#joy1axis1 = 276, 275
#joy1axis2 = 273, 274

# PSP ctrls
#joy1button1 = 49
#joy1button2 = 32
#joy1button3 = 127
#joy1button4 = 112
#joy1button5 = 27
#joy1button6 = 13
#joy1button7 = 274
#joy1button8 = 276
#joy1button9 = 273
#joy1button10 = 275
#joy1button11 = 285
#joy1button12 = 284

joy1button1 = 32
joy1button2 = 13
joy1button3 = 284
joy1button4 = 285
joy1button5 = 9
joy1button7 = 27
joy1button8 = 112


code:
/* MegaZeux
 *
 * Copyright (C) 2009 Alistair John Strachan <alistair@devzero.co.uk>
 * Copyright (C) 2007-2009 Kevin Vance <kvance@kvance.com>
 *
 * This program 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 of
 * the License, or (at your option) any later version.
 *
 * This program 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 this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 */

#include <stdarg.h>

#include "../../src/platform.h"
#undef main

#include "../../src/util.h"

#include <fat.h>
#include "render.h"
#include "ram.h"
#include "dlmalloc.h"
#include "exception.h"

// from arch/nds/event.c
extern void nds_update_input(void);

// Timer code stolen from SDL (LGPL)

#define timers2ms(tlow,thigh)(tlow | (thigh<<16)) >> 5

void delay(Uint32 ms)
{
  Uint32 now;
  now = timers2ms(TIMER0_DATA, TIMER1_DATA);
  while((Uint32)timers2ms(TIMER0_DATA, TIMER1_DATA) < now + ms)
    ;
}

Uint32 get_ticks(void)
{
  return timers2ms(TIMER0_DATA, TIMER1_DATA);
}

static void timer_init(void)
{
  // TIMER0, TIMER1: Tick timer
  TIMER0_DATA = 0;
  TIMER1_DATA = 0;
  TIMER0_CR = TIMER_ENABLE | TIMER_DIV_1024;
  TIMER1_CR = TIMER_ENABLE | TIMER_CASCADE;

  // TIMER2: Input handler IRQ
  // We could do this in vblank, but sometimes we need to block new input
  // events when the event queue is in use.  Using a hardware timer, we can
  // keep the graphics flicker running.
  TIMER2_DATA = TIMER_FREQ_1024(60);
  TIMER2_CR = TIMER_ENABLE | TIMER_DIV_1024 | TIMER_IRQ_REQ;
  irqSet(IRQ_TIMER2, nds_update_input);
  irqEnable(IRQ_TIMER2);
}

/*
 * Special handling for debug output
 */

static __attribute__((format(printf, 2, 0)))
void handle_debug_info(const char *type, const char *format, va_list args)
{
  if(!has_video_initialized())
  {
    iprintf("%s: ", type);
    viprintf(format, args);
    fflush(stdout);
  }
  // TODO: Have a debug console on the subscreen show these.
}

void info(const char *format, ...)
{
  va_list args;
  va_start(args, format);
  handle_debug_info("INFO", format, args);
  va_end(args);
}

void warn(const char *format, ...)
{
  va_list args;
  va_start(args, format);
  handle_debug_info("WARNING", format, args);
  va_end(args);
}

#ifdef DEBUG
void debug(const char *format, ...)
{
  va_list args;
  va_start(args, format);
  handle_debug_info("DEBUG", format, args);
  va_end(args);
}
#endif // DEBUG

bool platform_init(void)
{
  powerOn(POWER_ALL);
  setMzxExceptionHandler();

  if(!fatInitDefault())
  {
    iprintf("Unable to initialize FAT.\n"
            "Check your DLDI driver.\n");
    return false;
  }

  // If the "extra RAM" is missing, warn the user
  if(!REG_DSIMODE)
    if(nds_ram_init(DETECT_RAM))
      nds_ext_lock();

  timer_init();

  // Enable vblank interrupts, but don't install the handler until the
  // graphics have initialized.
  irqEnable(IRQ_VBLANK);

  return true;
}

void platform_quit(void)
{
  nds_ext_unlock();
}

// argc/argv is unreliable on NDS.  On my R4, argv[0] is an invalid pointer.

int main(int argc, char *argv[])
{
  static char _argv0[] = SHAREDIR "/mzxrun.nds";
  static char *_argv[] = {_argv0};
  consoleDemoInit();
  real_main(1, _argv);
  while(true)
    ;
}


code:
/**********************************
  Copyright (C) Rick Wong (Lick)

 Credits: Amadeus, Chishm, Cory1492, Lazy1, Pepsiman, Viruseb

Dual licensed under the MIT (http://www.opensource.org/licenses/mit-license.php)
and GPLv2 (http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt) licenses.

***********************************/
#ifndef __RAM
#define __RAM

#include <nds/ndstypes.h>

#ifdef __cplusplus
extern "C" {
#endif

typedef enum { DETECT_RAM=0, SC_RAM, M3_RAM, OPERA_RAM, G6_RAM, EZ_RAM } RAM_TYPE;

//  Call this before the others
bool  ram_init (RAM_TYPE);

//  Returns the type of the RAM device
RAM_TYPE   ram_type (void);

//  Returns the type of the RAM device in a string
const char*   ram_type_string (void);

//  Returns the total amount of RAM in bytes
u32   ram_size (void);

//  Unlocks the RAM and returns a pointer to the begin
vu16* ram_unlock (void);

//  Locks the RAM
void  ram_lock (void);

//  enable = set lowest waitstates, disable = set default waitstates
void  ram_turbo (bool enable);

#ifdef __cplusplus
}
#endif
#endif


code:
/* MegaZeux
 *
 * Copyright (C) 2007-2009 Kevin Vance <kvance@kvance.com>
 *
 * This program 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 of
 * the License, or (at your option) any later version.
 *
 * This program 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 this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 */

/* NOMENCLATURE NOTICE:
 * What we call the "subscreen" is called the "main screen" by libnds.
 * But in terms of functionality, it only contains auxiliary screen modes.
 */

#include <string.h>

#include "render.h"

#include "../../src/renderers.h"
#include "../../src/graphics.h"
#include "../../src/render.h"

#include <nds/arm9/keyboard.h>

// These variables control the panning along the 1:1 "main" screen.
static int cell_pan_x = 0;
static int cell_pan_y = 0;

// This table stores scroll register values for each scanline.
static u16 scroll_table[192];

// This table maps palette color combinations to their index in BG_PALETTE.
static int palette_idx_table[16][16];

// If we're looking around with the mouse, ignore the next call to focus.
static bool mouselook;

// The current transition state.
static struct {
  enum { TRANSITION_NONE = 0, TRANSITION_IN, TRANSITION_OUT } state;
  int time;
} transition;

// The subscreen can display different information.
enum Subscreen_Mode last_subscreen_mode;
enum Subscreen_Mode subscreen_mode;

// Forward declarations
void nds_mainscreen_focus(struct graphics_data *graphics, Uint32 x, Uint32 y);
static void nds_keyboard_scroll_in(void);
static void nds_keyboard_scroll_out(void);

// Do this every vblank irq:
static void nds_on_vblank(void)
{
  /* Handle sleep mode. */
  nds_sleep_check();

  /* Do all special video handling. */
  nds_video_rasterhack();
  nds_video_jitter();
  nds_video_do_transition();
}

bool is_scaled_mode(enum Subscreen_Mode mode)
{
  return (mode == SUBSCREEN_SCALED);
}

static void palette_idx_table_init(void)
{
  // Start the palette at 16, reserving colors 0-16 for the overlay.
  const int offset = 16;
  int idx;

  // Store the normal palette sequentially.
  for(idx = 0; idx < 16; idx++)
    palette_idx_table[idx][idx] = idx + offset;

  // Store the blends after that.
  idx += offset;
  for(int a = 0; a < 16; a++)
  {
    for(int b = a+1; b < 16; b++)
    {
      palette_idx_table[a][b] = idx;
      palette_idx_table[b][a] = idx;
      idx++;
    }
  }
}

static void nds_subscreen_scaled_init(void)
{
  int xscale = (int)(320.0/256.0 * 256.0);
  int yscale = (int)(350.0/192.0 * 256.0);

  /* Use banks A and B for the ZZT screen. */
  videoSetMode(MODE_5_2D | DISPLAY_BG2_ACTIVE | DISPLAY_BG3_ACTIVE);
  vramSetBankA(VRAM_A_MAIN_BG);
  vramSetBankB(VRAM_B_MAIN_BG);

  /* Use flickered alpha lerp to scale 320x350 to 256x192. */
  /* (Bump the BG up to base 3 (+0xc0000) to make room for the keyboard.) */
  REG_BG2CNT = BG_BMP8_512x512 | BG_BMP_BASE(3) | BG_PRIORITY(0);
  REG_BG2PA  = xscale;
  REG_BG2PB  = 0;
  REG_BG2PC  = 0;
  REG_BG2PD  = yscale;
  REG_BG2X   = 0;
  REG_BG2Y   = 0;

  REG_BG3CNT = BG_BMP8_512x512 | BG_BMP_BASE(3) | BG_PRIORITY(0);
  REG_BG3PA  = xscale;
  REG_BG3PB  = 0;
  REG_BG3PC  = 0;
  REG_BG3PD  = yscale;
  REG_BG3X   = 0;
  REG_BG3Y   = 0;

  /* Enable BG2/BG3 blending. */
  REG_BLDCNT   = BLEND_ALPHA | BLEND_SRC_BG2 | BLEND_DST_BG3;
  REG_BLDALPHA = (8 << 8) | 8; /* 50% / 50% */

  update_palette();
  update_screen();
}

static void nds_subscreen_keyboard_init(void)
{
  Keyboard *kb = keyboardGetDefault();

  // BG0: Keyboard 256x512 text, map following tiles (45056 bytes total)
  // Clear the keyboard area before drawing it.
  dmaFillWords(0, BG_MAP_RAM(20), 4096);
  keyboardInit(kb, 0, BgType_Text4bpp, BgSize_T_256x512, 20, 0, true, true);
  bgHide(0);
  kb->scrollSpeed = 7;
  transition.time = 0;
  transition.state = TRANSITION_IN;
}

static void nds_subscreen_keyboard_exit(void)
{
  transition.time = 0;
  transition.state = TRANSITION_OUT;
}

static void nds_mainscreen_init(struct graphics_data *graphics)
{
  u16 *vram;
  int i;

  graphics->fullscreen = true;
  graphics->resolution_width = 640;
  graphics->resolution_height = 350;

  /* Use bank C for the text screens. */
  videoSetModeSub(MODE_0_2D | DISPLAY_BG0_ACTIVE | DISPLAY_BG1_ACTIVE);
  vramSetBankC(VRAM_C_SUB_BG);

  /* BG0: foreground characters. */
  REG_BG0CNT_SUB  = BG_64x32 | BG_COLOR_16 | BG_MAP_BASE(0) |
                    BG_TILE_BASE(1);
  REG_BG0HOFS_SUB = 0;
  REG_BG0VOFS_SUB = 0;

  /* BG1: background characters. */
  REG_BG1CNT_SUB  = BG_64x32 | BG_COLOR_16 | BG_MAP_BASE(2) |
                    BG_TILE_BASE(1);
  REG_BG1HOFS_SUB = 0;
  REG_BG1VOFS_SUB = 0;

  // By default, pan to the center of the screen.
  nds_mainscreen_focus(graphics, 640/2, 350/2);

  // Add a solid tile for background colors.
  vram = (u16*)BG_TILE_RAM_SUB(1) + 32*256;
  for(i = 0; i < 16; i++)
    *(vram++) = 1 << 12 | 1 << 8 | 1 << 4 | 1;
}

void nds_video_jitter(void)
{
  const u16 jitterF4[] = {
    0x060, 0x000,   /* 0.375, 0.000 */
    0x020, 0x100,   /* 0.125, 1.000 */
    0x0e0, 0x000,   /* 0.875, 0.000 */
    0x0a0, 0x100,   /* 0.625, 1.000 */
  };
  static size_t jidx = 0;    /* jitter table index */

  /* Jitter the backgrounds for scaled mode. */
  if(jidx >= sizeof(jitterF4)/sizeof(*jitterF4))
    jidx = 0;

  REG_BG2X = jitterF4[jidx];
  REG_BG2Y = jitterF4[jidx+1];
  REG_BG3X = jitterF4[jidx+2];
  REG_BG3Y = jitterF4[jidx+3];
  jidx += 4;
}

/* Use HBlank DMA to load row scroll values in for each line. */
void nds_video_rasterhack(void)
{
  /* Prepare DMA transfer. */
  DMA1_CR         = 0;
  REG_BG0VOFS_SUB = scroll_table[0];
  DMA1_SRC        = (u32)(scroll_table + 1);
  DMA1_DEST       = (u32)&REG_BG0VOFS_SUB;
  DMA1_CR         = DMA_DST_FIX | DMA_SRC_INC | DMA_REPEAT | DMA_16_BIT |
                    DMA_START_HBL | DMA_ENABLE | 1;

  DMA2_CR         = 0;
  REG_BG1VOFS_SUB = scroll_table[0];
  DMA2_SRC        = (u32)(scroll_table + 1);
  DMA2_DEST       = (u32)&REG_BG1VOFS_SUB;
  DMA2_CR         = DMA_DST_FIX | DMA_SRC_INC | DMA_REPEAT | DMA_16_BIT |
                    DMA_START_HBL | DMA_ENABLE | 1;
}

// Handle the transition animation.
void nds_video_do_transition(void)
{
  // On transitioning in, use the current screen.  Out, use the last screen.
  enum Subscreen_Mode mode;
  if(transition.state == TRANSITION_NONE)
    return;
  if(transition.state == TRANSITION_IN)
    mode = subscreen_mode;
  else                // TRANSITION_OUT
    mode = last_subscreen_mode;

  if(mode == SUBSCREEN_KEYBOARD)
  {
    if(transition.state == TRANSITION_IN)
      nds_keyboard_scroll_in();
    else
      nds_keyboard_scroll_out();
  }
}

static void nds_keyboard_scroll_in(void)
{
  Keyboard *kb = keyboardGetDefault();
  if(transition.time == 0)
  {
    // Show the background.
    kb->visible = 1;
    bgSetScroll(kb->background, 0, -192);
    bgUpdate();
    bgShow(kb->background);
  }
  else
  {
    // Scroll 80 pixels.
    if(transition.time < 80)
    {
      bgSetScroll(kb->background, 0, -192 + transition.time);
    }
    else
    {
      bgSetScroll(kb->background, 0, kb->offset_y);
      transition.state = TRANSITION_NONE;
    }
    bgUpdate();
  }

  transition.time += kb->scrollSpeed;
}

static void nds_keyboard_scroll_out(void)
{
  Keyboard *kb = keyboardGetDefault();
  if(transition.time == 0)
    kb->visible = 0;

  if(transition.time >= 80)
  {
    // Hide the background.
    bgHide(kb->background);
    transition.state = TRANSITION_NONE;
  }
  else
  {
    // Scroll 80 pixels.
    bgSetScroll(kb->background, 0, kb->offset_y - transition.time);
    bgUpdate();
  }

  transition.time += kb->scrollSpeed;
}

void nds_sleep_check(void)
{
  static bool asleep = false;

  // Check if we were just woken up.
  if(asleep)
  {
    asleep = false;

    // Wait a bit until returning power.
    while(REG_VCOUNT != 0);
    while(REG_VCOUNT == 0);
    while(REG_VCOUNT != 0);
    powerOn(POWER_ALL);
  }

  // Check if it's time to go to sleep.
  if(keysDown() & KEY_LID)
  {
    // Cancel DMA from raster effects.
    DMA1_CR = 0;
    DMA2_CR = 0;

    // Power everything off.
    powerOff(POWER_ALL);
    asleep = true;
  }
}

static bool nds_init_video(struct graphics_data *graphics,
 struct config_info *config)
{
  lcdMainOnBottom();

  // Build the palette blend mapping table.
  palette_idx_table_init();

  // Start with scaled mode.
  subscreen_mode = SUBSCREEN_SCALED;
  last_subscreen_mode = SUBSCREEN_MODE_INVALID;
  nds_subscreen_scaled_init();

  // Initialize the 1:1 scaled "main" screen.
  nds_mainscreen_init(graphics);

  mouselook = false;
  transition.state = TRANSITION_NONE;
  transition.time = 0;

  // Now that we're initialized, install the vblank handler.
  irqSet(IRQ_VBLANK, nds_on_vblank);

  return true;
}

static bool nds_check_video_mode(struct graphics_data *graphics,
 int width, int height, int depth, bool fullscreen, bool resize)
{
  return true;  // stub
}

static bool nds_set_video_mode(struct graphics_data *graphics,
 int width, int height, int depth, bool fullscreen, bool resize)
{
  return true;	// stub
}

// Render the scaled screen.
static void nds_render_graph_scaled(struct graphics_data *graphics)
{
  int const WIDTH_IN_PIXELS  = 80 * 4;  // Screen width in pixels
  int const WIDTH_IN_CELLS   = 80;      // Screen width in chars
  int const CHARACTER_HEIGHT = 14;      // Character height in pixels
  int const VRAM_WIDTH       = 512;     // HW surface width in pixels

  struct char_element *text_cell = graphics->text_video;
  u32 *vram_ptr  = (u32*)BG_BMP_RAM(3);
  int chars, lines;

  /* Iterate over every character in text memory. */
  int columns = 0;
  for(chars = 0; chars < 80*25; chars++)
  {
    /* Extract the character, and fore/back colors. */
    int chr = (*text_cell).char_value & 0xFF;
    int bg  = (*text_cell).bg_color   & 0x0F;
    int fg  = (*text_cell).fg_color   & 0x0F;

    // Construct a table mapping charset two-bit pairs to palette entries.
    u32 bg_idx = bg + 16; // Solid colors are offset by 16.
    u32 fg_idx = fg + 16;
    u32 mix_idx = palette_idx_table[bg][fg];
    u32 pal_tbl[4] = {
      bg_idx,   /* 00: bg color    */
      mix_idx,  /* 01: fg+bg blend */
      mix_idx,  /* 10: "         " */
      fg_idx    /* 11: fg color    */
    };

    /* Iterate over every line in the character. */
    u8 *line = graphics->charset + CHARACTER_HEIGHT * chr;
    u32* vram_next = vram_ptr + 1;
    for(lines = 0; lines < CHARACTER_HEIGHT; lines++)
    {
      /* Plot four pixels using the two-bit pair table. */
      u32 fourpx = pal_tbl[((*line) & 0xC0) >> 6]      ;
      fourpx    |= pal_tbl[((*line) & 0x30) >> 4] <<  8;
      fourpx    |= pal_tbl[((*line) & 0x0C) >> 2] << 16;
      fourpx    |= pal_tbl[((*line) & 0x03)     ] << 24;
      *((u32*)vram_ptr) = fourpx;

      /* Advance to the next line. */
      line++;
      vram_ptr += VRAM_WIDTH/4;
    }

    /* Advance to the next character. */
    text_cell++;
    vram_ptr = vram_next;

    /* Insert padding at the end of a line. */
    columns++;
    if(columns == WIDTH_IN_CELLS)
    {
      columns = 0;
      vram_ptr += (VRAM_WIDTH - WIDTH_IN_PIXELS)/4;
      vram_ptr += VRAM_WIDTH * 13 / 4;
    }
  }
}

static void nds_render_graph_1to1(struct graphics_data *graphics)
{
  struct char_element *text_start =
   graphics->text_video + cell_pan_x + 80*cell_pan_y;
  struct char_element *text_cell  = text_start;
  u16 *vram;
  int x, y;

  /* Plot the first 32x30 foreground tiles. */
  vram = (u16*)BG_MAP_RAM_SUB(0);
  for(y = 0; y < 15; y++)
  {
    // Draw the top halves of this line for tile_offset=0, then the bottom
    // halves for tile_offset=1.
    int tile_offset = 0;

    while(tile_offset != 2)
    {
      int chr;
      int fg;

      for(x = 0; x < 32; x++)
      {
        // Extract the character and the foreground color.
        chr = (*text_cell).char_value & 0xFF;
        fg  = (*text_cell).fg_color   & 0x0F;

        *vram = (2*chr + tile_offset) | (fg << 12);

        text_cell++;
        vram++;
      }

      // Plot the 33rd column (in the next plane)
      chr = (*text_cell).char_value & 0xFF;
      fg  = (*text_cell).fg_color   & 0x0F;
      *(vram+992) = (2*chr + tile_offset) | (fg << 12);

      // Move back.
      text_cell -= 32;
      tile_offset++;
    }

    // Move to the next row.
    text_cell += 80;
  }

  // Plot the first 32x30 background tiles.
  text_cell = text_start;
  vram = (u16*)BG_MAP_RAM_SUB(2);
  for(y = 0; y < 15; y++)
  {
    // Draw the top halves of this line for tile_offset=0, then the bottom
    // halves for tile_offset=1.
    int tile_offset = 0;

    while(tile_offset != 2)
    {
      int bg;

      for(x = 0; x < 32; x++)
      {
        // Extract the background color only.
        bg  = (*text_cell).bg_color & 0x0F;

        *vram = 512 | (bg << 12);

        text_cell++;
        vram++;
      }

      // Plot the 33rd column (in the next plane)
      bg = (*text_cell).bg_color & 0x0F;
      *(vram+992) = 512 | (bg << 12);

      // Move back.
      text_cell -= 32;
      tile_offset++;
    }

    // Move to the next row.
    text_cell += 80;
  }
}

static void nds_render_graph(struct graphics_data *graphics)
{
  // Always render the scaled screen.
  nds_render_graph_scaled(graphics);

  // Always render the 1:1 "main" screen.
  nds_render_graph_1to1(graphics);
}

static void nds_update_palette_entry(struct rgb_color *palette, Uint32 idx)
{
  struct rgb_color color1 = palette[idx];
  int idx2;

  if(idx < 16)
  {
    // Update the mainscreen palette.
    BG_PALETTE_SUB[16*idx + 1] = RGB15(color1.r/8, color1.g/8, color1.b/8);

    // Update the subscreen palette.
    /* Iterate over an entire column of the palette, blending this color
     * with all 16 other colors (including itself). */
    for(idx2 = 0; idx2 < 16; idx2++)
    {
      /* Average the colors while reducing their accuracy to 5 bits. */
      struct rgb_color color2 = palette[idx2];
      u16 r = (color1.r + color2.r) / 16;
      u16 g = (color1.g + color2.g) / 16;
      u16 b = (color1.b + color2.b) / 16;

      BG_PALETTE[ palette_idx_table[idx][idx2] ] = RGB15(r, g, b);
    }
  }
}

static void nds_update_colors(struct graphics_data *graphics,
 struct rgb_color *palette, Uint32 count)
{
  Uint32 i;

  for(i = 0; i < count; i++)
    nds_update_palette_entry(palette, i);
}

static void nds_resize_screen(struct graphics_data *graphics, int w, int h)
{
  // stub
}

static void nds_render_cursor(struct graphics_data *graphics,
 Uint32 x, Uint32 y, Uint8 color, Uint8 lines, Uint8 offset)
{
  // stub
}

static void nds_render_mouse(struct graphics_data *graphics,
 Uint32 x, Uint32 y, Uint8 w, Uint8 h)
{
  // stub
}

static void nds_sync_screen(struct graphics_data *graphics)
{
  // stub
}

static void nds_remap_char(struct graphics_data *graphics, Uint16 chr)
{
  if(chr < 256)
  {
    /* Each character is 64 bytes.  Advance the vram pointer. */
    u16* vram = (u16*)BG_TILE_RAM_SUB(1) + 32*chr;
    u8* charset = graphics->charset + 14*chr;

    /* Iterate over every line. */
    u8 *end = charset + 14;
    for(; charset < end; charset++)
    {
      u16 line = *charset;

      /* Plot 8 pixels, 4 pixels at a time. */
      *(vram++) = ((line >> 7) & 1)       |
                  ((line >> 6) & 1) <<  4 |
                  ((line >> 5) & 1) <<  8 |
                  ((line >> 4) & 1) << 12;
      *(vram++) = ((line >> 3) & 1)       |
                  ((line >> 2) & 1) <<  4 |
                  ((line >> 1) & 1) <<  8 |
                  ((line     ) & 1) << 12;
    }
  }
}

static void nds_remap_charsets(struct graphics_data *graphics)
{
  int i;

  for(i = 0; i < 256; i++)
    nds_remap_char(graphics, i);
}

// Focus on a given screen position (in pixels up to 640x350).
void nds_mainscreen_focus(struct graphics_data *graphics, Uint32 x, Uint32 y)
{
  static Uint32 old_x = -1;
  static Uint32 old_y = -1;
  int scroll_x, scroll_y, i, ypos, ycounter;
  u16 *sptr;

  if(mouselook)
  {
    // We're mouselooking, don't move the focus.
    return;
  }

  if(x == old_x && y == old_y)
  {
    // Already focused there, quick abort.
    return;
  }
  old_x = x;
  old_y = y;

  // Clamp the coordinates so we stay within the screen.
  if(x < 132) x = 132;
  if(x > 508) x = 508;
  if(y <  96) y =  96;
  if(y > 253) y = 253;

  // Move to the top-left corner.
  x -= 264/2;
  y -= 192/2;

  // Find the relevant cell coordinates and scroll offsets into those cells.
  cell_pan_x = x / 8;
  scroll_x   = x % 8;
  cell_pan_y = y / 14;
  scroll_y   = y % 14;

  // Adjust the X scroll registers now.
  REG_BG0HOFS_SUB = scroll_x;
  REG_BG1HOFS_SUB = scroll_x;

  // Recalculate the scroll table.
  sptr     = scroll_table;
  ypos     = scroll_y;
  ycounter = scroll_y;
  for(i = 0; i < 192; i++)
  {
    (*sptr++) = ypos;

    ycounter++;
    if(ycounter == 14)
    {
      ycounter = 0;
      ypos += 2;
    }
  }

  // Redraw the map with our new coords.
  nds_render_graph_1to1(graphics);
}

void render_nds_register(struct renderer *renderer)
{
  memset(renderer, 0, sizeof(struct renderer));
  renderer->init_video = nds_init_video;
  renderer->check_video_mode = nds_check_video_mode;
  renderer->set_video_mode = nds_set_video_mode;
  renderer->update_colors = nds_update_colors;
  renderer->resize_screen = nds_resize_screen;
  renderer->remap_charsets = nds_remap_charsets;
  renderer->remap_char = nds_remap_char;
  renderer->get_screen_coords = get_screen_coords_centered;
  renderer->set_screen_coords = set_screen_coords_centered;
  renderer->render_graph = nds_render_graph;
  renderer->render_cursor = nds_render_cursor;
  renderer->render_mouse = nds_render_mouse;
  renderer->sync_screen = nds_sync_screen;
  renderer->focus_pixel = nds_mainscreen_focus;
}

void nds_subscreen_switch(void)
{
  // Don't switch during a transition.
  if(transition.state != TRANSITION_NONE)
    return;

  // Call appropriate exit function.
  if(subscreen_mode == SUBSCREEN_KEYBOARD)
    nds_subscreen_keyboard_exit();

  // Cycle between modes.
  last_subscreen_mode = subscreen_mode;
  if(++subscreen_mode == SUBSCREEN_MODE_COUNT)
    subscreen_mode = 0;

  // Call the appropriate init function.
  if(subscreen_mode == SUBSCREEN_KEYBOARD)
    nds_subscreen_keyboard_init();
}

void nds_mouselook(bool enabled)
{
  mouselook = enabled;
}

// Display a warning screen.
void warning_screen(u8 *pcx_data)
{
  sImage image;

  consoleDemoInit();

  if(loadPCX(pcx_data, &image))
  {
    u16 *img = image.image.data16;
    int ox, oy;
    u16 *gfx;

    videoSetMode(MODE_5_2D | DISPLAY_BG2_ACTIVE);
    vramSetBankA(VRAM_A_MAIN_BG);

    REG_BG2CNT = BG_BMP8_256x256;
    REG_BG2PA  = 1 << 8;
    REG_BG2PB  = 0;
    REG_BG2PC  = 0;
    REG_BG2PD  = 1 << 8;
    REG_BG2X   = 0;
    REG_BG2Y   = 0;

    memcpy(BG_PALETTE, image.palette, 2*256);

    ox = 128 - image.width/2;
    oy = 96 - image.height/2;
    gfx = (u16*)(0x06000000 + 256*oy + ox);

    for(int row = 0; row < image.height; row++)
    {
      for(int col = 0; col < image.width/2; col++)
        *(gfx++) = *(img++);
      gfx += ox;
    }

    while(!keysDown())
      scanKeys();

    imageDestroy(&image);
  }
}


code:
/* MegaZeux
 *
 * Copyright (C) 2007 Kevin Vance <kvance@kvance.com>
 *
 * This program 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 of
 * the License, or (at your option) any later version.
 *
 * This program 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 this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 */

#ifndef __RENDER_NDS_H
#define __RENDER_NDS_H

#include "compat.h"
#include "graphics.h"

__M_BEGIN_DECLS

// The subscreen can display different information.
enum Subscreen_Mode
{
  SUBSCREEN_SCALED = 0,
  SUBSCREEN_KEYBOARD,
  SUBSCREEN_MODE_COUNT,
  SUBSCREEN_MODE_INVALID
};

extern enum Subscreen_Mode subscreen_mode;

bool is_scaled_mode(enum Subscreen_Mode mode);

// Call these 4 functions every vblank.
void nds_sleep_check(void);
void nds_video_jitter(void);
void nds_video_rasterhack(void);
void nds_video_do_transition(void);

// Toggle to the next subscreen mode.
void nds_subscreen_switch(void);

// Turn on or off mouselook (the next focus call will be ignored).
void nds_mouselook(bool enable);

// Display a warning screen (PCX format).
void warning_screen(u8 *pcx_data);

__M_END_DECLS

#endif // __RENDER_NDS_H


code:
# placebo makefile

SUPPRESS_BUILD=1

mzx:
	@echo "Please run config.sh before make!"
	@echo
	@echo "Type ./config.sh for more information."

distclean:

utils:


code:
TOOLCHAIN       ?= /usr/local/pandora/arm-2009q3
CROSS_COMPILE   ?= $(TOOLCHAIN)/bin/arm-none-linux-gnueabi-
PANDORA_LIBPATH ?= $(PREFIX)/lib
PANDORA_INCPATH ?= $(PREFIX)/include

CC              = $(CROSS_COMPILE)gcc
CXX             = $(CROSS_COMPILE)g++
AR              = $(CROSS_COMPILE)ar
OBJCOPY         = $(CROSS_COMPILE)objcopy
STRIP           = $(CROSS_COMPILE)strip --strip-unneeded

ARCH_CFLAGS     = -march=armv7-a -mtune=cortex-a8 -fPIC -I$(PANDORA_INCPATH)
ARCH_CXXFLAGS   = $(ARCH_CFLAGS)

ARCH_LDFLAGS    = -L$(PANDORA_LIBPATH)

LIBPNG_CFLAGS   = -I$(PANDORA_INCPATH)/libpng12
LIBPNG_LDFLAGS  = -lpng12 -lz
SDL_CFLAGS      = `$(TOOLCHAIN)/usr/bin/sdl-config --cflags`
SDL_LDFLAGS     = `$(TOOLCHAIN)/usr/bin/sdl-config --libs`

DSOLDFLAGS      = -shared
DSOPRE          = lib
DSOPOST         = .so
DSORPATH        = -Wl,-rpath,$(LIBDIR)
DSOSONAME       = -Wl,-soname,

include arch/zip.inc


code:
WRITE ME

./config.sh --platform pandora --prefix /usr/local/pandora/arm-2009q3/usr \
            --disable-gl --disable-modular --enable-tremor --enable-release

path: arch/pandora/README


code:
#
# psp makefile generics
#

.PHONY: package-pbp

#
# PSP toolchain overrides
#
CC      = psp-gcc
CXX     = psp-g++
AR      = psp-ar
OBJCOPY = psp-objcopy
STRIP   = /bin/true

# flag PSP build and link in c/psppower
ARCH_CFLAGS   += -G0 -I${PREFIX}/sdk/include
ARCH_CXXFLAGS += ${ARCH_CFLAGS}
ARCH_LDFLAGS  += -lc -lpsppower

package: mzx
	psp-fixup-imports ${mzxrun}
	mksfo 'MegaZeux ${VERSION}' PARAM.SFO
	psp-strip ${mzxrun} -o ${mzxrun}.strip
	convert -scale 80x80 -border 32x0 -bordercolor transparent \
	        contrib/icons/quantump.png ICON0.PNG
	pack-pbp EBOOT.PBP PARAM.SFO ICON0.PNG NULL \
		 NULL NULL NULL ${mzxrun}.strip NULL
	rm -f ${mzxrun}.strip

clean:
	@rm -f EBOOT.PBP PARAM.SFO ICON0.PNG

build: package ${build}
	${CP} arch/psp/pad.config ${build}
	${CP} EBOOT.PBP ${build}
	${RM} ${build}/${mzxrun} ${build}/${mzxrun}.debug

include arch/zip.inc


code:
BUILDING MEGAZEUX FOR PSP

As of 2.81d, MegaZeux can be built for PSP. The only supported toolchain for
this is devkitPSP.

BUILDING DEPENDENCIES

MegaZeux depends on libTremor and SDL, on the PSP platform. Check these modules
out from the ps2dev SVN repositories.

svn co svn://svn.pspdev.org/psp/trunk/libTremor
svn co svn://svn.pspdev.org/psp/trunk/SDL

First, build libTremor:

cd libTremor
LDFLAGS="-L`psp-config --pspsdk-path`/lib -lc -lpspuser" \
./autogen.sh --host psp --prefix=`psp-config --psp-prefix`
make
make install
cd ..

Then build SDL:

cd SDL
./autogen.sh
LDFLAGS="-L`psp-config --pspsdk-path`/lib -lc -lpspuser" \
./configure --host psp --prefix=`psp-config --psp-prefix`
make
make install
cd ..

BUILDING MEGAZEUX FROM SOURCES

Extract the latest version of MegaZeux, or obtain it from SVN. Before starting,
we need to add another path to the PATH variable, so that SDL may be correctly
detected. You should have the $DEVKITPRO and $DEVKITPSP variables defined
correctly. I will assume that these exist and are correct. Enter the
following:

export PATH=$DEVKITPSP/psp/bin:$PATH

cd megazeux
./config.sh --platform psp --prefix $PSPDEV/psp --optimize-size \
            --disable-editor --disable-helpsys --disable-utils \
            --disable-libpng --enable-release --enable-meter
make package

This will create an EBOOT.PBP.

PACKAGING THE BUILD

make archive

To construct a bootable zip archive.

--ajs

path: arch/psp/README


code:
# 1 Triangle
# 2 Circle
# 3 Cross
# 4 Square
# 5 Left trigger
# 6 Right trigger
# 7 Down
# 8 Left
# 9 Up
# 10 Right
# 11 Select
# 12 Start
# 13 Home
# 14 Hold

# Enter:   13
# Escape:  27
# Space:   32
# Delete:  127
# p:       112
# F3:      284
# F4:      285
# 0 - 9:   48 - 57
# Up:      273
# Down:    274
# Left:    276
# Right:   275

joy1axis1 = 276, 275
joy1axis2 = 273, 274

joy1button1 = 49
joy1button2 = 32
joy1button3 = 127
joy1button4 = 112
joy1button5 = 27
joy1button6 = 13
joy1button7 = 274
joy1button8 = 276
joy1button9 = 273
joy1button10 = 275
joy1button11 = 285
joy1button12 = 284


code:
#
# linux makefile generics
#

.PHONY: install

DSOLDFLAGS = -shared
DSOPRE     = lib
DSOPOST    = .so
DSORPATH   = -Wl,-rpath,${LIBDIR}
DSOSONAME  = -Wl,-soname,

ifeq (${USER},root)
install = install -o root
else
install = install
endif

# linux os specific install target
install:
	@${install} -m 0755 -d \
		${DESTDIR}${SYSCONFDIR} \
		${DESTDIR}${SHAREDIR} \
		${DESTDIR}${SHAREDIR}/icons \
		${DESTDIR}${SHAREDIR}/applications \
		${DESTDIR}${SHAREDIR}/megazeux \
		${DESTDIR}${SHAREDIR}/megazeux/assets && \
	 ${install} -m 0644 assets/default.chr assets/edit.chr \
		assets/smzx.pal ${DESTDIR}${SHAREDIR}/megazeux/assets && \
	 ${install} -m 0644 config.txt ${DESTDIR}${SYSCONFDIR}/megazeux-config && \
	 ${install} -m 0644 contrib/icons/quantump.png \
		${DESTDIR}${SHAREDIR}/icons/megazeux.png && \
	 ${install} -m 0644 arch/unix/megazeux.desktop \
		${DESTDIR}${SHAREDIR}/applications/megazeux.desktop
ifeq (${BUILD_HELPSYS},1)
	@${install} -m 0644 assets/help.fil \
		${DESTDIR}${SHAREDIR}/megazeux/assets
endif
ifeq (${BUILD_MODULAR},1)
	@${install} -m 0755 -d ${DESTDIR}${LIBDIR} && \
	 ${install} -m 0755 ${core_target} ${DESTDIR}${LIBDIR} && \
	 ${install} -m 0755 ${editor_target} ${DESTDIR}${LIBDIR}
endif
ifeq (${BUILD_MZXRUN},1)
	@${install} -m 0755 -d ${DESTDIR}${GAMESDIR} && \
	 ${install} -m 0755 ${mzxrun} ${DESTDIR}${GAMESDIR}/ && \
	 ${install} -m 0644 arch/unix/mzxrun.desktop \
		${DESTDIR}${SHAREDIR}/applications/mzxrun.desktop
endif
ifeq (${BUILD_EDITOR},1)
	@${install} -m 0755 -d ${DESTDIR}${GAMESDIR} && \
	 ${install} -m 0755 ${mzx} ${DESTDIR}${GAMESDIR}/ && \
	 ${install} -m 0644 assets/ascii.chr assets/blank.chr \
		assets/smzx.chr ${DESTDIR}${SHAREDIR}/megazeux/assets
endif
ifeq (${BUILD_UTILS},1)
	@${install} -m 0755 -d ${DESTDIR}${BINDIR} && \
	 ${install} -m 0755 ${checkres} ${DESTDIR}${BINDIR}/ && \
	 ${install} -m 0755 ${downver} ${DESTDIR}${BINDIR}/ && \
	 ${install} -m 0755 ${hlp2txt} ${DESTDIR}${BINDIR}/ && \
	 ${install} -m 0755 ${txt2hlp} ${DESTDIR}${BINDIR}/
endif
ifeq (${BUILD_RENDER_GL_PROGRAM},1)
	@${install} -m 0755 -d \
		${DESTDIR}${SHAREDIR}/megazeux/assets/shaders && \
	 ${install} -m 0644 assets/shaders/cursor.frag \
		assets/shaders/cursor.vert \
		assets/shaders/mouse.frag \
		assets/shaders/mouse.vert \
		assets/shaders/scaler.frag \
		assets/shaders/scaler.vert \
		assets/shaders/tilemap.frag \
		assets/shaders/tilemap.vert \
		assets/shaders/tilemap.smzx12.frag \
		assets/shaders/tilemap.smzx3.frag \
		${DESTDIR}${SHAREDIR}/megazeux/assets/shaders
endif


code:
MEGAZEUX ON UNIX / UNIX CLONES

MegaZeux has been tested as compiling on the following UNIX / UNIX clones:

 - Linux (x86 / AMD64 / PPC / PPC64 are tested)
 - BSD (FreeBSD and OpenBSD are tested)
 - Solaris (OpenSolaris 2008.3 is tested)
 - HaikuOS (based on BeOS 5)

Some of these platforms may have additional dependencies or installation
instructions, which will be added to this document over time.

MEGAZEUX ON OPENSOLARIS

The package `SUNWxorg-headers' must be installed for X11 clipboard support.

path: arch/unix/README


code:
[Desktop Entry]
Encoding=UTF-8
Name=MegaZeux
Exec=megazeux
Icon=megazeux
Terminal=false
Type=Application
Categories=Application;Game;


code:
[Desktop Entry]
Encoding=UTF-8
Name=MegaZeux (Runtime Only)
Exec=mzxrun
Icon=megazeux
Terminal=false
Type=Application
Categories=Application;Game;


code:
#
# Wii Makefile
#

.PHONY: package

ifeq ($(strip $(DEVKITPRO)),)
$(error "DEVKITPRO must be set in your environment.")
endif

ifeq ($(strip $(DEVKITPPC)),)
$(error "DEVKITPPC must be set in your environment.")
endif

AS      := powerpc-eabi-as
CC      := powerpc-eabi-gcc
CXX     := powerpc-eabi-g++
AR      := powerpc-eabi-ar
OBJCOPY := powerpc-eabi-objcopy
ELF2DOL := elf2dol

#
# Wii target rules
#
STRIP  = /bin/true

#
# Override library paths
#

VORBIS_CFLAGS  = -Iarch/wii/libvorbis/include -Iarch/wii/libogg/include
VORBIS_LDFLAGS = -Larch/wii/libvorbis/lib -Larch/wii/libogg/lib \
                 -lvorbisfile -lvorbis -logg

EXTRA_INCLUDES = -I$(DEVKITPRO)/libogc/include
EXTRA_LIBS     = -L$(DEVKITPRO)/libogc/lib/wii -lfat -lasnd -lwiikeyboard \
                 -lwiiuse -lbte -logc -lstdc++ -lm

MACHDEP = -DGEKKO -mrvl -mcpu=750 -meabi -mhard-float

ARCH_CFLAGS   += ${EXTRA_INCLUDES} $(MACHDEP)
ARCH_CXXFLAGS += ${ARCH_CFLAGS}
ARCH_LDFLAGS  += ${EXTRA_LIBS} $(MACHDEP)

package: mzx
	${ELF2DOL} ${mzxrun} boot.dol
	${ELF2DOL} ${mzx} megazeux.dol

clean:
	@rm -f boot.dol megazeux.dol arch/wii/*.d arch/wii/*.o

#
# Vile hack, remove me ASAP
#
arch/wii/%.o: arch/wii/%.c
	$(if ${V},,@echo "  CC      " $<)
	${CC} -MD ${core_cflags} ${core_flags} -c $< -o $@

#
# Need to nest Wii binaries in a subdir
#
build := build/${SUBPLATFORM}/apps/megazeux
build: package ${build}
	${CP} arch/wii/pad.config arch/wii/icon.png ${build}
	${CP} boot.dol megazeux.dol ${build}
	@sed "s/%VERSION%/${VERSION}/g;s/%DATE%/`date -u +%Y%m%d%H%M`/g" \
	    arch/wii/meta.xml > ${build}/meta.xml
	${RM} ${build}/${mzxrun} ${build}/${mzxrun}.debug
	${RM} ${build}/${mzx} ${build}/${mzx}.debug

include arch/zip.inc


code:
BUILDING MEGAZEUX FOR WII

As of 2.82b, MegaZeux can be built for the Wii. You will need the devkitPPC
toolchain, and libogc. You will also need the following in this directory:

  - libfat
  - libogg (audio only)
  - libvorbis (audio only)

Make sure DEVKITPRO and DEVKITPPC are both defined and valid, and use a
config.sh configure line such as:

./config.sh --platform wii --prefix $DEVKITPPC --disable-libpng \
            --disable-utils --enable-release --enable-meter

Then build with the command:

make package

This will create a "boot.dol" file which you should know how to launch.

PACKAGING

Use "make archive" to build a build/dist/wii/mzxgit-wii.zip file for
distribution.

path: arch/wii/README


code:
/* MegaZeux
 *
 * Copyright (C) 2008 Alan Williams <mralert@gmail.com>
 *
 * This program 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 of
 * the License, or (at your option) any later version.
 *
 * This program 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 this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 */

#include "audio.h"

#include <stdlib.h>
#include <string.h>
#include <malloc.h>

#define BOOL _BOOL
#include <asndlib.h>
#include <gctypes.h>
#include <ogc/lwp.h>
#include <ogc/cache.h>
#include <ogc/system.h>
#undef BOOL

#define STACKSIZE 8192

static lwpq_t audio_queue;
static lwp_t audio_thread;
static u8 audio_stack[STACKSIZE];
static Sint16 *audio_buffer[3];
static volatile int current = 0;
static volatile int audio_stop = 0;
static int buffer_size;

static void *wii_audio_thread(void *dud)
{
  while(!audio_stop)
  {
    LWP_ThreadSleep(audio_queue);
    audio_callback(audio_buffer[current], buffer_size);
    DCFlushRange(audio_buffer[current], buffer_size);
    current = (current + 1) % 3;
  }

  return 0;
}

static void voice_callback(s32 voice)
{
  ASND_AddVoice(voice, audio_buffer[(current + 2) % 3], buffer_size);
  LWP_ThreadSignal(audio_queue);
}

void init_audio_platform(struct config_info *conf)
{
  int i;

  // buffer size must be multiple of 32 bytes, so samples must be multiple of 8
  audio.buffer_samples = conf->buffer_size & ~7;
  if(!audio.buffer_samples)
    audio.buffer_samples = 2048;

  buffer_size = sizeof(Sint16) * 2 * audio.buffer_samples;
  audio.mix_buffer = malloc(buffer_size * 2);

  for(i = 0; i < 3; i++)
  {
    audio_buffer[i] = memalign(32, buffer_size);
    memset(audio_buffer[i], 0, buffer_size);
    DCFlushRange(audio_buffer[i], buffer_size);
  }

  ASND_Init();
  LWP_InitQueue(&audio_queue);
  if(LWP_CreateThread(&audio_thread, wii_audio_thread, NULL, audio_stack,
   STACKSIZE, 80) >= 0)
  {
    ASND_SetVoice(0, VOICE_STEREO_16BIT, audio.output_frequency, 0,
     audio_buffer[0], buffer_size, 255, 255, voice_callback);
    ASND_Pause(0);
  }
}

void quit_audio_platform(void)
{
  void *dud;

  if(!audio.mix_buffer)
    return;

  audio_stop = 1;
  LWP_JoinThread(audio_thread, &dud);
  ASND_Pause(1);
  ASND_End();
  free(audio.mix_buffer);
  LWP_CloseQueue(audio_queue);
  // Don't free hardware audio buffers
  // Memory allocated with memalign() can't neccessarily be free()'d
}