/*****************************************************************************

			       XCopilot

	   This code is part of XCopilot, a port of copilot

     Portions of this code are Copyright (C) 1997 Ivan A. Curtis
		       icurtis@radlogic.com.au

The original MS-Windows95 copilot emulator was written by Greg Hewgill.
The following copyright notice appeared on the original copilot sources:

  // Copilot
  // Copyright (c) 1996 Greg Hewgill

 MC68000 Emulation code is from Bernd Schmidt's Unix Amiga Emulator.
       The following copyright notice appeared in those files:

	  Original UAE code Copyright (c) 1995 Bernd Schmidt

This code must not be distributed without these copyright notices intact.

*******************************************************************************
*******************************************************************************

Filename:	custom.c

Description:	Dragonball chip emulation

Update History:   (most recent first)
   Ian Goldberg 25-Sep-97 11:09 -- rewrite of serial and gdb support
   Ian Goldberg 11-Sep-97 09:48 -- added bus error support
   Ian Goldberg  3-Sep-97 14:07 -- added debugging support for PPPersonal ROM
   Ian Goldberg 20-Jun-97 14:09 -- added support for greyscale and panning
   Ian Goldberg 18-Apr-97 11:13 -- added support for gdb debugging via a pty
   Ian Goldberg 10-Apr-97 16:53 -- added port E (for Palm Pilot)
                                -- better sound support
   I. Curtis    17-Mar-97 20:42 -- added UART functionality
   I. Curtis     5-Mar-97 21:48 -- added dokey() and beep
   I. Curtis    26-Feb-97 14:28 -- modified from win95 copilot version

******************************************************************************/
#include <ctype.h>
#include <assert.h>
#include <stdio.h>
#include <time.h>
#include <sys/types.h>
#include <sys/time.h>
#include <unistd.h>

#include "sysdeps.h"
#include "shared.h"
#include "memory.h"
#include "custom.h"
#include "newcpu.h"
#include "dragonball.h"

extern int sram_protect;

shared_img *CustShptr = 0;

const int QUEUE_SIZE = 8;

/* Hmmm... We need to differentiate the debugger using the UART from other
   programs (PPP, etc.) using it.  We check the PC to see if we're in the
   debugger when a read or write is done to the UART.  However, we need
   to figure out the range of addresses the debugger lives in.
   
   For the Palm Pilot Pro, this seems to be 0x10c07540...0x10c0789c.
   For the original Pilot, this seems to be 0x10c07218...0x10c07532.
   I don't know for the Palm Pilot Personal.  We autodetect the ROM
   type in custom_init(), and set DEBUGSTART and DEBUGLEN.

      - Ian
*/
static unsigned long DEBUGSTART, DEBUGLEN;
#define IN_DEBUGGER \
    (((unsigned long)MC68000_getpc() - DEBUGSTART) < DEBUGLEN)
#define DEBUGGER_WRITING (CustShptr->gdb_writefd >= 0 && IN_DEBUGGER)
#define DEBUGGER_READING (CustShptr->gdb_writefd >= 0 && IN_DEBUGGER)

/*******************************************************
class TByteQueue {
public:
  TByteQueue();
  void Put(UBYTE b);
  UBYTE Get();
  int GetUsed() { return UsedCount; }
  int GetFree() { return FreeCount; }
private:
  HANDLE Mutex;
  HANDLE Used;
  int UsedCount;
  HANDLE Free;
  int FreeCount;
  UBYTE Buffer[QUEUE_SIZE];
  int Head;
  int Tail;
};
****************************************************************/

/*TByteQueue::TByteQueue() */
/*{ */
/*  Mutex = CreateMutex(NULL, FALSE, NULL); */
/*  Used = CreateSemaphore(NULL, 0, QUEUE_SIZE, NULL); */
/*  UsedCount = 0; */
/*  Free = CreateSemaphore(NULL, QUEUE_SIZE, QUEUE_SIZE, NULL); */
/*  FreeCount = QUEUE_SIZE; */
/*  Head = 0; */
/*  Tail = 0; */
/*} */

/*void TByteQueue::Put(UBYTE b) */
/*{ */
/*  HANDLE a[2]; */
/*  a[0] = Free; */
/*  a[1] = Mutex; */
/*  WaitForMultipleObjects(2, a, TRUE, INFINITE); */
/*  Buffer[Head] = b; */
/*  Head = (Head + 1) % QUEUE_SIZE; */
/*  UsedCount++; */
/*  FreeCount--; */
/*  ReleaseSemaphore(Used, 1, NULL); */
/*  ReleaseMutex(Mutex); */
/*} */

/*UBYTE TByteQueue::Get() */
/*{ */
/*  HANDLE a[2]; */
/*  a[0] = Used; */
/*  a[1] = Mutex; */
/*  WaitForMultipleObjects(2, a, TRUE, INFINITE); */
/*  UBYTE r = Buffer[Tail]; */
/*  Tail = (Tail + 1) % QUEUE_SIZE; */
/*  FreeCount++; */
/*  UsedCount--; */
/*  ReleaseSemaphore(Free, 1, NULL); */
/*  ReleaseMutex(Mutex); */
/*  return r; */
/*} */

/* HANDLE CommHandle; */
/* read stuff */
/* HANDLE OverlappedReadEvent; */
/* TByteQueue ReadBuffer; */
/* write stuff */
/* HANDLE OverlappedWriteEvent; */
/* OVERLAPPED OverlappedWrite; */
/* BOOL OverlappedWritePending; */

/* TByteQueue KeyBuffer; */

/* dragonball register definitions */

union {
  UBYTE x;
  struct {
#ifndef __BIG_ENDIAN__
    unsigned WDTH8: 1;
    unsigned RSVD : 1;
    unsigned DMAP : 1;
    unsigned SO   : 1;
    unsigned BETEN: 1;
    unsigned PRV  : 1;
    unsigned WPV  : 1;
#endif /* not __BIG_ENDIAN__ */
    unsigned BETO : 1;
#ifdef __BIG_ENDIAN__
    unsigned WPV  : 1;
    unsigned PRV  : 1;
    unsigned BETEN: 1;
    unsigned SO   : 1;
    unsigned DMAP : 1;
    unsigned RSVD : 1;
    unsigned WDTH8: 1;
#endif /* __BIG_ENDIAN__ */
  } anon;
} db_SCR;

union {
  UWORD x;
  struct {
#ifndef __BIG_ENDIAN__
    unsigned V     : 1;
    unsigned unused: 3;
#endif /* not __BIG_ENDIAN__ */
    unsigned GMA   : 12;
#ifdef __BIG_ENDIAN__
    unsigned unused: 3;
    unsigned V     : 1;
#endif /* __BIG_ENDIAN__ */
  } anon;
} db_GRPBASEA, db_GRPBASEC;

union {
  UWORD x;
  struct {
#ifndef __BIG_ENDIAN__
    unsigned unused: 4;
#endif /* not __BIG_ENDIAN__ */
    unsigned GMA   : 12;
#ifdef __BIG_ENDIAN__
    unsigned unused: 4;
#endif /* __BIG_ENDIAN__ */
  } anon;
} db_GRPMASKA, db_GRPMASKC;

union {
  ULONG x;
  struct {
#ifndef __BIG_ENDIAN__
    unsigned WAIT   : 3;
    unsigned RO     : 1;
    unsigned unused1: 4;
    unsigned AM     : 8;
    unsigned BSW    : 1;
    unsigned unused2: 7;
#endif /* not __BIG_ENDIAN__ */
    unsigned AC     : 8;
#ifdef __BIG_ENDIAN__
    unsigned unused2: 7;
    unsigned BSW    : 1;
    unsigned AM     : 8;
    unsigned unused1: 4;
    unsigned RO     : 1;
    unsigned WAIT   : 3;
#endif /* __BIG_ENDIAN__ */
  } anon;
} db_CSA[4], db_CSC[4];

union {
  UWORD x;
  struct {
#ifndef __BIG_ENDIAN__
    unsigned unused1  : 3;
    unsigned DISPLL   : 1;
    unsigned CLKEN    : 1;
    unsigned unused2  : 3;
    unsigned SYSCLKSEL: 3;
    unsigned PIXCLKSEL: 3;
#endif /* not __BIG_ENDIAN__ */
    unsigned unused3  : 2;
#ifdef __BIG_ENDIAN__
    unsigned PIXCLKSEL: 3;
    unsigned SYSCLKSEL: 3;
    unsigned unused2  : 3;
    unsigned CLKEN    : 1;
    unsigned DISPLL   : 1;
    unsigned unused1  : 3;
#endif /* __BIG_ENDIAN__ */
  } anon;
} db_PLLCR;

union {
  UWORD x;
  struct {
#ifndef __BIG_ENDIAN__
    unsigned PC    : 8;
    unsigned QC    : 4;
    unsigned unused: 2;
    unsigned PROT  : 1;
#endif /* not __BIG_ENDIAN__ */
    unsigned CLK32 : 1;
#ifdef __BIG_ENDIAN__
    unsigned PROT  : 1;
    unsigned unused: 2;
    unsigned QC    : 4;
    unsigned PC    : 8;
#endif /* __BIG_ENDIAN__ */
  } anon;
} db_PLLFSR;

union {
  UBYTE x;
  struct {
#ifndef __BIG_ENDIAN__
    unsigned WIDTH : 5;
    unsigned unused: 1;
    unsigned STOP  : 1;
#endif /* not __BIG_ENDIAN__ */
    unsigned PCEN  : 1;
#ifdef __BIG_ENDIAN__
    unsigned STOP  : 1;
    unsigned unused: 1;
    unsigned WIDTH : 5;
#endif /* __BIG_ENDIAN__ */
  } anon;
} db_PCTLR;

union {
  UBYTE x;
  struct {
#ifndef __BIG_ENDIAN__
    unsigned LEVEL : 3;
#endif /* not __BIG_ENDIAN__ */
    unsigned VECTOR: 5;
#ifdef __BIG_ENDIAN__
    unsigned LEVEL : 3;
#endif /* __BIG_ENDIAN__ */
  } anon;
} db_IVR;

union {
  UWORD x;
  struct {
#ifndef __BIG_ENDIAN__
    unsigned unused: 8;
    unsigned POL6: 1;
    unsigned POL3: 1;
    unsigned POL2: 1;
    unsigned POL1: 1;
    unsigned ET6 : 1;
    unsigned ET3 : 1;
    unsigned ET2 : 1;
#endif /* not __BIG_ENDIAN__ */
    unsigned ET1 : 1;
#ifdef __BIG_ENDIAN__
    unsigned ET2 : 1;
    unsigned ET3 : 1;
    unsigned ET6 : 1;
    unsigned POL1: 1;
    unsigned POL2: 1;
    unsigned POL3: 1;
    unsigned POL6: 1;
    unsigned unused: 8;
#endif /* __BIG_ENDIAN__ */
  } anon;
} db_ICR;

union {
  ULONG x;
  struct {
#ifndef __BIG_ENDIAN__
    unsigned SPIM: 1;
    unsigned TMR2: 1;
    unsigned UART: 1;
    unsigned WDT : 1;
    unsigned RTC : 1;
    unsigned LCDC: 1;
    unsigned KB  : 1;
    unsigned PWM : 1;
    unsigned INT0: 1;
    unsigned INT1: 1;
    unsigned INT2: 1;
    unsigned INT3: 1;
    unsigned INT4: 1;
    unsigned INT5: 1;
    unsigned INT6: 1;
    unsigned INT7: 1;
    unsigned IRQ1: 1;
    unsigned IRQ2: 1;
    unsigned IRQ3: 1;
    unsigned IRQ6: 1;
    unsigned PEN : 1;
    unsigned SPIS: 1;
    unsigned TMR1: 1;
    unsigned IRQ7: 1;
#endif /* not __BIG_ENDIAN__ */
    unsigned unused: 8;
#ifdef __BIG_ENDIAN__
    unsigned IRQ7: 1;
    unsigned TMR1: 1;
    unsigned SPIS: 1;
    unsigned PEN : 1;
    unsigned IRQ6: 1;
    unsigned IRQ3: 1;
    unsigned IRQ2: 1;
    unsigned IRQ1: 1;
    unsigned INT7: 1;
    unsigned INT6: 1;
    unsigned INT5: 1;
    unsigned INT4: 1;
    unsigned INT3: 1;
    unsigned INT2: 1;
    unsigned INT1: 1;
    unsigned INT0: 1;
    unsigned PWM : 1;
    unsigned KB  : 1;
    unsigned LCDC: 1;
    unsigned RTC : 1;
    unsigned WDT : 1;
    unsigned UART: 1;
    unsigned TMR2: 1;
    unsigned SPIM: 1;
#endif /* __BIG_ENDIAN__ */
  } anon;
} db_IMR, db_IWR, db_ISR, db_IPR;

union {
  UBYTE x;
  struct {
#ifndef __BIG_ENDIAN__
    unsigned MOCLK  : 1;
    unsigned UDS    : 1;
    unsigned LDS    : 1;
    unsigned unused1: 1;
    unsigned NMI    : 1;
    unsigned DTACK  : 1;
    unsigned WE     : 1;
#endif /* not __BIG_ENDIAN__ */
    unsigned unused2: 1;
#ifdef __BIG_ENDIAN__
    unsigned WE     : 1;
    unsigned DTACK  : 1;
    unsigned NMI    : 1;
    unsigned unused1: 1;
    unsigned LDS    : 1;
    unsigned UDS    : 1;
    unsigned MOCLK  : 1;
#endif /* __BIG_ENDIAN__ */
  } anon;
} db_PCDIR, db_PCDATA, db_PCSEL;

union {
  UBYTE x;
  struct {
#ifndef __BIG_ENDIAN__
    unsigned BB0: 1;
    unsigned B1: 1;
    unsigned B2: 1;
    unsigned B3: 1;
    unsigned B4: 1;
    unsigned B5: 1;
    unsigned B6: 1;
#endif /* not __BIG_ENDIAN__ */
    unsigned B7: 1;
#ifdef __BIG_ENDIAN__
    unsigned B6: 1;
    unsigned B5: 1;
    unsigned B4: 1;
    unsigned B3: 1;
    unsigned B2: 1;
    unsigned B1: 1;
    unsigned BB0: 1;
#endif /* __BIG_ENDIAN__ */
  } anon;
} db_PDDIR, db_PDPUEN, db_PDPOL, db_PDIRQEN, db_PDIRQEDGE;

struct {
  union {
    UBYTE x;
    struct {
#ifndef __BIG_ENDIAN__
      unsigned BB0: 1;
      unsigned B1: 1;
      unsigned B2: 1;
      unsigned B3: 1;
      unsigned B4: 1;
      unsigned B5: 1;
      unsigned B6: 1;
#endif /* not __BIG_ENDIAN__ */
      unsigned B7: 1;
#ifdef __BIG_ENDIAN__
      unsigned B6: 1;
      unsigned B5: 1;
      unsigned B4: 1;
      unsigned B3: 1;
      unsigned B2: 1;
      unsigned B1: 1;
      unsigned BB0: 1;
#endif /* __BIG_ENDIAN__ */
    } anon;
  } uu;
  UBYTE edge;
} db_PDDATA;

union {
  UBYTE x;
  struct {
    unsigned A: 8;
  } anon;
} db_PEDIR, db_PEDATA, db_PEPUEN, db_PESEL;

union {
  UBYTE x;
  struct {
    unsigned A: 8;
  } anon;
} db_PFDIR, db_PFDATA, db_PFPUEN, db_PFSEL;

union {
  UBYTE x;
  struct {
#ifndef __BIG_ENDIAN__
    unsigned UART_TXD: 1;
    unsigned UART_RXD: 1;
    unsigned PWMOUT  : 1;
    unsigned TOUT2   : 1;
    unsigned TIN2    : 1;
    unsigned TOUT1   : 1;
    unsigned TIN1    : 1;
#endif /* not __BIG_ENDIAN__ */
    unsigned RTCOUT  : 1;
#ifdef __BIG_ENDIAN__
    unsigned TIN1    : 1;
    unsigned TOUT1   : 1;
    unsigned TIN2    : 1;
    unsigned TOUT2   : 1;
    unsigned PWMOUT  : 1;
    unsigned UART_RXD: 1;
    unsigned UART_TXD: 1;
#endif /* __BIG_ENDIAN__ */
  } anon;
} db_PGDIR, db_PGDATA, db_PGPUEN, db_PGSEL;

union {
  UBYTE x;
  struct {
#ifndef __BIG_ENDIAN__
    unsigned SPIM_TXD  : 1;
    unsigned SPIM_RXD  : 1;
    unsigned SPIM_CLKO : 1;
    unsigned SPIS_EN   : 1;
    unsigned SPIS_RXD  : 1;
    unsigned SPIS_CLKI : 1;
    unsigned PCMCIA_CE2: 1;
#endif /* not __BIG_ENDIAN__ */
    unsigned PCMCIA_CE1: 1;
#ifdef __BIG_ENDIAN__
    unsigned PCMCIA_CE2: 1;
    unsigned SPIS_CLKI : 1;
    unsigned SPIS_RXD  : 1;
    unsigned SPIS_EN   : 1;
    unsigned SPIM_CLKO : 1;
    unsigned SPIM_RXD  : 1;
    unsigned SPIM_TXD  : 1;
#endif /* __BIG_ENDIAN__ */
  } anon;
} db_PKDIR, db_PKDATA, db_PKPUEN, db_PKSEL;

union {
  UBYTE x;
  struct {
#ifndef __BIG_ENDIAN__
    unsigned CTS      : 1;
    unsigned RTS      : 1;
    unsigned IRQ6     : 1;
    unsigned IRQ3     : 1;
    unsigned IRQ2     : 1;
    unsigned IRQ1     : 1;
    unsigned PEN_IRQ  : 1;
#endif /* not __BIG_ENDIAN__ */
    unsigned UART_GPIO: 1;
#ifdef __BIG_ENDIAN__
    unsigned PEN_IRQ  : 1;
    unsigned IRQ1     : 1;
    unsigned IRQ2     : 1;
    unsigned IRQ3     : 1;
    unsigned IRQ6     : 1;
    unsigned RTS      : 1;
    unsigned CTS      : 1;
#endif /* __BIG_ENDIAN__ */
  } anon;
} db_PMDIR, db_PMDATA, db_PMPUEN, db_PMSEL;

union {
  UWORD x;
  struct {
#ifndef __BIG_ENDIAN__
    unsigned CLKSEL : 3;
    unsigned unused1: 1;
    unsigned PWMEN  : 1;
    unsigned POL    : 1;
    unsigned unused2: 1;
    unsigned PIN    : 1;
    unsigned LOAD   : 1;
    unsigned unused3: 5;
    unsigned IRQEN  : 1;
#endif /* not __BIG_ENDIAN__ */
    unsigned PWMIRQ : 1;
#ifdef __BIG_ENDIAN__
    unsigned IRQEN  : 1;
    unsigned unused3: 5;
    unsigned LOAD   : 1;
    unsigned PIN    : 1;
    unsigned unused2: 1;
    unsigned POL    : 1;
    unsigned PWMEN  : 1;
    unsigned unused1: 1;
    unsigned CLKSEL : 3;
#endif /* __BIG_ENDIAN__ */
  } anon;
} db_PWMC;

union {
  UWORD x;
  struct {
    unsigned PERIOD: 16;
  } anon;
} db_PWMP;

union {
  UWORD x;
  struct {
    unsigned WIDTH: 16;
  } anon;
} db_PWMW;

union {
  UWORD x;
  struct {
#ifndef __BIG_ENDIAN__
    unsigned TEN         : 1;
    unsigned CLKSOURCE   : 3;
    unsigned IRQEN       : 1;
    unsigned OM          : 1;
    unsigned CAPTURE_EDGE: 2;
    unsigned FRR         : 1;
#endif /* not __BIG_ENDIAN__ */
    unsigned unused      : 7;
#ifdef __BIG_ENDIAN__
    unsigned FRR         : 1;
    unsigned CAPTURE_EDGE: 2;
    unsigned OM          : 1;
    unsigned IRQEN       : 1;
    unsigned CLKSOURCE   : 3;
    unsigned TEN         : 1;
#endif /* __BIG_ENDIAN__ */
  } anon;
} db_TCTL1, db_TCTL2;

union {
  UWORD x;
  struct {
#ifndef __BIG_ENDIAN__
    unsigned PRESCALE: 8;
#endif /* not __BIG_ENDIAN__ */
    unsigned unused  : 8;
#ifdef __BIG_ENDIAN__
    unsigned PRESCALE: 8;
#endif /* __BIG_ENDIAN__ */
  } anon;
} db_TPRER1, db_TPRER2;

union {
  UWORD x;
  struct {
    unsigned COMPARE: 16;
  } anon;
} db_TCMP1, db_TCMP2;

union {
  UWORD x;
  struct {
    unsigned COUNT: 16;
  } anon;
} db_TCN1, db_TCN2;

struct {
  union {
    UWORD x;
    struct {
#ifndef __BIG_ENDIAN__
      unsigned COMP  : 1;
      unsigned CAPT  : 1;
#endif /* not __BIG_ENDIAN__ */
      unsigned unused: 14;
#ifdef __BIG_ENDIAN__
      unsigned CAPT  : 1;
      unsigned COMP  : 1;
#endif /* __BIG_ENDIAN__ */
    } anon;
  } uu;
  UWORD lastseen;
} db_TSTAT1, db_TSTAT2;

union {
  UWORD x;
  struct {
#ifndef __BIG_ENDIAN__
    unsigned WDEN  : 1;
    unsigned FI    : 1;
    unsigned LOCK  : 1;
    unsigned WDRST : 1;
#endif /* not __BIG_ENDIAN__ */
    unsigned unused: 12;
#ifdef __BIG_ENDIAN__
    unsigned WDRST : 1;
    unsigned LOCK  : 1;
    unsigned FI    : 1;
    unsigned WDEN  : 1;
#endif /* __BIG_ENDIAN__ */
  } anon;
} db_WCR;

union {
  UWORD x;
  struct {
    unsigned COUNT: 16;
  } anon;
} db_WCN;

union {
  UWORD x;
  struct {
    unsigned DATA: 16;
  } anon;
} db_SPIMDATA;

union {
  UWORD x;
  struct {
#ifndef __BIG_ENDIAN__
    unsigned BITCOUNT: 4;
    unsigned POL     : 1;
    unsigned PHA     : 1;
    unsigned IRQEN   : 1;
    unsigned SPIMIRQ : 1;
    unsigned XCH     : 1;
    unsigned SPMEN   : 1;
    unsigned unused  : 3;
#endif /* not __BIG_ENDIAN__ */
    unsigned DATARATE: 3;
#ifdef __BIG_ENDIAN__
    unsigned unused  : 3;
    unsigned SPMEN   : 1;
    unsigned XCH     : 1;
    unsigned SPIMIRQ : 1;
    unsigned IRQEN   : 1;
    unsigned PHA     : 1;
    unsigned POL     : 1;
    unsigned BITCOUNT: 4;
#endif /* __BIG_ENDIAN__ */
  } anon;
} db_SPIMCONT;

union {
  UWORD x;
  struct {
#ifndef __BIG_ENDIAN__
    unsigned TX_AVAIL_ENABLE  : 1;
    unsigned TX_HALF_ENABLE   : 1;
    unsigned TX_EMPTY_ENABLE  : 1;
    unsigned RX_READY_ENABLE  : 1;
    unsigned RX_HALF_ENABLE   : 1;
    unsigned RX_FULL_ENABLE   : 1;
    unsigned CTS_DELTA_ENABLE : 1;
    unsigned GPIO_DELTA_ENABLE: 1;
    unsigned BITS_8           : 1;
    unsigned STOP_BITS        : 1;
    unsigned ODD_EVEN         : 1;
    unsigned PARITY_ENABLE    : 1;
    unsigned RX_CLK_CONT      : 1;
    unsigned TX_ENABLE        : 1;
    unsigned RX_ENABLE        : 1;
#endif /* not __BIG_ENDIAN__ */
    unsigned UART_ENABLE      : 1;
#ifdef __BIG_ENDIAN__
    unsigned RX_ENABLE        : 1;
    unsigned TX_ENABLE        : 1;
    unsigned RX_CLK_CONT      : 1;
    unsigned PARITY_ENABLE    : 1;
    unsigned ODD_EVEN         : 1;
    unsigned STOP_BITS        : 1;
    unsigned BITS_8           : 1;
    unsigned GPIO_DELTA_ENABLE: 1;
    unsigned CTS_DELTA_ENABLE : 1;
    unsigned RX_FULL_ENABLE   : 1;
    unsigned RX_HALF_ENABLE   : 1;
    unsigned RX_READY_ENABLE  : 1;
    unsigned TX_EMPTY_ENABLE  : 1;
    unsigned TX_HALF_ENABLE   : 1;
    unsigned TX_AVAIL_ENABLE  : 1;
#endif /* __BIG_ENDIAN__ */
  } anon;
} db_USTCNT;

union {
  UWORD x;
  struct {
#ifndef __BIG_ENDIAN__
    unsigned PRESCALER : 6;
    unsigned unused    : 2;
    unsigned DIVIDE    : 3;
    unsigned BAUD_SRC  : 1;
    unsigned GPIO_SRC  : 1;
    unsigned GPIO_DIR  : 1;
    unsigned GPIO      : 1;
#endif /* not __BIG_ENDIAN__ */
    unsigned GPIO_DELTA: 1;
#ifdef __BIG_ENDIAN__
    unsigned GPIO      : 1;
    unsigned GPIO_DIR  : 1;
    unsigned GPIO_SRC  : 1;
    unsigned BAUD_SRC  : 1;
    unsigned DIVIDE    : 3;
    unsigned unused    : 2;
    unsigned PRESCALER : 6;
#endif /* __BIG_ENDIAN__ */
  } anon;
} db_UBAUD;

union {
  UWORD x;
  struct {
#ifndef __BIG_ENDIAN__
    unsigned DATA        : 8;
    unsigned PARITY_ERROR: 1;
    unsigned BREAK       : 1;
    unsigned FRAME_ERROR : 1;
    unsigned OVRUN       : 1;
    unsigned unused      : 1;
    unsigned DATA_READY  : 1;
    unsigned FIFO_HALF   : 1;
#endif /* not __BIG_ENDIAN__ */
    unsigned FIFO_FULL   : 1;
#ifdef __BIG_ENDIAN__
    unsigned FIFO_HALF   : 1;
    unsigned DATA_READY  : 1;
    unsigned unused      : 1;
    unsigned OVRUN       : 1;
    unsigned FRAME_ERROR : 1;
    unsigned BREAK       : 1;
    unsigned PARITY_ERROR: 1;
    unsigned DATA        : 8;
#endif /* __BIG_ENDIAN__ */
  } anon;
} db_URX, db_URXdb;

union {
  UWORD x;
  struct {
#ifndef __BIG_ENDIAN__
    unsigned DATA      : 8;
    unsigned CTS_DELTA : 1;
    unsigned CTS_STATUS: 1;
    unsigned unused    : 1;
    unsigned IGNORE_CTS: 1;
    unsigned SEND_BREAK: 1;
    unsigned TX_AVAIL  : 1;
    unsigned FIFO_HALF : 1;
#endif /* not __BIG_ENDIAN__ */
    unsigned FIFO_EMPTY: 1;
#ifdef __BIG_ENDIAN__
    unsigned FIFO_HALF : 1;
    unsigned TX_AVAIL  : 1;
    unsigned SEND_BREAK: 1;
    unsigned IGNORE_CTS: 1;
    unsigned unused    : 1;
    unsigned CTS_STATUS: 1;
    unsigned CTS_DELTA : 1;
    unsigned DATA      : 8;
#endif /* __BIG_ENDIAN__ */
  } anon;
} db_UTX;

union {
  UWORD x;
  struct {
#ifndef __BIG_ENDIAN__
    unsigned unused1    : 4;
    unsigned IRDA_LOOP  : 1;
    unsigned IRDA_ENABLE: 1;
    unsigned RTS        : 1;
    unsigned RTS_CONT   : 1;
    unsigned unused2    : 4;
    unsigned LOOP       : 1;
    unsigned FORCE_PERR : 1;
    unsigned CLK_SRC    : 1;
#endif /* not __BIG_ENDIAN__ */
    unsigned unused3    : 1;
#ifdef __BIG_ENDIAN__
    unsigned CLK_SRC    : 1;
    unsigned FORCE_PERR : 1;
    unsigned LOOP       : 1;
    unsigned unused2    : 4;
    unsigned RTS_CONT   : 1;
    unsigned RTS        : 1;
    unsigned IRDA_ENABLE: 1;
    unsigned IRDA_LOOP  : 1;
    unsigned unused1    : 4;
#endif /* __BIG_ENDIAN__ */
  } anon;
} db_UMISC;

union {
  ULONG x;
  struct {
    unsigned SSA: 32;
  } anon;
} db_LSSA;

union {
  UBYTE x;
  struct {
    unsigned VPW: 8;
  } anon;
} db_LVPW;

union {
  UWORD x;
  struct {
#ifndef __BIG_ENDIAN__
    unsigned XMAX  : 10;
#endif /* not __BIG_ENDIAN__ */
    unsigned unused: 6;
#ifdef __BIG_ENDIAN__
    unsigned XMAX  : 10;
#endif /* __BIG_ENDIAN__ */
  } anon;
} db_LXMAX;

union {
  UWORD x;
  struct {
#ifndef __BIG_ENDIAN__
    unsigned YMAX  : 10;
#endif /* not __BIG_ENDIAN__ */
    unsigned unused: 6;
#ifdef __BIG_ENDIAN__
    unsigned YMAX  : 10;
#endif /* __BIG_ENDIAN__ */
  } anon;
} db_LYMAX;

union {
  UWORD x;
  struct {
#ifndef __BIG_ENDIAN__
    unsigned CXP   : 10;
    unsigned unused: 4;
#endif /* not __BIG_ENDIAN__ */
    unsigned CC    : 2;
#ifdef __BIG_ENDIAN__
    unsigned unused: 4;
    unsigned CXP   : 10;
#endif /* __BIG_ENDIAN__ */
  } anon;
} db_LCXP;

union {
  UWORD x;
  struct {
#ifndef __BIG_ENDIAN__
    unsigned CYP   : 10;
#endif /* not __BIG_ENDIAN__ */
    unsigned unused: 6;
#ifdef __BIG_ENDIAN__
    unsigned CYP   : 10;
#endif /* __BIG_ENDIAN__ */
  } anon;
} db_LCYP;

union {
  UWORD x;
  struct {
#ifndef __BIG_ENDIAN__
    unsigned CH     : 5;
    unsigned unused1: 3;
    unsigned CW     : 5;
#endif /* not __BIG_ENDIAN__ */
    unsigned unused2: 3;
#ifdef __BIG_ENDIAN__
    unsigned CW     : 5;
    unsigned unused1: 3;
    unsigned CH     : 5;
#endif /* __BIG_ENDIAN__ */
  } anon;
} db_LCWCH;

union {
  UWORD x;
  struct {
#ifndef __BIG_ENDIAN__
    unsigned BD  : 7;
#endif /* not __BIG_ENDIAN__ */
    unsigned BKEN: 1;
#ifdef __BIG_ENDIAN__
    unsigned BD  : 7;
#endif /* __BIG_ENDIAN__ */
  } anon;
} db_LBLKC;

union {
  UBYTE x;
  struct {
#ifndef __BIG_ENDIAN__
    unsigned GS    : 1;
    unsigned PBSIZ : 2;
#endif /* not __BIG_ENDIAN__ */
    unsigned unused: 5;
#ifdef __BIG_ENDIAN__
    unsigned PBSIZ : 2;
    unsigned GS    : 1;
#endif /* __BIG_ENDIAN__ */
  } anon;
} db_LPICF;

union {
  UBYTE x;
  struct {
#ifndef __BIG_ENDIAN__
    unsigned PCD   : 6;
#endif /* not __BIG_ENDIAN__ */
    unsigned unused: 2;
#ifdef __BIG_ENDIAN__
    unsigned PCD   : 6;
#endif /* __BIG_ENDIAN__ */
  } anon;
} db_LPXCD;

union {
  UBYTE x;
  struct {
#ifndef __BIG_ENDIAN__
    unsigned PCDS  : 1;
    unsigned DWIDTH: 1;
    unsigned unused: 2;
    unsigned WS0   : 1;
    unsigned WS1   : 1;
    unsigned DMA16 : 1;
#endif /* not __BIG_ENDIAN__ */
    unsigned LCDCON: 1;
#ifdef __BIG_ENDIAN__
    unsigned DMA16 : 1;
    unsigned WS1   : 1;
    unsigned WS0   : 1;
    unsigned unused: 2;
    unsigned DWIDTH: 1;
    unsigned PCDS  : 1;
#endif /* __BIG_ENDIAN__ */
  } anon;
} db_LCKCON;

union {
  UBYTE x;
  struct {
    unsigned LBAR: 8;
  } anon;
} db_LLBAR;

union {
  UBYTE x;
  struct {
    unsigned OTC: 8;
  } anon;
} db_LOTCR;

union {
  UBYTE x;
  struct {
#ifndef __BIG_ENDIAN__
    unsigned POS: 3;
    unsigned BOS: 1;
#endif /* not __BIG_ENDIAN__ */
    unsigned unused: 4;
#ifdef __BIG_ENDIAN__
    unsigned BOS: 1;
    unsigned POS: 3;
#endif /* __BIG_ENDIAN__ */
  } anon;
} db_LPOSR;

union {
  UBYTE x;
  struct {
#ifndef __BIG_ENDIAN__
    unsigned YMOD: 4;
#endif /* not __BIG_ENDIAN__ */
    unsigned XMOD: 4;
#ifdef __BIG_ENDIAN__
    unsigned YMOD: 4;
#endif /* __BIG_ENDIAN__ */
  } anon;
} db_LFRCM;

union {
  UWORD x;
  struct {
#ifndef __BIG_ENDIAN__
    unsigned G2: 4;
    unsigned G3: 4;
    unsigned G0: 4;
#endif /* not __BIG_ENDIAN__ */
    unsigned G1: 4;
#ifdef __BIG_ENDIAN__
    unsigned G0: 4;
    unsigned G3: 4;
    unsigned G2: 4;
#endif /* __BIG_ENDIAN__ */
  } anon;
} db_LGPMR;

union {
  ULONG x;
  struct {
#ifndef __BIG_ENDIAN__
    unsigned SECONDS: 6;
    unsigned unused1: 10;
    unsigned MINUTES: 6;
    unsigned unused2: 2;
    unsigned HOURS  : 5;
#endif /* not __BIG_ENDIAN__ */
    unsigned unused3: 3;
#ifdef __BIG_ENDIAN__
    unsigned HOURS  : 5;
    unsigned unused2: 2;
    unsigned MINUTES: 6;
    unsigned unused1: 10;
    unsigned SECONDS: 6;
#endif /* __BIG_ENDIAN__ */
  } anon;
} db_RTCHMS, db_RTCALARM;

union {
  UBYTE x;
  struct {
#ifndef __BIG_ENDIAN__
    unsigned unused1: 5;
    unsigned REF384 : 1;
    unsigned unused2: 1;
#endif /* not __BIG_ENDIAN__ */
    unsigned ENABLE : 1;
#ifdef __BIG_ENDIAN__
    unsigned unused2: 1;
    unsigned REF384 : 1;
    unsigned unused1: 5;
#endif /* __BIG_ENDIAN__ */
  } anon;
} db_RTCCTL;

union {
  UBYTE x;
  struct {
#ifndef __BIG_ENDIAN__
    unsigned SW    : 1;
    unsigned MININT: 1;
    unsigned ALARM : 1;
    unsigned HOUR24: 1;
    unsigned HZ1   : 1;
#endif /* not __BIG_ENDIAN__ */
    unsigned unused: 3;
#ifdef __BIG_ENDIAN__
    unsigned HZ1   : 1;
    unsigned HOUR24: 1;
    unsigned ALARM : 1;
    unsigned MININT: 1;
    unsigned SW    : 1;
#endif /* __BIG_ENDIAN__ */
  } anon;
} db_RTCISR, db_RTCIENR;

int pendown = 0, penx, peny;

 /*
  * Events
  */

unsigned long int cycles, specialflags;

#ifdef _DEBUG
/*#define LOGGING */
#endif

#ifdef LOGGING
#include <stdio.h>
FILE *logfile = fopen("hotsync.log", "wt");
BOOL lastoutput = FALSE;
char s[80];
int col = 0;

void LOG(BOOL output, UBYTE b)
{
  if (output != lastoutput) {
    if (col > 0) {
      fprintf(logfile, "%s", s);
    }
    if (output) {
      fprintf(logfile, "\n\nPilot -> Desktop\n\n");
    } else {
      fprintf(logfile, "\n\nDesktop -> Pilot\n\n");
    }
    fflush(logfile);
    lastoutput = output;
    col = 0;
  }
  sprintf(&s[col], "%02x ", b);
  col += 3;
  if (col > 75) {
    fprintf(logfile, "%s\n", s);
    col = 0;
  }
}
#endif

/**********************************************
DWORD __stdcall CommRead(void *)
{
  while (1) {
    OVERLAPPED ov = {0};
    ov.hEvent = OverlappedReadEvent;
    BYTE buf[16];
    DWORD n;
    if ((ReadFile(CommHandle, buf, sizeof(buf), &n, &ov) || GetOverlappedResult(CommHandle, &ov, &n, TRUE)) && n > 0) {
      for (DWORD i = 0; i < n; i++) {
        ReadBuffer.Put(buf[i]);
      }
    }
  }
  return 0;
}

void CommWrite(UBYTE b)
{
  if (CommHandle == NULL) {
    return;
  }
#ifdef LOGGING
  LOG(TRUE, b);
#endif
  DWORD n;
  if (OverlappedWritePending) {
    GetOverlappedResult(CommHandle, &OverlappedWrite, &n, TRUE);
    OverlappedWritePending = FALSE;
  }
  static UBYTE bytetosend;
  bytetosend = b;
  memset(&OverlappedWrite, 0, sizeof(OverlappedWrite));
  OverlappedWrite.hEvent = OverlappedWriteEvent;
  if (!WriteFile(CommHandle, &bytetosend, 1, &n, &OverlappedWrite) && GetLastError() == ERROR_IO_PENDING) {
    OverlappedWritePending = TRUE;
  }
}
**********************************************/

/*
 * helper functions
 */

/***************************************
 * Handle a key event. The fields      *
 * keydown and key of the shared image *
 * are interpreted, and the dragonball *
 * registers updated                   *
 ***************************************/
void dokey(void)
{
  UBYTE d;
  d = db_PDDATA.uu.x;
  if (CustShptr->keydown) {
    db_PDDATA.uu.x |= (1 << CustShptr->key);
    CustShptr->keydown = 0;
    CustShptr->key = -1;
  } else if (CustShptr->key >= 0) {
    db_PDDATA.uu.x &= ~(1 << CustShptr->key);
    CustShptr->key = -1;
  }
  db_PDDATA.edge |= db_PDDATA.uu.x & ~d;
  db_IPR.anon.PEN = CustShptr->pen;
  db_IPR.x = (db_IPR.x & 0xFFFF00FF) |
    ((((db_PDDATA.edge & db_PDIRQEDGE.x) |
       (db_PDDATA.uu.x & ~db_PDIRQEDGE.x)) & db_PDIRQEN.x) << 8);
}

void updateisr()
{
  db_IPR.anon.PEN = CustShptr->pen;
  db_ISR.x = db_IPR.x & ~db_IMR.x;
  if (db_ISR.x) {
    specialflags |= SPCFLAG_INT;
  }
  CustShptr->run_updateisr = 0; 
}

void maybe_updateisr()
{
  if (CustShptr->run_updateisr) {
    dokey();
    updateisr();
  }
}

int intbase()
{
  return db_IVR.anon.VECTOR << 3;
}

int intlev()
{
  if (db_ISR.anon.IRQ7) return 7;
  if (db_ISR.anon.SPIS) return 6;
  if (db_ISR.anon.TMR1) return 6;
  if (db_ISR.anon.IRQ6) return 6;
  if (db_ISR.anon.PEN ) return 5;
  if (db_ISR.anon.SPIM) return 4;
  if (db_ISR.anon.TMR2) return 4;
  if (db_ISR.anon.UART) return 4;
  if (db_ISR.anon.WDT ) return 4;
  if (db_ISR.anon.RTC ) return 4;
  if (db_ISR.anon.KB  ) return 4;
  if (db_ISR.anon.PWM ) return 4;
  if (db_ISR.anon.INT0) return 4;
  if (db_ISR.anon.INT1) return 4;
  if (db_ISR.anon.INT2) return 4;
  if (db_ISR.anon.INT3) return 4;
  if (db_ISR.anon.INT4) return 4;
  if (db_ISR.anon.INT5) return 4;
  if (db_ISR.anon.INT6) return 4;
  if (db_ISR.anon.INT7) return 4;
  if (db_ISR.anon.IRQ3) return 3;
  if (db_ISR.anon.IRQ2) return 2;
  if (db_ISR.anon.IRQ1) return 1;
  return -1;
}

void pen(int down, int x, int y)
{
  if (!pendown && down) {
    db_IPR.anon.PEN = 1;
    updateisr();
  } else if (pendown && !down) {
    db_IPR.anon.PEN = 0;
    updateisr();
  }
  pendown = down;
  penx = x;
  peny = y;
}

void hotsync(int down)
{
  if (down) {
    db_IPR.anon.IRQ1 = 1;
  } else {
    db_IPR.anon.IRQ1 = 0;
  }
  updateisr();
}

/***********************************
void putkey(UBYTE key)
{
  if (KeyBuffer.GetFree() > 0) {
    KeyBuffer.Put(key);
  }
}
************************************/

void customreset(void)
{
  cycles = 0; 
  specialflags = 0;
    
  db_SCR.x      = 0x0C;
  db_GRPBASEA.x = 0x0000;
  db_GRPBASEC.x = 0x0000;
  db_GRPMASKA.x = 0x0000;
  db_GRPMASKC.x = 0x0000;
  db_CSA[0].x   = 0x00010006;
  db_CSA[1].x   = 0x00010006;
  db_CSA[2].x   = 0x00010006;
  db_CSA[3].x   = 0x00010006;
  db_CSC[0].x   = 0x00010006;
  db_CSC[1].x   = 0x00010006;
  db_CSC[2].x   = 0x00010006;
  db_CSC[3].x   = 0x00010006;
  db_PLLCR.x    = 0x2400;
  db_PLLFSR.x   = 0x0123;
  db_PCTLR.x    = 0x1F;
  db_IVR.x      = 0x00;
  db_ICR.x      = 0x0000;
  db_IMR.x      = 0x00FFFFFF;
  db_IWR.x      = 0x00FFFFFF;
  db_ISR.x      = 0x00000000;
  db_IPR.x      = 0x00000000;
  db_PCDIR.x    = 0x00;
  db_PCDATA.x   = 0x00;
  db_PCSEL.x    = 0x00;
  db_PDDIR.x    = 0x00;
  db_PDDATA.uu.x = 0x00;
  db_PDDATA.edge= 0x00;
  db_PDPUEN.x   = 0xFF;
  db_PDPOL.x    = 0x00;
  db_PDIRQEN.x  = 0x00;
  db_PDIRQEDGE.x= 0x00;
  db_PEDIR.x    = 0x00;
  db_PEDATA.x   = 0x00;
  db_PEPUEN.x   = 0xFF;
  db_PESEL.x    = 0xFF;
  db_PFDIR.x    = 0x00;
  db_PFDATA.x   = 0x00;
  db_PFPUEN.x   = 0xFF;
  db_PFSEL.x    = 0xFF;
  db_PGDIR.x    = 0x00;
  db_PGDATA.x   = 0x00;
  db_PGPUEN.x   = 0xFF;
  db_PGSEL.x    = 0xFF;
  db_PKDIR.x    = 0x00;
  db_PKDATA.x   = 0x00;
  db_PKPUEN.x   = 0xFF;
  db_PKSEL.x    = 0xFF;
  db_PMDIR.x    = 0x00;
  db_PMDATA.x   = 0x00;
  db_PMPUEN.x   = 0xFF;
  db_PMSEL.x    = 0xFF;
  db_PWMC.x     = 0x0000;
  db_PWMP.x     = 0x0000;
  db_PWMW.x     = 0x0000;
  db_TCTL1.x    = 0x0000;
  db_TPRER1.x   = 0x0000;
  db_TCMP1.x    = 0xFFFF;
  db_TCN1.x     = 0x0000;
  db_TSTAT1.uu.x = 0x0000;
  db_TSTAT1.lastseen = 0x0000;
  db_TCTL2.x    = 0x0000;
  db_TPRER2.x   = 0x0000;
  db_TCMP2.x    = 0xFFFF;
  db_TCN2.x     = 0x0000;
  db_TSTAT2.uu.x = 0x0000;
  db_TSTAT2.lastseen = 0x0000;
  db_WCR.x      = 0x0000;
  db_WCN.x      = 0x0000;
  db_SPIMDATA.x = 0x0000;
  db_SPIMCONT.x = 0x0000;
  db_USTCNT.x   = 0x0000;
  db_UBAUD.x    = 0x003F;
  db_URX.x      = 0x0000;
  db_URXdb.x    = 0x0000;
  db_UTX.x      = 0x0000;
  db_UMISC.x    = 0x0000;
  db_LSSA.x     = 0x00000000;
  db_LVPW.x     = 0xFF;
  db_LXMAX.x    = 0x03FF;
  db_LYMAX.x    = 0x01FF;
  db_LCXP.x     = 0x0000;
  db_LCYP.x     = 0x0000;
  db_LCWCH.x    = 0x0101;
  db_LBLKC.x    = 0x7F;
  db_LPICF.x    = 0x00;
  db_LPXCD.x    = 0x00;
  db_LCKCON.x   = 0x40;
  db_LLBAR.x    = 0x3E;
  db_LOTCR.x    = 0x3F;
  db_LPOSR.x    = 0x00;
  db_LFRCM.x    = 0xB9;
  db_LGPMR.x    = 0x1073;
  db_RTCHMS.x   = 0x00000000;
  db_RTCALARM.x = 0x00000000;
  db_RTCCTL.x   = 0x00;
  db_RTCISR.x   = 0x00;
  db_RTCIENR.x  = 0x00;
}

void dumpcustom(void)
{
}

/*
 * custptr is a pointer to a shared memory block which will "back-up"
 * the register values of the custom circuits, allowing other processes
 * to look at register values
 */
void custom_init(shared_img *shptr)
{
    CustShptr = shptr;
    CustShptr->PICF = 0;
    CustShptr->VPW = 0xFF;
    CustShptr->POSR = 0;
    CustShptr->grpalette[0] = 1;
    CustShptr->grpalette[1] = 0;
    CustShptr->grpalette[2] = 3;
    CustShptr->grpalette[3] = 7;
    CustShptr->quit = 0;
    CustShptr->run_updateisr = 0;
    CustShptr->LcdPower = lcdOn;
    CustShptr->Backlight = 0;
    customreset();

    /* If we're debugging, try to figure out our ROM type so we know where
       the debugger code lives. */
    if (get_word(0x10c0020e) == 0x5259) {
	/* Original Pilot */
	DEBUGSTART = 0x10c07218;
	DEBUGLEN = 0x10c07532 - DEBUGSTART;
    } else if (get_word(0x10c0276a) == 0x606e) {
	/* Palm Pilot Personal */
	DEBUGSTART = 0x10c07536;
	DEBUGLEN = 0x10c07892 - DEBUGSTART;
    } else {
	/* Assume Palm Pilot Pro */
	DEBUGSTART = 0x10c07540;
	DEBUGLEN = 0x10c0789c - DEBUGSTART;
    }
}

void do_cycles(int longtime)
{
  if (db_TCTL2.anon.TEN) {
    db_TCN2.anon.COUNT++;
    if (db_TCN2.anon.COUNT > db_TCMP2.anon.COMPARE || longtime) {
      db_TSTAT2.uu.anon.COMP = 1;
      if (db_TCTL2.anon.FRR == 0) {
        db_TCN2.anon.COUNT = 0;
      }
      if (db_TCTL2.anon.IRQEN) {
        db_IPR.anon.TMR2 = 1;
        updateisr();
      }
    }
  }

    /*
    * Determine if there are any chars to read
    * from the serial port or debugger
    */ 

    /* WARNING: This uses a shared memory data structure to store the FIFO.
	      The producer is adding things to this _at the same time_ as
	      this is consuming.  Examine main.c and take a course in
	      concurrent programming before modifying this.  :-) - Ian */

    if (CustShptr->serial.head != CustShptr->serial.tail &&
	    db_USTCNT.anon.UART_ENABLE && !db_URX.anon.DATA_READY) {
	int curhead = CustShptr->serial.head;
	db_URX.anon.DATA = CustShptr->serial.fifo[curhead];
	curhead += 1;
	if (curhead == FIFO_SIZE) {
	    curhead = 0;
	}
	CustShptr->serial.head = curhead;
	db_URX.anon.DATA_READY = 1;
	if (db_USTCNT.anon.RX_READY_ENABLE) {
	    db_IPR.anon.UART = 1;
	    updateisr();
	}
    }

    if (CustShptr->gdb.head != CustShptr->gdb.tail &&
	    db_USTCNT.anon.UART_ENABLE && !db_URXdb.anon.DATA_READY) {
	int curhead = CustShptr->gdb.head;
	db_URXdb.anon.DATA = CustShptr->gdb.fifo[curhead];
	curhead += 1;
	if (curhead == FIFO_SIZE) {
	    curhead = 0;
	}
	CustShptr->gdb.head = curhead;
	db_URXdb.anon.DATA_READY = 1;
    }
}

struct EventType {
  UWORD eType;
  UWORD penDown;
  UWORD screenX;
  UWORD screenY;
  UWORD data[8];
};

struct SndCommandType {
  UWORD cmd;
  UWORD param1hi;
  UWORD param1lo;
  UWORD param2;
  UWORD param3;
};

#define keyDownEvent        4
#define sysTrapEvtGetEvent  41245
#define sysTrapSndDoCmd     41523

int do_api(int api)
{
  switch (api) {
    /************************************
      case sysTrapEvtGetEvent:
      if (KeyBuffer.GetUsed() > 0) {
      EventType *ev = (EventType *)get_real_address(get_long(regs.a[7]));
      ev->eType = keyDownEvent;
      ev->data[0] = KeyBuffer.Get();
      ev->data[1] = 0;
      ev->data[2] = 0;
      return 1;
      }
      break;
      *********************************/
  case sysTrapEvtGetEvent:
    if (CustShptr->kbin != CustShptr->kbout) {

	struct EventType	*ev;
	int			out;

	out = CustShptr->kbout;
	ev = (struct EventType *) get_real_address(get_long(CustShptr->regs.a[7]));

	ev->eType = keyDownEvent;
	ev->data[0] = CustShptr->kb[out];
	ev->data[1] = 0;
	ev->data[2] = 0;
	CustShptr->kbout = (out + 1) & 7;
	return 1;
    }
    break;
  case sysTrapSndDoCmd:
    {
      struct SndCommandType *sc;
      sc = (struct SndCommandType *)
	get_real_address(get_long(CustShptr->regs.a[7]+4));
      if ((sc->cmd >> 8) == 1) {
	CustShptr->BellFreq = (sc->param1hi << 16) + sc->param1lo;
	CustShptr->BellDur = sc->param2;
	CustShptr->BellAmp = sc->param3;
	CustShptr->LcdReq = lcdBell;
	return 1;
      }
    }
    break;
  }
  return 0;
}

/* Custom chip memory bank */

static ULONG custom_lget(CPTR);
static UWORD custom_wget(CPTR);
static UBYTE custom_bget(CPTR);
static void  custom_lput(CPTR, ULONG);
static void  custom_wput(CPTR, UWORD);
static void  custom_bput(CPTR, UBYTE);

addrbank custom_bank = {
    custom_lget, custom_wget, custom_bget,
    custom_lput, custom_wput, custom_bput,
    default_xlate, default_check
};

UWORD custom_wget(CPTR addr)
{
  switch (addr & 0xFFFF) {
    case GRPBASEA:
      return db_GRPBASEA.x;
    case GRPBASEC:
      return db_GRPBASEC.x;
    case GRPMASKA:
      return db_GRPMASKA.x;
    case GRPMASKC:
      return db_GRPMASKC.x;
    case PLLCR:
      return db_PLLCR.x;
    case PLLFSR:
      db_PLLFSR.anon.CLK32 ^= 1;
      return db_PLLFSR.x;
    case ICR:
      return db_ICR.x;
    case IMR:
      return db_IMR.x >> 16;
    case IMR+2:
      return db_IMR.x;
    case IWR:
      return db_IWR.x >> 16;
    case IWR+2:
      return db_IWR.x;
    case ISR:
      return db_ISR.x >> 16;
    case ISR+2:
      return db_ISR.x;
    case IPR:
      db_IPR.anon.PEN = CustShptr->pen;
      return db_IPR.x >> 16;
    case PWMC:
      return db_PWMC.x;
    case PWMP:
      return db_PWMP.x;
    case PWMW:
      return db_PWMW.x;
    case TCTL1:
      return db_TCTL1.x;
    case TPRER1:
      return db_TPRER1.x;
    case TCMP1:
      return db_TCMP1.x;
    case TSTAT1:
      db_TCN1.anon.COUNT += 16;
      if (db_TCN1.anon.COUNT - db_TCMP1.anon.COMPARE < 16) {
        db_TSTAT1.uu.anon.COMP = 1;
        if (db_TCTL1.anon.FRR == 0) {
          db_TCN1.anon.COUNT = 0;
        }
      }
      db_TSTAT1.lastseen |= db_TSTAT1.uu.x;
      return db_TSTAT1.uu.x;
    case TCTL2:
      return db_TCTL2.x;
    case TPRER2:
      return db_TPRER2.x;
    case TCMP2:
      return db_TCMP2.x;
    case TSTAT2:
      db_TSTAT2.lastseen |= db_TSTAT2.uu.x;
      return db_TSTAT2.uu.x;
    case WCR:
      return db_WCR.x;
    case WCN:
      return db_WCN.x;
    case SPIMDATA:
      return db_SPIMDATA.x;
    case SPIMCONT:
      return db_SPIMCONT.x;
    case USTCNT:
      return db_USTCNT.x;
    case UBAUD:
      return db_UBAUD.x;
    case URX:
      if (DEBUGGER_READING) {
	  db_URXdb.anon.DATA_READY = 0;
	  return db_URXdb.x;
      } else {
	  db_URX.anon.DATA_READY = 0;
	  db_IPR.anon.UART = 0;
	  updateisr();
	  return db_URX.x;
      }
    case UTX:
      db_UTX.anon.FIFO_EMPTY = 1;
      db_UTX.anon.FIFO_HALF = 1;
      db_UTX.anon.TX_AVAIL = 1;
      db_UTX.anon.CTS_STATUS = 1;
      return db_UTX.x;
    case UMISC:
      return db_UMISC.x;
    case LXMAX:
      return db_LXMAX.x;
    case LYMAX:
      return db_LYMAX.x;
    case LCXP:
      return db_LCXP.x;
    case LCYP:
      return db_LCYP.x;
    case LCWCH:
      return db_LCWCH.x;
    case LGPMR:
      return db_LGPMR.x;
    case RTCCTL:
      return db_RTCCTL.x;
    case RTCISR:
      return db_RTCISR.x;
    case RTCIENR:
      return db_RTCIENR.x;
    default:
	fatal(); return 0;
	/* __asm int 3; */
      	/* return 0xffff; */
  }
}

UBYTE custom_bget(CPTR addr)
{
  switch (addr & 0xFFFF) {
    case SCR:
      return db_SCR.x;
    case PCTLR:
      return db_PCTLR.x;
    case IVR:
      return db_IVR.x;
    case PCDIR:
      return db_PCDIR.x;
    case PCDATA:
      db_PCDATA.anon.NMI = 1; /* who knows, this makes the power on key work */
      return db_PCDATA.x;
    case PCSEL:
      return db_PCSEL.x;
    case PDDIR:
      return db_PDDIR.x;
    case PDDATA:
      return db_PDDATA.uu.x;
    case PDPUEN:
      return db_PDPUEN.x;
    case PDPOL:
      return db_PDPOL.x;
    case PDIRQEN:
      return db_PDIRQEN.x;
    case PDIRQEDGE:
      return db_PDIRQEDGE.x;
    case PEDIR:
      return db_PEDIR.x;
    case PEDATA:
      return db_PEDATA.x;
    case PEPUEN:
      return db_PEPUEN.x;
    case PESEL:
      return db_PESEL.x;
    case PFDIR:
      return db_PFDIR.x;
    case PFDATA:
      return db_PFDATA.x;
    case PFPUEN:
      return db_PFPUEN.x;
    case PFSEL:
      return db_PFSEL.x;
    case PGDIR:
      return db_PGDIR.x;
    case PGDATA:
      return db_PGDATA.x;
    case PGPUEN:
      return db_PGPUEN.x;
    case PGSEL:
      return db_PGSEL.x;
    case PKDIR:
      return db_PKDIR.x;
    case PKDATA:
      return db_PKDATA.x;
    case PKPUEN:
      return db_PKPUEN.x;
    case PKSEL:
      return db_PKSEL.x;
    case PMDIR:
      return db_PMDIR.x;
    case PMDATA:
      return db_PMDATA.x;
    case PMPUEN:
      return db_PMPUEN.x;
    case PMSEL:
      return db_PMSEL.x;
    case URX: 
      if (DEBUGGER_READING) {
	  return db_URXdb.x >> 8;
      } else {
	  return db_URX.x >> 8;
      }
    case LVPW:
      return db_LVPW.x;
    case LBLKC:
      return db_LBLKC.x;
    case LPICF:
      return db_LPICF.x;
    case LPXCD:
      return db_LPXCD.x;
    case LCKCON:
      return db_LCKCON.x;
    case LLBAR:
      return db_LLBAR.x;
    case LOTCR:
      return db_LOTCR.x;
    case LPOSR:
      return db_LPOSR.x;
    case LFRCM:
      return db_LFRCM.x;
    default:
      /* __asm int 3; */
      /* return custom_wget(addr & 0xfffe) >> (addr & 1 ? 0 : 8); */
      fatal(); return 0;
  }
}

ULONG custom_lget(CPTR addr)
{
  switch (addr & 0xFFFF) {
    case CSAx:
      return db_CSA[0].x;
    case CSAx+4:
      return db_CSA[1].x;
    case CSAx+8:
      return db_CSA[2].x;
    case CSAx+12:
      return db_CSA[3].x;
    case CSCx:
      return db_CSC[0].x;
    case CSCx+4:
      return db_CSC[1].x;
    case CSCx+8:
      return db_CSC[2].x;
    case CSCx+12:
      return db_CSC[3].x;
    case IMR:
      return db_IMR.x;
    case IWR:
      return db_IWR.x;
    case ISR:
      return db_ISR.x;
    case IPR:
      db_IPR.anon.PEN = CustShptr->pen;
      return db_IPR.x;
    case LSSA:
      return db_LSSA.x;
    case RTCHMS: {
      struct tm *st;
      time_t t;
      t= time(0);
      st = localtime(&t);
      db_RTCHMS.anon.HOURS = st->tm_hour;
      db_RTCHMS.anon.MINUTES = st->tm_min;
      db_RTCHMS.anon.SECONDS = st->tm_sec;
      return db_RTCHMS.x;
    }
    case RTCALARM:
      return db_RTCALARM.x;
    default:
      fatal(); return 0;
      /* __asm int 3; */
      /* return ((ULONG)custom_wget(addr & 0xfffe) << 16) | */
	custom_wget((addr+2) & 0xfffe);
  }
}

void custom_wput(CPTR addr, UWORD value)
{
  switch (addr & 0xFFFF) {
    case GRPBASEA:
      db_GRPBASEA.x = value;
      break;
    case GRPBASEC:
      db_GRPBASEC.x = value;
      break;
    case GRPMASKA:
      db_GRPMASKA.x = value;
      break;
    case GRPMASKC:
      db_GRPMASKC.x = value;
      break;
    case PLLCR:
      db_PLLCR.x = value;
      break;
    case PLLFSR:
      db_PLLFSR.x = value;
      break;
    case ICR:
      db_ICR.x = value;
      break;
    case IMR:
      db_IMR.x &= 0x0000FFFF;
      db_IMR.x |= value << 16;
      updateisr();
      break;
    case IMR+2:
      db_IMR.x &= 0xFFFF0000;
      db_IMR.x |= value;
      updateisr();
      break;
    case IWR:
      db_IWR.x &= 0x0000FFFF;
      db_IWR.x |= value << 16;
      break;
    case IWR+2:
      db_IWR.x &= 0xFFFF0000;
      db_IWR.x |= value;
      break;
    case ISR:
      if (db_ICR.anon.ET1 && (value & 0x0001)) {
        db_IPR.anon.IRQ1 = 0;
        updateisr();
      }
      if (db_ICR.anon.ET2 && (value & 0x0002)) {
        db_IPR.anon.IRQ2 = 0;
        updateisr();
      }
      if (db_ICR.anon.ET3 && (value & 0x0004)) {
        db_IPR.anon.IRQ3 = 0;
        updateisr();
      }
      if (db_ICR.anon.ET6 && (value & 0x0008)) {
        db_IPR.anon.IRQ6 = 0;
        updateisr();
      }
      if (value & 0x0080) {
        db_IPR.anon.IRQ7 = 0;
        updateisr();
      }
      break;
    case ISR+2:
      /* do nothing */
      break;
    case PWMC:
      db_PWMC.x = value;
      break;
    case PWMP:
      db_PWMP.x = value;
      break;
    case PWMW:
      db_PWMW.x = value;
      break;
    case TCTL1:
      db_TCTL1.x = value;
      break;
    case TPRER1:
      db_TPRER1.x = value;
      break;
    case TCMP1:
      db_TCMP1.x = value;
      break;
    case TSTAT1:
      db_TSTAT1.uu.x = db_TSTAT1.uu.x & (value | ~db_TSTAT1.lastseen);
      db_TSTAT1.lastseen = 0;
      if (db_TSTAT1.uu.anon.COMP == 0) {
        db_IPR.anon.TMR1 = 0;
        updateisr();
      }
      break;
    case TCTL2:
      db_TCTL2.x = value;
      break;
    case TPRER2:
      db_TPRER2.x = value;
      break;
    case TCMP2:
      db_TCMP2.x = value;
      break;
    case TSTAT2:
      db_TSTAT2.uu.x = db_TSTAT2.uu.x & (value | ~db_TSTAT2.lastseen);
      db_TSTAT2.lastseen = 0;
      if (db_TSTAT2.uu.anon.COMP == 0) {
        db_IPR.anon.TMR2 = 0;
        updateisr();
      }
      break;
    case WCR:
      db_WCR.x = value;
      break;
    case WCN:
      db_WCN.x = 0;
      break;
    case SPIMDATA:
      db_SPIMDATA.x = value;
      break;
    case SPIMCONT:
      db_SPIMCONT.x = value;
      if (db_SPIMCONT.anon.XCH && db_SPIMCONT.anon.IRQEN) {
        db_SPIMCONT.anon.SPIMIRQ = 1;
        db_SPIMCONT.anon.XCH = 0;
        switch (db_PFDATA.x & 0x0F) {
            case 0x6: db_SPIMDATA.x = (0xff - CustShptr->penx)*2; break;
            case 0x9: db_SPIMDATA.x = (0xff - CustShptr->peny)*2; break;
        }
      }
      break;
    case USTCNT:
      db_USTCNT.x = value;
      CustShptr->serial_flags = (db_USTCNT.anon.PARITY_ENABLE << 3) |
	(db_USTCNT.anon.ODD_EVEN << 2) | (db_USTCNT.anon.STOP_BITS << 1) |
	(db_USTCNT.anon.BITS_8);
      break;
    case UBAUD: {
      db_UBAUD.x = value;
      CustShptr->serial_baud = 1036800 / (1 << db_UBAUD.anon.DIVIDE) / 
	(65 - db_UBAUD.anon.PRESCALER);
      /*
       *       fprintf(stderr, "UBAUD = %d\n", value);
       *       fprintf(stderr, "baudrate = %d\n", baudrate);
       */
      break;
    }
    case URX:
      /* ignore db_URX.x = value; */
      break;
    case UTX:
      db_UTX.x = value;
      if (db_UMISC.anon.LOOP) {
        if (DEBUGGER_WRITING) {
	    db_URXdb.anon.DATA = db_UTX.anon.DATA;
	    db_URXdb.anon.DATA_READY = 1;
	} else {
	    db_URX.anon.DATA = db_UTX.anon.DATA;
	    db_URX.anon.DATA_READY = 1;
	    if (db_USTCNT.anon.RX_READY_ENABLE) {
	      db_IPR.anon.UART = 1;
	      updateisr();
	    }
	}
      } else {
        unsigned char outbuf[1] = { value & 0xff };
	int fd = (DEBUGGER_WRITING) ? CustShptr->gdb_writefd :
					CustShptr->serial_writefd;
	write(fd, outbuf, 1);
      }
      break;
    case UMISC:
      db_UMISC.x = value;
      break;
    case LXMAX:
      db_LXMAX.x = value;
      break;
    case LYMAX:
      db_LYMAX.x = value;
      break;
    case LCXP:
      db_LCXP.x = value;
      break;
    case LCYP:
      db_LCYP.x = value;
      break;
    case LCWCH:
      db_LCWCH.x = value;
      break;
    case LGPMR:
      db_LGPMR.x = value;
      CustShptr->grpalette[0] = db_LGPMR.anon.G0;
      CustShptr->grpalette[1] = db_LGPMR.anon.G1;
      CustShptr->grpalette[2] = db_LGPMR.anon.G2;
      CustShptr->grpalette[3] = db_LGPMR.anon.G3;
      break;
    case RTCCTL:
      db_RTCCTL.x = (UBYTE)value;
      break;
    case RTCISR:
      db_RTCISR.x &= ~(UBYTE)value;
      break;
    case RTCIENR:
      db_RTCIENR.x = (UBYTE)value;
      break;
    default:
      /*__asm int 3; */
      /*Beep(1000, 100); */
      fatal();
      break;
  }
}

void custom_bput(CPTR addr, UBYTE value)
{
  switch (addr & 0xFFFF) {
    case SCR:
      db_SCR.x = value & ~(value & 0xE0);
      break;
    case PCTLR:
      db_PCTLR.x = value;
      break;
    case IVR:
      db_IVR.x = value;
      break;
    case PCDIR:
      db_PCDIR.x = value;
      break;
    case PCDATA:
      db_PCDATA.x = value;
      break;
    case PCSEL:
      db_PCSEL.x = value;
      break;
    case PDDIR:
      db_PDDIR.x = value;
      break;
    case PDDATA:
      db_PDDATA.edge &= ~value;
      db_IPR.anon.PEN = CustShptr->pen;
      db_IPR.x = (db_IPR.x & 0xFFFF00FF) |
	((((db_PDDATA.edge & db_PDIRQEDGE.x) |
	   (db_PDDATA.uu.x & ~db_PDIRQEDGE.x)) & db_PDIRQEN.x) << 8);
      updateisr();
      break;
    case PDPUEN:
      db_PDPUEN.x = value;
      break;
    case PDPOL:
      db_PDPOL.x = value;
      break;
    case PDIRQEN:
      db_PDIRQEN.x = value;
      db_IPR.anon.PEN = CustShptr->pen;
      db_IPR.x = (db_IPR.x & 0xFFFF00FF) |
	((((db_PDDATA.edge & db_PDIRQEDGE.x) |
	   (db_PDDATA.uu.x & ~db_PDIRQEDGE.x)) & db_PDIRQEN.x) << 8);
      updateisr();
      break;
    case PDIRQEDGE:
      db_PDIRQEDGE.x = value;
      break;
    case PEDIR:
      db_PEDIR.x = value;
      break;
    case PEDATA:
      db_PEDATA.x = value;
      break;
    case PEPUEN:
      db_PEPUEN.x = value;
      break;
    case PESEL:
      db_PESEL.x = value;
      break;
    case PFDIR:
      db_PFDIR.x = value;
      break;
    case PFDATA:
      db_PFDATA.x = value;
      break;
    case PFPUEN:
      db_PFPUEN.x = value;
      break;
    case PFSEL:
      db_PFSEL.x = value;
      break;
    case PGDIR:
      db_PGDIR.x = value;
      break;
    case PGDATA:
      db_PGDATA.x = value;
      CustShptr->Backlight = (db_PGDATA.x & 0x80);
      break;
    case PGPUEN:
      db_PGPUEN.x = value;
      break;
    case PGSEL:
      db_PGSEL.x = value;
      break;
    case PKDIR:
      db_PKDIR.x = value;
      break;
    case PKDATA:
      db_PKDATA.x = value;
      break;
    case PKPUEN:
      db_PKPUEN.x = value;
      break;
    case PKSEL:
      db_PKSEL.x = value;
      break;
    case PMDIR:
      db_PMDIR.x = value;
      break;
    case PMDATA:
      db_PMDATA.x = value;
      break;
    case PMPUEN:
      db_PMPUEN.x = value;
      break;
    case PMSEL:
      db_PMSEL.x = value;
      break;
    case UTX+1:
      db_UTX.anon.DATA = value;
      if (db_UMISC.anon.LOOP) {
	if (DEBUGGER_WRITING) {
	    db_URXdb.anon.DATA = db_UTX.anon.DATA;
	    db_URXdb.anon.DATA_READY = 1;
	} else {
	    db_URX.anon.DATA = db_UTX.anon.DATA;
	    db_URX.anon.DATA_READY = 1;
	    if (db_USTCNT.anon.RX_READY_ENABLE) {
	      db_IPR.anon.UART = 1;
	      updateisr();
	    }
	}
      } else {
        unsigned char outbuf[1] = { value & 0xff };
	int fd = (DEBUGGER_WRITING) ? CustShptr->gdb_writefd :
					CustShptr->serial_writefd;
	write(fd, outbuf, 1);
      }
      break;
    case LVPW:
      db_LVPW.x = value;
      CustShptr->VPW = db_LVPW.x;
      break;
    case LBLKC:
      db_LBLKC.x = value;
      break;
    case LPICF:
      db_LPICF.x = value;
      CustShptr->PICF = db_LPICF.x;
      break;
    case LPXCD:
      db_LPXCD.x = value;
      break;
    case LCKCON:
      db_LCKCON.x = value;
      CustShptr->LcdPower = db_LCKCON.anon.LCDCON ? lcdOn : lcdOff;
      break;
    case LLBAR:
      db_LLBAR.x = value;
      break;
    case LOTCR:
      db_LOTCR.x = value;
      break;
    case LPOSR:
      db_LPOSR.x = value;
      CustShptr->POSR = db_LPOSR.x;
      break;
    case LFRCM:
      db_LFRCM.x = value;
      break;
    case RTCCTL:
      db_RTCCTL.x = value;
      break;
    case RTCISR:
      db_RTCISR.x &= ~value;
      break;
    case RTCIENR:
      db_RTCIENR.x = value;
      break;
    default:
      /* __asm int 3; */
      /* Beep(1000, 100); */
      fatal();
      break;
  }
}

void custom_lput(CPTR addr, ULONG value)
{
  switch (addr & 0xFFFF) {
    case CSAx:
      db_CSA[0].x = value;
      break;
    case CSAx+4:
      db_CSA[1].x = value;
      sram_protect = (value & 0x0008) != 0;
      break;
    case CSAx+8:
      db_CSA[2].x = value;
      break;
    case CSAx+12:
      db_CSA[3].x = value;
      break;
    case CSCx:
      db_CSC[0].x = value;
      break;
    case CSCx+4:
      db_CSC[1].x = value;
      break;
    case CSCx+8:
      db_CSC[2].x = value;
      break;
    case CSCx+12:
      db_CSC[3].x = value;
      break;
    case IMR:
      db_IMR.x = value;
      updateisr();
      break;
    case IWR:
      db_IWR.x = value;
      break;
    case ISR:
      if (db_ICR.anon.ET1 && (value & 0x00010000)) {
        db_IPR.anon.IRQ1 = 0;
        updateisr();
      }
      if (db_ICR.anon.ET2 && (value & 0x00020000)) {
        db_IPR.anon.IRQ2 = 0;
        updateisr();
      }
      if (db_ICR.anon.ET3 && (value & 0x00040000)) {
        db_IPR.anon.IRQ3 = 0;
        updateisr();
      }
      if (db_ICR.anon.ET6 && (value & 0x00080000)) {
        db_IPR.anon.IRQ6 = 0;
        updateisr();
      }
      if (value & 0x00800000) {
        db_IPR.anon.IRQ7 = 0;
        updateisr();
      }
      break;
    case IPR:
      /* do nothing */
      break;
    case LSSA:
      CustShptr->lssa = value;
      db_LSSA.x = value;
      break;
    case RTCHMS:
      db_RTCHMS.x = value;
      break;
    case RTCALARM:
      db_RTCALARM.x = value;
      break;
    default:
      /* __asm int 3; */
      /* custom_wput(addr & 0xfffe, value >> 16); */
      /* custom_wput((addr+2) & 0xfffe, (UWORD)value); */
      fatal();
      break;
  }
}
