

#define TEST

#include <string.h>

typedef unsigned char byte;
typedef signed char sbyte;
typedef unsigned short word;
typedef signed short sword;

/// HARDWARE

__sfr __at(0x00) hw_col0r;	// palette 0
__sfr __at(0x01) hw_col1r;
__sfr __at(0x02) hw_col2r;
__sfr __at(0x03) hw_col3r;
__sfr __at(0x04) hw_col0l;
__sfr __at(0x05) hw_col1l;
__sfr __at(0x06) hw_col2l;
__sfr __at(0x07) hw_col3l;	// palette 7

__sfr __at(0x08) hw_intst;	// intercept test feedback
__sfr __at(0x09) hw_horcb;	// horiz color boundary
__sfr __at(0x0a) hw_verbl;	// vertical blanking line * 2
__sfr __at(0x0c) hw_magic;	// magic register
__sfr __at(0x0d) hw_infbk;	// interrupt feedback
__sfr __at(0x0e) hw_inmod;	// interrupt enable
__sfr __at(0x0f) hw_inlin;	// interrupt line
__sfr __at(0x19) hw_xpand;	// expander register

__sfr __at(0x10) hw_p1ctrl;	// player controls
__sfr __at(0x11) hw_p2ctrl;	// player controls
__sfr __at(0x12) hw_p3ctrl;	// player controls
__sfr __at(0x13) hw_p4ctrl;	// player controls

#define M_SHIFT0	0x00
#define M_SHIFT1	0x01
#define M_SHIFT2	0x02
#define M_SHIFT3	0x03
#define M_XPAND		0x08
#define M_MOVE		0x00
#define M_OR		0x10
#define M_XOR		0x20
#define M_FLOP		0x40
#define M_SHIFT(x)	((x)&3)
#define XPAND_COLORS(off,on) (((off)&3) | (((on)&3)<<2))

/// GRAPHICS FUNCTIONS

#define VHEIGHT 89	// number of scanlines
#define VBWIDTH 40	// number of bytes per scanline
#define PIXWIDTH 160	// 4 pixels per byte

#define EXIT_CLIPDEST(addr) if ((((word)addr)&0xfff) >= 0xe10) return

byte __at (0x0000) vmagic[VHEIGHT][VBWIDTH];
byte __at (0x4000) vidmem[VHEIGHT][VBWIDTH];

// start @ $4FCE
volatile word MUZPC;   // music PC
volatile word MUZSP;   // music SP
volatile byte PVOLAB;  // channels A and B volume
volatile byte PVOLMC;  // channel C volume and noise mask
volatile byte VOICES;  // voice smask

volatile byte CT[8];   // counter timers

volatile byte CNT;
volatile byte SEMI4S;
volatile byte OPOT[4];
volatile byte KEYSEX;
volatile byte OSW[4];
volatile word COLLST;

volatile byte DURAT;   // note duration
volatile byte TMR60;   // 1/60 sec timer
volatile byte TIMOUT;  // blackout timer
volatile byte GTSECS;  // seconds timer
volatile byte GTMINS;  // minutes timer

unsigned long RANSHT;	// RNG
byte NUMPLY;		// # players
byte ENDSCR[3];		// end score
byte MRLOCK;		// magic register lock out
byte GAMSTB;		// game status
byte PRIOR;		// music protect
byte SENFLG; 		// sentry control

byte* UMARGT;		// user mask table (-64 bytes)
word* USERTB;		// user routine table (-128 bytes)

// start routine @ 0x0
void bios_start()  __naked {
__asm
  DI			; disable interrupts
  LD	HL,#0x2000
  LD	A,(HL)		; A <- mem[0x2000]
  CP	#0x55		; found sentinel byte? ($55)
  JP	Z,FoundSentinel	; yes, load program
#ifndef TEST
  JP	0x2000		; jump to $2000
#else
  JP	_main		; jump to test program
#endif
FoundSentinel:
  LD	SP,#0x4fce	; position stack below BIOS vars
  CALL	_bios_init	; misc. bios init routines
  LD	HL,#0x2005	; cartridge start vector
  LD	A,(HL)
  INC	HL
  LD	H,(HL)
  LD	L,A
  JP	(HL)		; jump to cart start vector
  .ds	0x38 - 0x1b	; eat up space until 0x38
__endasm;
}

void interrupt_0x38() __naked {
__asm
  push	hl
  push	af
  push	bc
  push	de
  push	ix
  push	iy
  ld	hl,#0
  add	hl,sp
  push	hl		; HL points to context block
  call	_SYSCALL	; syscall handler
  pop	hl
  pop	iy
  pop	ix
  pop	de
  pop	bc
  pop	af
  pop	hl
  ret
__endasm;
}

void _predope() __naked {
__asm
  .ds	0x200-0x52	; skip to 0x200
__endasm;
}

// DOPE vector @ 0x200
void DOPE()  __naked {
__asm
  JP	_STIMER
  JP	_CTIMER
  .db	0x20, 8, 8, 1, 7	; Font descriptor (big font)
  .dw	_BIGFONT
  .db	0xa0, 4, 6, 1, 5	; Font descriptor (small font)
  .dw	_SMLFONT
__endasm;
}

typedef struct {
  byte base_ch;
  byte frame_x;
  byte frame_y;
  byte pattern_x;
  byte pattern_y;
  const byte* chartab;
} FontDescriptor;

#define LOCHAR 0x20
#define HICHAR 0x63

extern const char BIGFONT[HICHAR-LOCHAR+1][7];
extern const char SMLFONT[HICHAR-LOCHAR+1][5];

const FontDescriptor __at(0x206) FNTSYS; // = { 0x20, 8, 8, 1, 7, (byte*)BIGFONT };
const FontDescriptor __at(0x20d) FNTSML; // = { 0xa0, 4, 6, 1, 5, (byte*)SMLFONT };

void hw_interrupt() __interrupt {
  CT[0]++;
  CT[1]++;
  CT[2]++;
  CT[3]++;
  if (++TMR60 == 60) {
    TMR60 = 0;
    if (++GTSECS == 60) {
      GTMINS++;
    }
  }
}

const void* const actint_vec = &hw_interrupt;

void STIMER() {
}

void CTIMER() {
}

void bios_init() {
  memset((void*)0x4fce, 0, 0x5000-0x4fce);
}

///// INTERPRETER

typedef struct {
  union {
    struct {
      word iy,ix,de,bc,af,hl;
    } w;
    struct {
      byte iyl,iyh,ixl,ixh,e,d,c,b,f,a,l,h;
    } b;
  } regs;
  byte* params;
} ContextBlock;

#define REG_IY	0x1
#define REG_E	0x2
#define REG_D	0x4
#define REG_C	0x8
#define REG_B	0x10
#define REG_IX  0x20
#define REG_A	0x40
#define REG_HL	0x80
#define REG_DE	(REG_D|REG_E)
#define REG_BC	(REG_B|REG_C)

typedef void (Routine)();
typedef void (SysRoutine)(ContextBlock *ctx);

typedef struct {
  SysRoutine* routine;
  byte argmask;
} SysCallEntry;

void SYSCALL(ContextBlock *ctx);

void INTPC(ContextBlock *ctx) {
  while (ctx->params[0] != 2) { // 2 = exit
    SYSCALL(ctx);
  }
}

// never called, hopefully
void EXIT(ContextBlock *ctx) {
  ctx;
}

// jumps to HL
void RCALL(ContextBlock *ctx) {
  ((Routine*)ctx->regs.w.hl)();
}

// start interpreting at HL
void MCALL(ContextBlock *ctx) {
  ctx; // TODO
}

// exit MCALL loop
void MRET(ContextBlock *ctx) {
  ctx; // TODO
}

// jump within MCALL
void MJUMP(ContextBlock *ctx) {
  ctx->params = (byte*) ctx->regs.w.hl; // TODO?
}

void suckParams(ContextBlock *ctx, byte argmask) {
  byte* dest = (byte*) ctx;
  byte* src = ctx->params;
  if (argmask & REG_IX) {
    ctx->regs.b.ixl = *src++;
    ctx->regs.b.ixh = *src++;
  }
  if (argmask & REG_E)
    ctx->regs.b.e = *src++;
  if (argmask & REG_D)
    ctx->regs.b.d = *src++;
  if (argmask & REG_C)
    ctx->regs.b.c = *src++;
  if (argmask & REG_B)
    ctx->regs.b.b = *src++;
  if (argmask & REG_A)
    ctx->regs.b.a = *src++;
  if (argmask & REG_HL) {
    ctx->regs.b.l = *src++;
    ctx->regs.b.h = *src++;
  }
  ctx->params = src;
}

void SUCK(ContextBlock* ctx) {
  suckParams(ctx, ctx->regs.b.b);
}

void ACTINT(ContextBlock *ctx) {
  ctx;
  hw_inlin = 200;
  hw_infbk = (byte) &actint_vec;
  hw_inmod = 8;
__asm
  LD	A,#0x2	; I = 0x200
  LD	I,A
  IM	2	; mode 2
  EI		; enable interrupts
__endasm;
}

// Outputs D to port 0A, B to port 09, A to port 0E.
void SETOUT(ContextBlock *ctx) {
  hw_verbl = ctx->regs.b.d;
  hw_horcb = ctx->regs.b.b;
  hw_inmod = ctx->regs.b.a;
}

// sets color palettes from (HL)
void COLSET(ContextBlock *ctx) {
  byte* palette = (byte*) ctx->regs.w.hl;
  hw_col0r = *palette++;
  hw_col1r = *palette++;
  hw_col2r = *palette++;
  hw_col3r = *palette++;
  hw_col0l = *palette++;
  hw_col1l = *palette++;
  hw_col2l = *palette++;
  hw_col3l = *palette++;
}

// Stores A in BC bytes starting at location DE.
void FILL(ContextBlock *ctx) {
  byte* dest = (byte*) ctx->regs.w.de;
  word count = ctx->regs.w.bc;
  byte val = ctx->regs.b.a;
  memset(dest, val, count);
}

const char BIGFONT[HICHAR-LOCHAR+1][7] = {/*{count:68,w:8,h:7,brev:1}*/
{ 0x00,0x00,0x00,0x00,0x00,0x00,0x00 },{ 0x00,0x20,0x20,0x20,0x00,0x20,0x00 },{ 0x50,0x50,0x50,0x00,0x00,0x00,0x00 },{ 0x00,0x50,0xF8,0x50,0xF8,0x50,0x00 },{ 0x00,0xF8,0xA0,0xF8,0x28,0xF8,0x00 },{ 0x00,0xC8,0xD0,0x20,0x58,0x98,0x00 },{ 0x00,0xE0,0xA8,0xF8,0x90,0xF8,0x00 },{ 0x40,0x40,0x40,0x00,0x00,0x00,0x00 },{ 0x30,0x20,0x20,0x20,0x20,0x20,0x30 },{ 0x60,0x20,0x20,0x20,0x20,0x20,0x60 },{ 0x00,0x20,0xA8,0x70,0xA8,0x20,0x00 },{ 0x00,0x20,0x20,0xF8,0x20,0x20,0x00 },{ 0x00,0x00,0x00,0x00,0x60,0x60,0x40 },{ 0x00,0x00,0x00,0xF8,0x00,0x00,0x00 },{ 0x00,0x00,0x00,0x00,0x60,0x60,0x00 },{ 0x00,0x08,0x10,0x20,0x40,0x80,0x00 },{ 0x00,0xF8,0x88,0xE8,0x88,0xF8,0x00 },{ 0x00,0x10,0x30,0x50,0x10,0x10,0x00 },{ 0x00,0xF8,0x08,0xF8,0x80,0xF8,0x00 },{ 0x00,0xF8,0x08,0xF8,0x08,0xF8,0x00 },{ 0x00,0x38,0x48,0x88,0xF8,0x08,0x00 },{ 0x00,0xF8,0x80,0xF8,0x08,0xF8,0x00 },{ 0x00,0xF8,0x80,0xF8,0x88,0xF8,0x00 },{ 0x00,0xF8,0x08,0x10,0x20,0x40,0x00 },{ 0x00,0xF8,0x88,0xF8,0x88,0xF8,0x00 },{ 0x00,0xF8,0x88,0xF8,0x08,0xF8,0x00 },{ 0x00,0x30,0x30,0x00,0x30,0x30,0x00 },{ 0x00,0x30,0x30,0x00,0x30,0x30,0x20 },{ 0x08,0x10,0x20,0x40,0x20,0x10,0x08 },{ 0x00,0x00,0xF8,0x00,0xF8,0x00,0x00 },{ 0x40,0x20,0x10,0x08,0x10,0x20,0x40 },{ 0x00,0xF8,0x08,0x78,0x00,0x60,0x00 },{ 0x00,0xF8,0xA8,0xB8,0x80,0xF8,0x00 },{ 0x00,0xF8,0x88,0xF8,0x88,0x88,0x00 },{ 0x00,0xF0,0x90,0xF8,0x88,0xF8,0x00 },{ 0x00,0xF8,0x80,0x80,0x80,0xF8,0x00 },{ 0x00,0xE0,0x90,0x88,0x88,0xF8,0x00 },{ 0x00,0xF8,0x80,0xF8,0x80,0xF8,0x00 },{ 0x00,0xF8,0x80,0xF8,0x80,0x80,0x00 },{ 0x00,0xF8,0x80,0xB8,0x88,0xF8,0x00 },{ 0x00,0x88,0x88,0xF8,0x88,0x88,0x00 },{ 0x00,0x40,0x40,0x40,0x40,0x40,0x00 },{ 0x00,0x08,0x08,0x88,0x88,0xF8,0x00 },{ 0x00,0x88,0x90,0xA0,0x90,0x88,0x00 },{ 0x00,0x80,0x80,0x80,0x80,0xF8,0x00 },{ 0x00,0xFE,0x92,0x92,0x92,0x92,0x00 },{ 0x00,0x88,0xC8,0xA8,0x98,0x88,0x00 },{ 0x00,0xF8,0x88,0x88,0x88,0xF8,0x00 },{ 0x00,0xF8,0x88,0x88,0xF8,0x80,0x00 },{ 0x00,0xF8,0x88,0xA8,0xA8,0xF8,0x10 },{ 0x00,0xF8,0x88,0xF8,0x90,0x88,0x00 },{ 0x00,0xF8,0x80,0xF8,0x08,0xF8,0x00 },{ 0x00,0xF8,0x20,0x20,0x20,0x20,0x00 },{ 0x00,0x88,0x88,0x88,0x88,0xF8,0x00 },{ 0x00,0x88,0x88,0x90,0xA0,0xC0,0x00 },{ 0x00,0x92,0x92,0x92,0x92,0xFE,0x00 },{ 0x00,0x88,0x50,0x20,0x50,0x88,0x00 },{ 0x00,0x88,0x88,0xF8,0x08,0xF8,0x00 },{ 0x00,0xF8,0x10,0x20,0x40,0xF8,0x00 },{ 0x38,0x20,0x20,0x20,0x20,0x20,0x38 },{ 0x00,0x80,0x40,0x20,0x10,0x08,0x00 },{ 0x70,0x10,0x10,0x10,0x10,0x10,0x70 },{ 0x00,0x20,0x70,0xA8,0x20,0x20,0x00 },{ 0x00,0x20,0x40,0xF8,0x40,0x20,0x00 },{ 0x00,0x20,0x20,0xA8,0x70,0x20,0x00 },{ 0x00,0x20,0x10,0xF8,0x10,0x20,0x00 },{ 0x00,0x88,0x50,0x20,0x50,0x88,0x00 },{ 0x00,0x20,0x00,0xF8,0x00,0x20,0x00 }};

const char SMLFONT[HICHAR-LOCHAR+1][5] = {/*{count:68,w:5,h:5,brev:1}*/
{ 0x00,0x00,0x00,0x00,0x00 },{ 0x40,0x40,0x00,0x40,0x00 },{ 0xA0,0xA0,0x00,0x00,0x00 },{ 0x60,0xF0,0xF0,0x60,0x00 },{ 0x40,0xE0,0xE0,0x40,0x00 },{ 0x90,0x20,0x40,0x90,0x00 },{ 0xC0,0xB0,0xE0,0xD0,0x00 },{ 0x20,0x40,0x00,0x00,0x00 },{ 0x20,0x40,0x40,0x20,0x00 },{ 0x40,0x20,0x20,0x40,0x00 },{ 0x40,0xE0,0x40,0xA0,0x00 },{ 0x00,0x40,0xE0,0x40,0x00 },{ 0x00,0x00,0x00,0x60,0x20 },{ 0x00,0x00,0xE0,0x00,0x00 },{ 0x00,0x00,0x00,0x40,0x00 },{ 0x20,0x20,0x40,0x40,0x00 },{ 0xE0,0xA0,0xA0,0xE0,0x00 },{ 0xC0,0x40,0x40,0xE0,0x00 },{ 0xE0,0x20,0xC0,0xE0,0x00 },{ 0xE0,0x60,0x20,0xE0,0x00 },{ 0xA0,0xA0,0xE0,0x20,0x00 },{ 0xE0,0xC0,0x20,0xE0,0x00 },{ 0xC0,0x80,0xE0,0xE0,0x00 },{ 0xE0,0x20,0x40,0x40,0x00 },{ 0x60,0xE0,0xA0,0xE0,0x00 },{ 0xE0,0xE0,0x20,0x60,0x00 },{ 0x00,0x40,0x00,0x40,0x00 },{ 0x00,0x40,0x00,0x60,0x20 },{ 0x00,0x20,0x40,0x20,0x00 },{ 0x00,0xE0,0x00,0xE0,0x00 },{ 0x00,0x40,0x20,0x40,0x00 },{ 0xE0,0x20,0x60,0x00,0x40 },{ 0xF0,0x90,0x10,0xD0,0xF0 },{ 0x60,0xA0,0xE0,0xA0,0x00 },{ 0xC0,0xE0,0xA0,0xE0,0x00 },{ 0x60,0x80,0x80,0xE0,0x00 },{ 0xC0,0xA0,0xA0,0xC0,0x00 },{ 0xE0,0xC0,0x80,0xE0,0x00 },{ 0xE0,0xC0,0x80,0x80,0x00 },{ 0x60,0x80,0xA0,0xE0,0x00 },{ 0xA0,0xA0,0xE0,0xA0,0x00 },{ 0xE0,0x40,0x40,0xE0,0x00 },{ 0x60,0x20,0xA0,0xE0,0x00 },{ 0xA0,0xC0,0xC0,0xA0,0x00 },{ 0x80,0x80,0x80,0xE0,0x00 },{ 0xE0,0xE0,0xE0,0xA0,0x00 },{ 0xE0,0xA0,0xA0,0xA0,0x00 },{ 0xE0,0xA0,0xA0,0xE0,0x00 },{ 0xE0,0xA0,0xE0,0x80,0x00 },{ 0xE0,0xA0,0xE0,0xF0,0x00 },{ 0xE0,0xA0,0xC0,0xA0,0x00 },{ 0xE0,0x80,0x60,0xE0,0x00 },{ 0xE0,0x40,0x40,0x40,0x00 },{ 0xA0,0xA0,0xA0,0xE0,0x00 },{ 0xA0,0xA0,0xC0,0x80,0x00 },{ 0xA0,0xE0,0xE0,0xE0,0x00 },{ 0xA0,0x40,0xA0,0xA0,0x00 },{ 0xA0,0xE0,0x40,0x40,0x00 },{ 0xE0,0x20,0x40,0xE0,0x00 },{ 0x60,0x40,0x40,0x60,0x00 },{ 0x40,0x40,0x20,0x20,0x00 },{ 0x60,0x20,0x20,0x60,0x00 },{ 0x40,0xA0,0x00,0x00,0x00 },{ 0x00,0x00,0x00,0x00,0xF0 },{ 0x80,0x40,0x00,0x00,0x00 },{ 0x00,0x60,0xA0,0xE0,0x00 },{ 0x80,0xE0,0xA0,0xE0,0x00 },{ 0x00,0x60,0x80,0xE0,0x00 }};

// draw a letter
void draw_char(const FontDescriptor* font, byte ch, byte x, byte y, byte op) {
  const byte* src = font->chartab + (ch-font->base_ch)*font->pattern_y;
  byte xb = x>>2;		// divide x by 4
  byte* dest = &vmagic[y][xb];	// destination address
  hw_magic = M_SHIFT(x) | M_XPAND | op;
  for (byte i=0; i<font->pattern_y; i++) {
    char b = *src++;
    EXIT_CLIPDEST(dest);
    *dest++ = b;	// expand lower nibble -> 1st byte
    *dest++ = b;	// expand upper nibble -> 2nd byte
    *dest++ = 0;	// leftover -> 3rd byte
    *dest = 0;		// reset upper/lower flag
    dest += VBWIDTH-3;	// we incremented 3 bytes for this line
  }
}

#define FONT_IX ((const FontDescriptor*)ctx->regs.w.ix)

void draw_string(ContextBlock *ctx, const char* str, byte x, byte y, byte op) {
  do {
    byte ch = *str++;
    if (!ch) {
      ctx->regs.b.e = x;
      break;
    }
    if (ch < 0x20) {
      x += 8 * ch;
    } else if (ch < 0x64) {
      draw_char(&FNTSYS, ch, x, y, op);
      x += 8;
    } else if (ch >= 0x80) {
      draw_char(FONT_IX, ch, x, y, op);
      x += FONT_IX->frame_x;
    } else {
      /*
      if (ch & 0x10) {
        ctx->regs.b.ixl = *str++;
        ctx->regs.b.ixh = *str++;
      }
      if (ch & 0x1)
        ctx->regs.b.e = *str++;
      if (ch & 0x2)
        ctx->regs.b.d = *str++;
      if (ch & 0x4)
        ctx->regs.b.c = *str++;
      */
      // TODO: only can change font
    }
  } while (1);
}

// String display routine (pass pointer to string)
void STRDIS2(ContextBlock *ctx, char *str) {
  byte opts = ctx->regs.b.c;
  byte x = ctx->regs.b.e;
  byte y = ctx->regs.b.d;
  void* fontdesc = (void*) ctx->regs.w.ix;
  hw_xpand = opts & 0xf;
  draw_string(ctx, str, x, y, 3&(opts>>4)); // TODO: opts
}

// String display routine
void STRDIS(ContextBlock *ctx) {
  char* str = (char*) ctx->regs.w.hl;
  STRDIS2(ctx, str);
}

// Character display routine
void CHRDIS(ContextBlock *ctx) {
  char chstr[2];
  chstr[0] = ctx->regs.b.a;
  chstr[1] = 0;
  STRDIS2(ctx, chstr);
}

// BCD routine
const char BCDTAB[17] = "0123456789*+,-./";

void DISNUM(ContextBlock *ctx) {
  // TODO: options, B
  word oldhl = ctx->regs.w.hl;
  byte val = *(byte*) oldhl;
  char bcdstr[3];
  bcdstr[0] = BCDTAB[val >> 4];
  bcdstr[1] = BCDTAB[val & 15];
  bcdstr[2] = 0;
  STRDIS2(ctx, bcdstr);
}

typedef struct {
  sbyte xofs, yofs;
  byte xsize, ysize;
  byte pattern[0];
} PatternBlock;
// TODO

void wait_vsync() {
  byte x = TMR60;
  while (x == TMR60) ; // wait until timer/60 changes
}

void PAWS(ContextBlock *ctx) {
  while (ctx->regs.b.b--) {
    wait_vsync();
  }
}

// MATH

void RANGED(ContextBlock *ctx) {
  /* Algorithm "xor" from p. 4 of Marsaglia, "Xorshift RNGs" */
  RANSHT ^= RANSHT << 13;
  RANSHT ^= RANSHT >> 17;
  RANSHT ^= RANSHT << 5;
  if (ctx->regs.b.a == 0) {
    ctx->regs.b.a = (byte)RANSHT;
  } else {
    ctx->regs.b.a = (byte)(RANSHT % ctx->regs.b.a);
  }
}

const SysCallEntry SYSCALL_TABLE[64] = {
  /* 0 */
  { &INTPC,	0 },
  { &EXIT,	0 },
  { &RCALL,	REG_HL },
  { &MCALL,	REG_HL },
  { &MRET, 	0 },
  /* 10 */
  { &MJUMP,	REG_HL },
  { &SUCK,	REG_B },
  { &ACTINT,	0 },
  { 0, 0 },
  { 0, 0 },
  /* 20 */
  { 0, 0 },
  { &SETOUT,	REG_D|REG_B|REG_A },
  { &COLSET,	REG_HL },
  { &FILL,	REG_A|REG_BC|REG_DE },
  { 0, 0 },
  /* 30 */
  { 0, 0 },
  { 0, 0 },
  { 0, 0 },
  { 0, 0 },
  { 0, 0 },
  /* 40 */
  { 0, 0 },
  { 0, 0 },
  { 0, 0 },
  { 0, 0 },
  { 0, 0 },
  /* 50 */
  { &CHRDIS,	REG_E|REG_D|REG_C|REG_A },
  { &STRDIS,	REG_E|REG_D|REG_C|REG_HL },
  { &DISNUM,	REG_E|REG_D|REG_C|REG_HL },
  { 0, 0 },
  { 0, 0 },
  /* 60 */
  { 0, 0 },
  { 0, 0 },
  { 0, 0 },
  { 0, 0 },
  { 0, 0 },
  /* 70 */
  { 0, 0 },
  { 0, 0 },
  { 0, 0 },
  { 0, 0 },
  { 0, 0 },
  /* 80 */
  { &PAWS,	REG_B },
  { 0, 0 },
  { 0, 0 },
  { 0, 0 },
  { 0, 0 },
  /* 90 */
  { 0, 0 },
  { 0, 0 },
  { 0, 0 },
  { 0, 0 },
  { 0, 0 },
  /* 100 */
  { 0, 0 },
  { 0, 0 },
  { 0, 0 },
  { 0, 0 },
  { 0, 0 },
  /* 110 */
  { 0, 0 },
  { 0, 0 },
  { 0, 0 },
  { 0, 0 },
  { &RANGED,	REG_A },
  /* 120 */
  { 0, 0 },
  { 0, 0 },
  { 0, 0 },
  { 0, 0 },
};

void SYSCALL(ContextBlock *ctx) {
  byte op = *ctx->params++;
  byte argmask;
  SysRoutine* routine;
  // user-defined?
  if (op & 0x80) {
    argmask = UMARGT[op>>1];
    routine = (SysRoutine*) USERTB[op>>1];
  } else {
    const SysCallEntry* entry = &SYSCALL_TABLE[op>>1];
    argmask = entry->argmask;
    routine = entry->routine;
  }
  // suck params into context block?
  if (op & 1) {
    suckParams(ctx, argmask);
  }
  // call the routine
  if (routine) {
    routine(ctx);
  }
}

#ifdef TEST
void main() {
__asm
	LD	SP,#0x4fce	; position stack below BIOS vars
  	ld	hl,#0x20d
  	push	hl
  	pop	ix
	RST	0x38
  	.db	0	; INTPC
  	.db	22+1	; SETOUT
        .db	102*2, 0x1c, 0x08
        .db	14	; ACTINT
        .db	24+1	; COLSET
        .dw	_main   ; palette???
        .db	26+1	; FILL
        .dw	0x4000
        .dw	90*40
        .db	0xaa
        .db	52+1	; draw string
        .db	0
        .db	32
        .db	0x0c
        .dw	HelloString
        .db	52+1	; draw string
        .db	0
        .db	48
        .db	0x0c
        .dw	HelloString
        .db	50+1	; draw char
        .db	0
        .db	64
        .db	0x0c
        .db	0x40
        .db	2	; EXIT
.loop:
        ld	hl,#0x4FED
        ld	a,(hl)
        add	a,#0x30
	RST	0x38
        .db	50	; draw char
        jp	.loop
HelloString:
        .ascii	"HELLO WORLD! "
        .db	0xc1, 0xc2, 0xc3, 0xc4, 0xc5
        .db	0
; Critter Pattern
; Color 0 = White and Color 2 = Black
;
PATERN: .DB     0,0      ; (0,0) Position
        .DB     0x02,0x08  ; 2 byte, 8 line pattern size

        .DB     0x0A,0xA0  ; 0000101010100000 - . . 2 2 2 2 . .  
        .DB     0x22,0x88  ; 0010001010001000 - . 2 . 2 2 . 2 .
        .DB     0xAA,0xAA  ; 1010101010101010 - 2 2 2 2 2 2 2 2
        .DB     0x2A,0xA8  ; 0010101010101000 - . 2 2 2 2 2 2 .
        .DB     0x08,0x20  ; 0000100000100000 - . . 2 . . 2 . .
        .DB     0x20,0x08  ; 0010000000001000 - . 2 . . . . 2 .
        .DB     0x08,0x20  ; 0000100000100000 - . . 2 . . 2 . .
        .DB     0x00,0x00  ; 0000000000000000 - . . . . . . . .
__endasm;
  	while (1) ;
}
#endif
