Marcus Comstedt's SoftVMS ported to Pokitto.

Dependencies:   PokittoLib

Fork of HelloWorld by Pokitto Community Team

cpu.cpp

Committer:
fmanga
Date:
2018-03-31
Revision:
11:88c459b0ac3a

File content as of revision 11:88c459b0ac3a:

#ifdef WIN32
#include <windows.h>
#include <io.h>
#define HAVE_FCNTL_H
#define HAVE_ERRNO_H
#endif

#ifdef __DJGPP__
#include <io.h>
#include <sys/timeb.h>
#define HAVE_FCNTL_H
#define HAVE_ERRNO_H
#define HAVE_SYS_TIME_H
#endif

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#ifdef HAVE_FCNTL_H
#include <fcntl.h>
#endif
#ifdef HAVE_ERRNO_H
#include <errno.h>
#endif
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
#ifdef HAVE_UTIME_H
#include <utime.h>
#endif
#ifdef TIME_WITH_SYS_TIME
#include <sys/time.h>
#include <time.h>
#else
#ifdef HAVE_SYS_TIME_H
#include <sys/time.h>
#else
#include <time.h>
#endif
#endif
#ifdef HAVE_DEVICES_TIMER_H
#include <devices/timer.h>
#undef tv_secs
#undef tv_micro
#define tv_sec tv_secs
#define tv_usec tv_micro
#endif

#include "prototypes.h"

#define SGNEXT(n) ((n)&0x80? (n)-0x100:(n))

#ifndef BIG
#define BIG 
#endif

#ifndef O_BINARY
#define O_BINARY 0
#endif

// BIG extern const unsigned char flash[];
#include "flash.h"

BIG unsigned char bios[0];
unsigned char ram[2][0x100];
unsigned char sfr[0x100];
unsigned char xram[3][0x80];
unsigned char wram[0x200];
const unsigned char *rom;

unsigned char parity[0x100];

int pc;
int lcd_updated, lcdon, imask, intreq, hasbios=0;
int spd;
int t0h, t0l, t0base, t0scale;
int t1h, t1l;
int gamesize;

void keypress(int i)
{
  sfr[0x4c]&=~(1<<i);
  if(sfr[0x4e]&4)
    sfr[0x4e]|=2;
}

void keyrelease(int i)
{
  sfr[0x4c]|=(1<<i);
  if(sfr[0x4e]&4)
    sfr[0x4e]|=2;
}

int tobcd(int n)
{
  return ((n/10)<<4)|(n%10);
}

/*
int initflash(int fd)
{
  int r=0, t=0;
  memset(flash, 0, sizeof(flash));
  while(t<sizeof(flash) && (r=read(fd, flash+t, sizeof(flash)-t))>0)
    t += r;
  if(r<0 || t<0x480)
    return 0;
  return t;
}

void fakeflash(char *filename, int sz)
{
  unsigned char *root, *fat, *dir;
  time_t t = time(NULL);
  struct tm *tm = localtime(&t);
  int i;
  char *fn2;

  if((fn2 = strrchr(filename, '/'))!=NULL)
    filename = fn2+1;
  else if((fn2 = strrchr(filename, '\\'))!=NULL)
    filename = fn2+1;
  memset(flash+241*512, 0, sizeof(flash)-241*512);
  root = flash+255*512;
  fat = flash+254*512;
  dir = flash+253*512;
  sz = ((sz+511)>>9);
  for(i=0; i<256*2; i+=2) {
    fat[i] = 0xfc;
    fat[i+1] = 0xff;
  }
  for(i=0; i<sz; i++) {
    fat[2*i] = i+1;
    fat[2*i+1] = 0;
  }
  if((--i)>=0) {
    fat[2*i] = 0xfa;
    fat[2*i+1] = 0xff;
  }
  fat[254*2] = 0xfa;
  fat[254*2+1] = 0xff;
  fat[255*2] = 0xfa;
  fat[255*2+1] = 0xff;
  for(i=253; i>241; --i) {
    fat[2*i] = i-1;
    fat[2*i+1] = 0;
  }
  fat[241*2] = 0xfa;
  fat[241*2+1] = 0xff;
  dir[0] = 0xcc;
  strncpy(dir+4, filename, 12);
  for(i=strlen(filename); i<12; i++)
    dir[4+i]=' ';
  dir[0x10] = tobcd(tm->tm_year/100+19);
  dir[0x11] = tobcd(tm->tm_year%100);
  dir[0x12] = tobcd(tm->tm_mon+1);
  dir[0x13] = tobcd(tm->tm_mday);
  dir[0x14] = tobcd(tm->tm_hour);
  dir[0x15] = tobcd(tm->tm_min);
  dir[0x16] = tobcd(tm->tm_sec);
  dir[0x17] = tobcd(tm->tm_wday);
  dir[0x18] = sz&0xff;
  dir[0x19] = sz>>8;
  dir[0x1a] = 1;
  memset(root, 0x55, 16);
  root[0x10] = 1;
  memcpy(root+0x30, dir+0x10, 8);
  root[0x44] = 255;
  root[0x46] = 254;
  root[0x48] = 1;
  root[0x4a] = 253;
  root[0x4c] = 13;
  root[0x50] = 200;
}

void check_gamesize()
{
  unsigned char *root, *fat, *dir;
  int i, fatblk, dirblk, dircnt;
  root = flash+255*512;
  fatblk = (root[0x47]<<8)|root[0x46];
  dirblk = (root[0x4b]<<8)|root[0x4a];
  dircnt = (root[0x4d]<<8)|root[0x4c];
  if(fatblk>=256 || dircnt>=256 || !dircnt)
    return;
  fat = flash+fatblk*512;
  while(dircnt-- && dirblk<256) {
    dir = flash+dirblk*512;
    dirblk = (fat[2*dirblk+1]<<8)|fat[2*dirblk];
    for(i=0; i<16; i++)
      if(dir[i*32] == 0xcc) {
	int sz = (dir[i*32+0x19]<<8)|dir[i*32+0x18];
	if(sz>1 && sz<=200) {
	  gamesize = sz*512;
	  return;
	}
      }
  }
}

int loadflash(char *filename)
{
  int r, fd;
  
  if((fd = open(filename, O_RDONLY|O_BINARY))>=0) {
    if(!(r=initflash(fd))) {
      close(fd);
      error_msg("%s: can't load FLASH image", filename);
      return 0;
    }
    close(fd);
    gamesize = r;
    if(r<241*512)
      fakeflash(filename, r);
    check_gamesize();
    return 1;
  } else {
    perror(filename);
    return 0;
  }
}
*/

void halt_mode()
{
  waitforevents(NULL);
  checkevents();
}

void lcdrefresh()
{
  static unsigned char icon[4][7] = {
    { 0x2a, 0x23, 0x47, 0xfb, 0x23, 0x23, 0x3f },
    { 0xff, 0x83, 0x93, 0xbb, 0x93, 0xbb, 0xff },
    { 0x7c, 0x82, 0x92, 0x9b, 0x82, 0x82, 0x7c },
    { 0x10, 0x28, 0xef, 0x6e, 0x6c, 0x7e, 0xc3 }
  };
  int y, x, i, a, b=0, p=0;

  p = sfr[0x22];
  if(p>=0x83)
    p -= 0x83;
  b = (p>>6);
  p = (p&0x3f)*2;
  for(y=0; y<32; y++) {
    for(x=0; x<48; ) {
      int value = xram[b][p++];
      if(!lcdon)
	value = 0;
      vmputpixel(x++, y, value>>7);
      vmputpixel(x++, y, value>>6);
      vmputpixel(x++, y, value>>5);
      vmputpixel(x++, y, value>>4);
      vmputpixel(x++, y, value>>3);
      vmputpixel(x++, y, value>>2);
      vmputpixel(x++, y, value>>1);
      vmputpixel(x++, y, value);
      if((p&0xf)>=12)
	p+=4;
      if(p>=128) {
	b++;
	p-=128;
      }
      if(b==2 && p>=6) {
	b = 0;
	p -= 6;
      }
    }
  }
  p++;
  if((p&0xf)>=12)
    p+=4;
  if(p>=128) {
    b++;
    p-=128;
  }
  if(b==2 && p>=6) {
    b = 0;
    p -= 6;
  }
  for(i=0; i<4; i++) {
    a = xram[b][p++]>>(2*(3-i));
    if(!lcdon)
      a = 0;
    for(y=0; y<7; y++) {
      int value = icon[i][y];
      for(x=0; x<7; x++)
	vmputpixel(2+i*12+x, y+33, (value>>(7-x))&a);
    }
    if((p&0xf)>=12)
      p+=4;
    if(p>=128) {
      b++;
      p-=128;
    }
    if(b==2 && p>=6) {
      b = 0;
      p -= 6;
    }
  }
  redrawlcd();
  lcd_updated = 0;
}

void writemem(int addr, int value)
{
  value &= 0xff;
  if(addr<0x100) {
    ram[(sfr[0x01]&2)>>1][addr] = value;
    return;
  }
  if(addr>=0x180) {
    int b = sfr[0x25];
    if(b>2 || (b==2 && addr>=0x186))
      return;
    xram[b][addr-0x180]=value;
    if(lcdon)
      lcd_updated = 1;
  } else switch(addr) {
   case 0x100:
     sfr[0x01] = (sfr[0x01]&0xfe)|parity[value];
     break;
   case 0x10d:
     if((value&1) != (sfr[0x0d]&1)) {
       if(pc>0xfffd || rom[pc]!=0x21)
	 error_msg("EXT 0 changed without following JMPF.  pc = %04x",
		   pc&0xffff);
       else
	 pc = (rom[pc+1]<<8)|rom[pc+2];
       // if(hasbios)
 	 // rom = ((value&1)? flash : bios);
     }
     break;
   case 0x10e:
     switch(value&0xa0) {
      case 0x00: spd = 3000; break;
      case 0x20: spd = 164; break;
      case 0x80: spd = 6000; break;
      case 0xa0: spd = 328; break;
     }
     break;
   case 0x110:
     if(!(value&0x40))
       t0l = sfr[0x13];
     if(!(value&0x80))
       t0h = sfr[0x15];
     break;
   case 0x111:
     t0scale = 256-value;
     t0base = 0;
     break;
   case 0x113:
     if(!(sfr[0x10]&0x40))
       t0l = value;
     break;
   case 0x115:
     if(!(sfr[0x10]&0x80))
       t0h = value;
     break;
   case 0x118:
     if(!(value&0x40))
       t1l = sfr[0x1b];
     if(!(value&0x80))
       t1h = sfr[0x1d];
     break;
   case 0x11b:
     if(!(sfr[0x18]&0x40))
       t1l = value;
     break;
   case 0x11d:
     if(!(sfr[0x18]&0x80))
       t1h = value;
     break;
   case 0x122:
     if(lcdon)
       lcd_updated = 1;
     break;
   case 0x127:
     if((!!(value&0x80)) != lcdon) {
       lcdon = !!(value&0x80);
       lcdrefresh();
     }
     break;
   case 0x166:
     wram[0x1ff&((sfr[0x65]<<8)|sfr[0x64])] = value;
     if(sfr[0x63]&0x10)
       if(!++sfr[0x64])
	 sfr[0x65]^=1;
     return;
  }
  /*
    if(addr>0x10e && addr<0x120 && addr != 0x118)
      fprintf(stderr, "%04x: Write to %03x: %02x\n", pc, addr, value);
  */
  sfr[addr&0xff] = value;
  if(addr == 0x118 || addr == 0x11b) {
    /* Check for sound... */
    if(sfr[0x18]&0x40)
      sound(32768/((256-sfr[0x1b])*6));
    else
      sound(-1);
  }
}

int readmem(int addr)
{
  int r;
  if(addr<0x100)
    return ram[(sfr[0x01]&2)>>1][addr];
  if(addr>=0x180) {
    int b = sfr[0x25];
    if(b>2)
      return 0xff;
    return xram[b][addr-0x180];
  } else switch(addr) {
   case 0x112:
     return t0l;
   case 0x114:
     return t0h;
   case 0x11b:
     return t1l;
   case 0x11d:
     return t1h;
   case 0x15c:
     return 2;
   case 0x165:
     return 0xfe|(sfr[0x65]&1);
   case 0x166:
     r = wram[0x1ff&((sfr[0x65]<<8)|sfr[0x64])];
     if(sfr[0x63]&0x10)
       if(!++sfr[0x64])
	 sfr[0x65]^=1;
     return r;
  }
  /*
  if(addr>0x106 && addr<0x180)
    fprintf(stderr, "%04x: Read from %03x: %02x\n", pc, addr, sfr[addr&0xff]);
  */
  return sfr[addr&0xff];
}

int readlatch(int addr)
{
  switch(addr) {
   case 0x11b:
   case 0x11d:
     return 0xff;
   default:
     return readmem(addr);
  }
}

void push(int n)
{
  writemem(0x106, readmem(0x106)+1);
  ram[0][readmem(0x106)]=n;
}

int pop()
{
  int r = ram[0][readmem(0x106)];
  writemem(0x106, readmem(0x106)-1);
  return r;
}

void resetcpu()
{
  int i;
  time_t t = time(NULL);
  struct tm *tm = localtime(&t);
  memset(ram, 0, sizeof(ram));
  memset(xram, 0, sizeof(xram));
  memset(wram, 0, sizeof(wram));
  memset(sfr, 0, sizeof(sfr));
  parity[0] = 0;
  parity[1] = 1;
  parity[2] = 1;
  parity[3] = 0;
  for(i=4; i<16; i++)
    parity[i] = parity[i&3]^parity[i>>2];
  for(i=16; i<255; i++)
    parity[i] = parity[i&15]^parity[i>>4];  
  sfr[0x06] = 0x7f;
  sfr[0x4c] = 0xff;
  sfr[0x01] = 0x02;
  t0h = t0l = 0;
  t1h = t1l = 0;
  t0base = 0;
  t0scale = 256;
  ram[0][0x10] = tobcd(tm->tm_year/100+19);
  ram[0][0x11] = tobcd(tm->tm_year%100);
  ram[0][0x12] = tobcd(tm->tm_mon+1);
  ram[0][0x13] = tobcd(tm->tm_mday);
  ram[0][0x14] = tobcd(tm->tm_hour);
  ram[0][0x15] = tobcd(tm->tm_min);
  ram[0][0x17] = (tm->tm_year+1900)>>8;
  ram[0][0x18] = (tm->tm_year+1900)&0xff;
  ram[0][0x19] = tm->tm_mon+1;
  ram[0][0x1a] = tm->tm_mday;
  ram[0][0x1b] = tm->tm_hour;
  ram[0][0x1c] = tm->tm_min;
  ram[0][0x1d] = tm->tm_sec;
  ram[0][0x31] = 0xff;
  sfr[0x08] = 0x80;
  writemem(0x10e, 0x81);
  lcd_updated = 0;
  lcdon = 0;
  imask = 0;
  intreq = 0;
  if(hasbios) {
    sfr[0x0d] = 0;
    rom = bios;
    pc = 0x1f0;
  } else {
    sfr[0x0d] = 1;
    rom = flash;
    pc = 0;
    writemem(0x125, 2);
    writemem(0x182, 0x10);
    writemem(0x125, 0);
    writemem(0x127, 0x80);
  }
  sound(-1);
}

int month_days()
{
  int m = ram[0][0x19];
  if(m==2) {
    int y = ram[0][0x18] | (ram[0][0x17] << 8);
    if(y&3)
      return 28;
    if(!(y%4000))
      return 29;
    if(!(y%1000))
      return 28;
    if(!(y%400))
      return 29;
    if(!(y%100))
      return 28;
    return 29;
  } else return (m>7? ((m&1)? 30:31) : ((m&1)? 31:30));
}

int handle_fwcall(int pc)
{
  switch(pc) {
   case 0x100:
     {
//       int i, a = ((ram[1][0x7d]<<16)|(ram[1][0x7e]<<8)|ram[1][0x7f])&0x1ffff;
//       if(a>=gamesize)
//	 writemem(0x100, 0xff);
//       else {
//	 writemem(0x100, 0x00);
	 //for(i=0; i<0x80; i++)
	 //  flash[(a&~0xff)|((a+i)&0xff)] = ram[1][i+0x80];
//#ifdef __DC__
	 //if(!flash_written(a))
	   writemem(0x100, 0xff);
//#endif
//       }
       /*
       fprintf(stderr, "ROM write @ %05x:\n", a);
       for(i=0; i<0x80; i++)
	 fprintf(stderr, " %02x", ram[1][i+0x80]);
       fprintf(stderr, "\n");
       */
     }
     return 0x105;
   case 0x110:
     {
       int i, a = ((ram[1][0x7d]<<16)|(ram[1][0x7e]<<8)|ram[1][0x7f])&0x1ffff;
       int r = 0;
       for(i=0; i<0x80; i++)
	 if((r = (flash[(a&~0xff)|((a+i)&0xff)] ^ ram[1][i+0x80])) != 0)
	   break;
       writemem(0x100, r);
     }
     return 0x115;
   case 0x120:
     {
       int i, a = ((ram[1][0x7d]<<16)|(ram[1][0x7e]<<8)|ram[1][0x7f])&0x1ffff;
       for(i=0; i<0x80; i++)
	 ram[1][i+0x80] = flash[(a&~0xff)|((a+i)&0xff)];
       /*
       fprintf(stderr, "ROM read @ %05x\n", a);
       */
     }
     return 0x125;
   case 0x130:
     if(!((ram[0][0x1e]^=1)&1))
       if(++ram[0][0x1d]>=60) {
	 ram[0][0x1d] = 0;
	 if(++ram[0][0x1c]>=60) {
	   ram[0][0x1c] = 0;
	   if(++ram[0][0x1b]>=24) {
	     ram[0][0x1b] = 0;
	     if(++ram[0][0x1a]>month_days()) {
	       ram[0][0x1a] = 1;
	       if(++ram[0][0x19]>=13) {
		 ram[0][0x19] = 1;
		 if(ram[0][0x18]==0xff) {
		   ram[0][0x18]=0;
		   ram[0][0x17]++;
		 } else
		   ram[0][0x18]++;
	       }
	     }
	   }
	 }
       }
     return 0x139;
   case 0x1f0:
     return 0;
   default:
     error_msg("Firmware entered at unknown vector %04x!", pc);
     return 0;
  }
}

void run_cpu( void )
{
  struct timeval epoch;
  int mcy = 0, tick = 0;

  GETTIMEOFDAY(&epoch);
  for(;;) {
    int r, s, c, cy = 1, i = rom[pc];
#ifdef TRACE
    printf("%04x\n", pc);
#endif
    pc++;
    pc &= 0xffff;
    switch(i&0xf) {
     case 0:
       switch(i>>4) {
	case 0:
	  break;
	case 1:
	  cy = 4;
	  push((pc+2)&0xff);
	  push(((pc+2)&0xff00)>>8);
	  pc = 0xffff&(pc+1+rom[pc]+((rom[(pc+1)&0xffff])<<8));
	  break;
	case 2:
	  cy = 2;
	  push((pc+2)&0xff);
	  push(((pc+2)&0xff00)>>8);
	  pc = (rom[pc]<<8)|rom[(pc+1)&0xffff];
	  break;
	case 3:
	  cy = 7;
	  r = readmem(0x102)*((readmem(0x100)<<8)|readmem(0x103));
	  writemem(0x101, (readmem(0x101)&0x7b)|(r>65535? 4:0));
	  writemem(0x103, r&0xff);
	  writemem(0x100, (r&0xff00)>>8);
	  writemem(0x102, (r&0xff0000)>>16);
	  break;
	case 4:
	  cy = 7;
	  r = readmem(0x102);
	  if(r) {
	    int v = (readmem(0x100)<<8)|readmem(0x103);
	    s = v%r;
	    r = v/r;
	  } else {
	    r = 0xff00|readmem(0x103);
	    s = 0;
	  }
	  writemem(0x101, (readmem(0x101)&0x7b)|(s? 0:4));
	  writemem(0x103, r&0xff);
	  writemem(0x100, (r&0xff00)>>8);
	  writemem(0x102, s);
	  break;
	case 5:
	  cy = 2; /* ? */
	  writemem(0x100, flash[0x1ffff&(readmem(0x104)+(readmem(0x105)<<8)+
					 (readmem(0x154)<<16))]);
	  break;
	case 6:
	  cy = 2;
	  push(readmem(rom[pc++]));
	  pc &= 0xffff;
	  break;
	case 7:
	  cy = 2;
	  writemem(rom[pc++], pop());
	  pc &= 0xffff;
	  break;
	case 8:
	  cy = 2;
	  if(readmem(0x100)==0)
	    pc = 0xffff&(pc+1+SGNEXT(rom[pc]));
	  else {
	    pc++;
	    pc &= 0xffff;
	  }
	  break;
	case 9:
	  cy = 2;
	  if(readmem(0x100)!=0)
	    pc = 0xffff&(pc+1+SGNEXT(rom[pc]));
	  else {
	    pc++;
	    pc &= 0xffff;
	  }
	  break;
	case 0xa:
	  cy = 2;
	  r = pop()<<8;
	  r |= pop();
	  pc = r;
	  break;
	case 0xb:
	  cy = 2;
	  r = pop()<<8;
	  r |= pop();
	  pc = r;
	  --imask;
	  break;
	case 0xc:
	  r = readmem(0x100);
	  writemem(0x100, (r>>1)|((r&1)<<7));
	  break;
	case 0xd:
	  r = readmem(0x100);
	  s = readmem(0x101);
	  writemem(0x101, (s&0x7f)|((r&1)<<7));
	  writemem(0x100, (r>>1)|(s&0x80));
	  break;
	case 0xe:
	  r = readmem(0x100);
	  writemem(0x100, (r<<1)|((r&0x80)>>7));
	  break;
	case 0xf:
	  r = readmem(0x100);
	  s = readmem(0x101);
	  writemem(0x101, (s&0x7f)|(r&0x80));
	  writemem(0x100, (r<<1)|((s&0x80)>>7));
	  break;
       }
       break;
     case 1:
       switch(i>>4) {
	case 0:
	  cy = 2;
	  pc = 0xffff&(pc+1+SGNEXT(rom[pc]));
	  break;
	case 1:
	  cy = 4;
	  pc = 0xffff&(pc+1+rom[pc]+((rom[(pc+1)&0xffff])<<8));
	  break;
	case 2:
	  cy = 2;
	  pc = (rom[pc]<<8)|rom[(pc+1)&0xffff];
	  break;
	case 3:
	  cy = 2;
	  r = readmem(0x100);
	  writemem(0x101, (readmem(0x101)&0x7f)|(r<rom[pc]? 0x80:0));
	  s = (r == rom[pc++]);
	  pc &= 0xffff;
	  if(s)
	    pc = 0xffff&(pc+1+SGNEXT(rom[pc]));
	  else {
	    pc++;
	    pc &= 0xffff;
	  }
	  break;
	case 4:
	  cy = 2;
	  r = readmem(0x100);
	  writemem(0x101, (readmem(0x101)&0x7f)|(r<rom[pc]? 0x80:0));
	  s = (r != rom[pc++]);
	  pc &= 0xffff;
	  if(s)
	    pc = 0xffff&(pc+1+SGNEXT(rom[pc]));
	  else {
	    pc++;
	    pc &= 0xffff;
	  }
	  break;
	case 5:
	  cy = 2; /* ? * /
	  if(!(readmem(0x154)&2))
	    flash[0x1ffff&(readmem(0x104)+(readmem(0x105)<<8)+
			   (readmem(0x154)<<16))] = readmem(0x100);
			   /* */
	  break;
	case 6:
	  cy = 2;
	  push(readmem(0x100|rom[pc++]));
	  pc &= 0xffff;
	  break;
	case 7:
	  cy = 2;
	  writemem(0x100|rom[pc++], pop());
	  pc &= 0xffff;
	  break;
	case 8:
	  r = readmem(0x100);
	  s = rom[pc++];
	  pc &= 0xffff;
	  writemem(0x100, r+s);
	  writemem(0x101, (readmem(0x101)&0x3b)|(r+s>255? 0x80:0)|
		   ((r&15)+(s&15)>15? 0x40:0)|((0x80&(~r^s)&(s^(r+s)))? 4:0));
	  break;
	case 9:
	  r = readmem(0x100);
	  s = rom[pc++];
	  pc &= 0xffff;
	  c = (readmem(0x101)&0x80)>>7;
	  writemem(0x100, r+s+c);
	  writemem(0x101, (readmem(0x101)&0x3b)|(r+s+c>255? 0x80:0)|
		   ((r&15)+(s&15)+c>15? 0x40:0)|
		   ((0x80&(~r^s)&(s^(r+s+c)))? 4:0));
	  break;
	case 0xa:
	  /* FIXME: OV */
	  r = readmem(0x100);
	  s = rom[pc++];
	  pc &= 0xffff;
	  writemem(0x100, r-s);
	  writemem(0x101, (readmem(0x101)&0x3b)|(r-s<0? 0x80:0)|
		   ((r&15)-(s&15)<0? 0x40:0)|(0? 4:0));
	  break;
	case 0xb:
	  /* FIXME: OV */
	  r = readmem(0x100);
	  s = rom[pc++];
	  pc &= 0xffff;
	  c = (readmem(0x101)&0x80)>>7;
	  writemem(0x100, r-s-c);
	  writemem(0x101, (readmem(0x101)&0x3b)|(r-s-c<0? 0x80:0)|
		   ((r&15)-(s&15)-c<0? 0x40:0)|(0? 4:0));
	  break;
	case 0xc:
	  cy = 2;
	  writemem(0x100, rom[0xffff&(readmem(0x100)+readmem(0x104)+
				      (readmem(0x105)<<8))]);
	  break;
	case 0xd:
	  writemem(0x100, readmem(0x100)|rom[pc++]);
	  pc &= 0xffff;
	  break;
	case 0xe:
	  writemem(0x100, readmem(0x100)&rom[pc++]);
	  pc &= 0xffff;
	  break;
	case 0xf:
	  writemem(0x100, readmem(0x100)^rom[pc++]);
	  pc &= 0xffff;
	  break;
       }
       break;
     case 2:
     case 3:
       r = ((i&1)<<8)|rom[pc++];
       pc &= 0xffff;
       switch(i>>4) {
	case 0:
	  writemem(0x100, readmem(r));
	  break;
	case 1:
	  writemem(r, readmem(0x100));
	  break;
	case 2:
	  cy = 2;
	  writemem(r, rom[pc++]);
	  pc &= 0xffff;
	  break;
	case 3:
	  cy = 2;
	  s = readmem(r);
	  r = readmem(0x100);
	  writemem(0x101, (readmem(0x101)&0x7f)|(r<s? 0x80:0));
	  if(r == s)
	    pc = 0xffff&(pc+1+SGNEXT(rom[pc]));
	  else {
	    pc++;
	    pc &= 0xffff;
	  }
	  break;
	case 4:
	  cy = 2;
	  s = readmem(r);
	  r = readmem(0x100);
	  writemem(0x101, (readmem(0x101)&0x7f)|(r<s? 0x80:0));
	  if(r != s)
	    pc = 0xffff&(pc+1+SGNEXT(rom[pc]));
	  else {
	    pc++;
	    pc &= 0xffff;
	  }
	  break;
	case 5:
	  cy = 2;
	  s = (readlatch(r)-1)&0xff;
	  writemem(r, s);
	  if(s != 0)
	    pc = 0xffff&(pc+1+SGNEXT(rom[pc]));
	  else {
	    pc++;
	    pc &= 0xffff;
	  }
	  break;
	case 6:
	  writemem(r, readlatch(r)+1);
	  break;
	case 7:
	  writemem(r, readlatch(r)-1);
	  break;
	case 8:
	  s = readmem(r);
	  r = readmem(0x100);
	  writemem(0x100, r+s);
	  writemem(0x101, (readmem(0x101)&0x3b)|(r+s>255? 0x80:0)|
		   ((r&15)+(s&15)>15? 0x40:0)|((0x80&(~r^s)&(s^(r+s)))? 4:0));
	  break;
	case 9:
	  s = readmem(r);
	  r = readmem(0x100);
	  c = (readmem(0x101)&0x80)>>7;
	  writemem(0x100, r+s+c);
	  writemem(0x101, (readmem(0x101)&0x3b)|(r+s+c>255? 0x80:0)|
		   ((r&15)+(s&15)+c>15? 0x40:0)|
		   ((0x80&(~r^s)&(s^(r+s+c)))? 4:0));
	  break;
	case 0xa:
	  /* FIXME: OV */
	  s = readmem(r);
	  r = readmem(0x100);
	  writemem(0x100, r-s);
	  writemem(0x101, (readmem(0x101)&0x3b)|(r-s<0? 0x80:0)|
		   ((r&15)-(s&15)<0? 0x40:0)|(0? 4:0));
	  break;
	case 0xb:
	  /* FIXME: OV */
	  s = readmem(r);
	  r = readmem(0x100);
	  c = (readmem(0x101)&0x80)>>7;
	  writemem(0x100, r-s-c);
	  writemem(0x101, (readmem(0x101)&0x3b)|(r-s-c<0? 0x80:0)|
		   ((r&15)-(s&15)-c<0? 0x40:0)|(0? 4:0));
	  break;
	case 0xc:
	  s = readmem(r);
	  writemem(r, readmem(0x100));
	  writemem(0x100, s);
	  break;
	case 0xd:
	  writemem(0x100, readmem(0x100)|readmem(r));
	  break;
	case 0xe:
	  writemem(0x100, readmem(0x100)&readmem(r));
	  break;
	case 0xf:
	  writemem(0x100, readmem(0x100)^readmem(r));
	  break;
       }
       break;
     case 4:
     case 5:
     case 6:
     case 7:
       r = readmem((i&3)|((readmem(0x101)>>1)&0xc))|((i&2)? 0x100 : 0);
       switch(i>>4) {
	case 0:
	  writemem(0x100, readmem(r));
	  break;
	case 1:
	  writemem(r, readmem(0x100));
	  break;
	case 2:
	  writemem(r, rom[pc++]);
	  pc &= 0xffff;
	  break;
	case 3:
	  cy = 2;
	  s = readmem(r);
	  r = rom[pc++];
	  pc &= 0xffff;
	  writemem(0x101, (readmem(0x101)&0x7f)|(s<r? 0x80:0));
	  if(r == s)
	    pc = 0xffff&(pc+1+SGNEXT(rom[pc]));
	  else {
	    pc++;
	    pc &= 0xffff;
	  }
	  break;
	case 4:
	  cy = 2;
	  s = readmem(r);
	  r = rom[pc++];
	  pc &= 0xffff;
	  writemem(0x101, (readmem(0x101)&0x7f)|(s<r? 0x80:0));
	  if(r != s)
	    pc = 0xffff&(pc+1+SGNEXT(rom[pc]));
	  else {
	    pc++;
	    pc &= 0xffff;
	  }
	  break;
	case 5:
	  cy = 2;
	  s = (readlatch(r)-1)&0xff;
	  writemem(r, s);
	  if(s != 0)
	    pc = 0xffff&(pc+1+SGNEXT(rom[pc]));
	  else {
	    pc++;
	    pc &= 0xffff;
	  }
	  break;
	case 6:
	  writemem(r, readlatch(r)+1);
	  break;
	case 7:
	  writemem(r, readlatch(r)-1);
	  break;
	case 8:
	  s = readmem(r);
	  r = readmem(0x100);
	  writemem(0x100, r+s);
	  writemem(0x101, (readmem(0x101)&0x3b)|(r+s>255? 0x80:0)|
		   ((r&15)+(s&15)>15? 0x40:0)|((0x80&(~r^s)&(s^(r+s)))? 4:0));
	  break;
	case 9:
	  s = readmem(r);
	  r = readmem(0x100);
	  c = (readmem(0x101)&0x80)>>7;
	  writemem(0x100, r+s+c);
	  writemem(0x101, (readmem(0x101)&0x3b)|(r+s+c>255? 0x80:0)|
		   ((r&15)+(s&15)+c>15? 0x40:0)|
		   ((0x80&(~r^s)&(s^(r+s+c)))? 4:0));
	  break;
	case 0xa:
	  /* FIXME: OV */
	  s = readmem(r);
	  r = readmem(0x100);
	  writemem(0x100, r-s);
	  writemem(0x101, (readmem(0x101)&0x3b)|(r-s<0? 0x80:0)|
		   ((r&15)-(s&15)<0? 0x40:0)|(0? 4:0));
	  break;
	case 0xb:
	  /* FIXME: OV */
	  s = readmem(r);
	  r = readmem(0x100);
	  c = (readmem(0x101)&0x80)>>7;
	  writemem(0x100, r-s-c);
	  writemem(0x101, (readmem(0x101)&0x3b)|(r-s-c<0? 0x80:0)|
		   ((r&15)-(s&15)-c<0? 0x40:0)|(0? 4:0));
	  break;
	case 0xc:
	  s = readmem(r);
	  writemem(r, readmem(0x100));
	  writemem(0x100, s);
	  break;
	case 0xd:
	  writemem(0x100, readmem(0x100)|readmem(r));
	  break;
	case 0xe:
	  writemem(0x100, readmem(0x100)&readmem(r));
	  break;
	case 0xf:
	  writemem(0x100, readmem(0x100)^readmem(r));
	  break;
       }
       break;
     default:
       switch(i&0xe0) {
	case 0x00:
	  cy = 2;
	  r = ((i&7)<<8)|((i&0x10)<<7)|rom[pc++];
	  push(pc&0xff);
	  push((pc&0xff00)>>8);
	  pc = (pc&0xf000)|r;
	  break;
	case 0x20:
	  cy = 2;
	  r = ((i&7)<<8)|((i&0x10)<<7)|rom[pc++];
	  pc = (pc&0xf000)|r;
	  break;
	case 0x40:
	  cy = 2;
	  r = ((i&0x10)<<4)|rom[pc++];
	  pc &= 0xffff;
	  if((s=readmem(r))&(1<<(i&7))) {
	    writemem(r, s & ~(1<<(i&7)));
	    pc = 0xffff&(pc+1+SGNEXT(rom[pc]));
	  } else {
	    pc++;
	    pc &= 0xffff;
	  }
	  break;
	case 0x60:
	  cy = 2;
	  r = ((i&0x10)<<4)|rom[pc++];
	  pc &= 0xffff;
	  if(readmem(r)&(1<<(i&7)))
	    pc = 0xffff&(pc+1+SGNEXT(rom[pc]));
	  else {
	    pc++;
	    pc &= 0xffff;
	  }
	  break;
	case 0x80:
	  cy = 2;
	  r = ((i&0x10)<<4)|rom[pc++];
	  pc &= 0xffff;
	  if(!(readmem(r)&(1<<(i&7))))
	    pc = 0xffff&(pc+1+SGNEXT(rom[pc]));
	  else {
	    pc++;
	    pc &= 0xffff;
	  }
	  break;
	case 0xa0:
	  r = ((i&0x10)<<4)|rom[pc++];
	  pc &= 0xffff;
	  writemem(r, readlatch(r) ^ (1<<(i&7)));
	  break;
	case 0xc0:
	  r = ((i&0x10)<<4)|rom[pc++];
	  pc &= 0xffff;
	  writemem(r, readlatch(r) & ~(1<<(i&7)));
	  break;
	case 0xe0:
	  r = ((i&0x10)<<4)|rom[pc++];
	  pc &= 0xffff;
	  writemem(r, readlatch(r) | (1<<(i&7)));
	  break;
       }
       break;
    }
    mcy += cy;
    if(mcy>=spd) {
      struct timeval now, t;
      if(lcd_updated)
	lcdrefresh();
      GETTIMEOFDAY(&now);
      if((epoch.tv_usec += 10000)>=1000000) {
	epoch.tv_usec -= 1000000;
	epoch.tv_sec++;
      }
      if(now.tv_sec>epoch.tv_sec ||
	 (now.tv_sec == epoch.tv_sec && now.tv_usec >= epoch.tv_usec)) {
	t.tv_usec = 0;
	t.tv_sec = 0;
      } else if(epoch.tv_usec<now.tv_usec) {
	t.tv_usec = 1000000 + epoch.tv_usec - now.tv_usec;
	t.tv_sec = epoch.tv_sec - now.tv_sec - 1;
      } else {
	t.tv_usec = epoch.tv_usec - now.tv_usec;
	t.tv_sec = epoch.tv_sec - now.tv_sec;
      }
      waitforevents(&t);
      checkevents();
      mcy -= spd;
      ++tick;
      if(tick>=50) {
	intreq |= 1<<3;
	tick -= 50;
      }
    }
    /* Timer 0 */
    if(sfr[0x10] & 0xc0) {
      int c0=0;
      if((t0base+=cy) >= t0scale)
	do
	  c0++;
	while((t0base-=t0scale) >= t0scale);
      if(c0)
	if((sfr[0x10] & 0xe0) == 0xe0) {
	  t0l += c0;
	  if(t0l>=256) {
	    t0l -= 256;
	    if(++t0h >= 256) {
	      t0h -= 256;
	      if((t0l += sfr[0x13])>=256) {
		t0l -= 256;
		if((t0h += sfr[0x15])>=256) {
		  t0l = sfr[0x13];
		  t0h = sfr[0x15];
		}
	      }
	      sfr[0x10] |= 10;
	      if(sfr[0x10]&4)
		intreq |= 1<<4;
	    }
	  }
	} else {
	  if(sfr[0x10] & 0x40) {
	    t0l += c0;
	    if(t0l>=256) {
	      t0l -= 256;
	      if((t0l += sfr[0x13])>=256)
		t0l = sfr[0x13];
	      sfr[0x10] |= 2;
	      if(sfr[0x10]&1)
		intreq |= 1<<2;
	    }
	  }
	  if(sfr[0x10] & 0x80) {
	    t0h += c0;
	    if(t0h>=256) {
	      t0h -= 256;
	      if((t0h += sfr[0x15])>=256)
		t0h = sfr[0x15];
	      sfr[0x10] |= 8;
	      if(sfr[0x10]&4)
		intreq |= 1<<4;
	    }
	  }
	}
    }
    /* Timer 1 */
    if(sfr[0x18] & 0xc0) {
      if((sfr[0x18] & 0xe0) == 0xe0) {
	t1l += cy;
	if(t1l>=256) {
	  t1l -= 256;
	  if(++t1h >= 256) {
	    t1h -= 256;
	    if((t1l += sfr[0x1b])>=256) {
	      t1l -= 256;
	      if((t1h += sfr[0x1d])>=256) {
		t1l = sfr[0x1b];
		t1h = sfr[0x1d];
	      }
	    }
	    sfr[0x18] |= 10;
	    if(sfr[0x18]&4)
	      intreq |= 1<<5;
	  }
	}
      } else {
	if(sfr[0x18] & 0x40) {
	  t1l += cy;
	  if(t1l>=256) {
	    t1l -= 256;
	    if((t1l += sfr[0x1b])>=256)
	      t1l = sfr[0x1b];
	    sfr[0x18] |= 2;
	    if(sfr[0x18]&1)
	      intreq |= 1<<5;
	  }
	}
	if(sfr[0x18] & 0x80) {
	  t1h += cy;
	  if(t1h>=256) {
	    t1h -= 256;
	    if((t1h += sfr[0x1d])>=256)
	      t1h = sfr[0x1d];
	    sfr[0x18] |= 8;
	    if(sfr[0x18]&4)
	      intreq |= 1<<5;
	  }
	}
      }
    }
    if(!(sfr[0x0d]&1) && !hasbios)
      if(!(pc=handle_fwcall(pc)))
	break;
      else
	sfr[0x0d]|=1;

    if((sfr[0x4e]&3)==3 && !imask)
      intreq |= 1<<9;

    if(!intreq || imask || !(sfr[0x08]&0x80))
      continue;
    for(r=0; r<10; r++)
      if(intreq & (1<<r))
	break;
    intreq &= ~(1<<r);
    push(pc&0xff);
    push((pc&0xff00)>>8);
    imask++;
    pc = ((r&~1)<<3)+((r&1)?0xb:0x3);
  }
}

/*
int do_vmsgame(char *filename, char *biosname)
{
  if(filename == NULL && biosname == NULL)
    return 0;
  if(biosname != NULL)
    if(!loadbios(biosname))
      return 0;
  if(filename != NULL)
    if(!loadflash(filename))
      return 0;
  resetcpu();
  run_cpu();
  return 1;
}
*/