master uxn.c
   1#if 0
   2gcc -s -O2 -Wno-unused-result -fwrapv main.c `sdl-config --cflags --libs`
   3exit
   4#endif
   5
   6// This file is public domain.
   7
   8#include "SDL/SDL.h"
   9#include <dirent.h>
  10#include <fcntl.h>
  11#include <stdio.h>
  12#include <stdlib.h>
  13#include <string.h>
  14#include <sys/stat.h>
  15#include <sys/types.h>
  16#include <time.h>
  17#include <unistd.h>
  18#include <stdio.h>
  19#include <stdlib.h>
  20#include <string.h>
  21#include <sys/types.h>
  22
  23static ssize_t getline(char **line, size_t *cap, FILE *fp)
  24{
  25    if (!line || !cap || !fp)
  26        return -1;
  27
  28    size_t len = 0;
  29    int c;
  30
  31    if (*line == NULL || *cap == 0) {
  32        *cap = 128;
  33        *line = malloc(*cap);
  34        if (!*line) {
  35            *cap = 0;
  36            return -1;
  37        }
  38    }
  39
  40    while ((c = fgetc(fp)) != EOF) {
  41        if (len + 1 >= *cap) {
  42            size_t newcap = *cap * 2;
  43            char *tmp = realloc(*line, newcap);
  44            if (!tmp)
  45                return -1;
  46            *line = tmp;
  47            *cap = newcap;
  48        }
  49
  50        (*line)[len++] = (char)c;
  51
  52        if (c == '\n')
  53            break;
  54    }
  55
  56    if (len == 0 && c == EOF)
  57        return -1;
  58
  59    (*line)[len] = '\0';
  60    return (ssize_t)len;
  61}
  62
  63void warn(const char *fmt, ...)
  64{
  65    va_list args;
  66    va_start(args, fmt);
  67
  68    vprintf(fmt, args);
  69    printf("\n");
  70
  71    va_end(args);
  72}
  73
  74static void err(int e, char* str){
  75  show_msgbox("Error", str);
  76  exit(e);
  77}
  78
  79static const Uint8 fontdata[]={ // font for built-in menus
  80  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x30,0x78,0x78,0x30,0x30,0x00,0x30,0x00,
  81  0x6C,0x6C,0x6C,0x00,0x00,0x00,0x00,0x00,0x6C,0x6C,0xFE,0x6C,0xFE,0x6C,0x6C,0x00,
  82  0x30,0x7C,0xC0,0x78,0x0C,0xF8,0x30,0x00,0x00,0xC6,0xCC,0x18,0x30,0x66,0xC6,0x00,
  83  0x38,0x6C,0x38,0x76,0xDC,0xCC,0x76,0x00,0x60,0x60,0xC0,0x00,0x00,0x00,0x00,0x00,
  84  0x18,0x30,0x60,0x60,0x60,0x30,0x18,0x00,0x60,0x30,0x18,0x18,0x18,0x30,0x60,0x00,
  85  0x00,0x66,0x3C,0xFF,0x3C,0x66,0x00,0x00,0x00,0x30,0x30,0xFC,0x30,0x30,0x00,0x00,
  86  0x00,0x00,0x00,0x00,0x00,0x30,0x30,0x60,0x00,0x00,0x00,0xFC,0x00,0x00,0x00,0x00,
  87  0x00,0x00,0x00,0x00,0x00,0x30,0x30,0x00,0x06,0x0C,0x18,0x30,0x60,0xC0,0x80,0x00,
  88  0x7C,0xC6,0xCE,0xDE,0xF6,0xE6,0x7C,0x00,0x30,0x70,0x30,0x30,0x30,0x30,0xFC,0x00,
  89  0x78,0xCC,0x0C,0x38,0x60,0xCC,0xFC,0x00,0x78,0xCC,0x0C,0x38,0x0C,0xCC,0x78,0x00,
  90  0x1C,0x3C,0x6C,0xCC,0xFE,0x0C,0x1E,0x00,0xFC,0xC0,0xF8,0x0C,0x0C,0xCC,0x78,0x00,
  91  0x38,0x60,0xC0,0xF8,0xCC,0xCC,0x78,0x00,0xFC,0xCC,0x0C,0x18,0x30,0x30,0x30,0x00,
  92  0x78,0xCC,0xCC,0x78,0xCC,0xCC,0x78,0x00,0x78,0xCC,0xCC,0x7C,0x0C,0x18,0x70,0x00,
  93  0x00,0x30,0x30,0x00,0x00,0x30,0x30,0x00,0x00,0x30,0x30,0x00,0x00,0x30,0x30,0x60,
  94  0x18,0x30,0x60,0xC0,0x60,0x30,0x18,0x00,0x00,0x00,0xFC,0x00,0x00,0xFC,0x00,0x00,
  95  0x60,0x30,0x18,0x0C,0x18,0x30,0x60,0x00,0x78,0xCC,0x0C,0x18,0x30,0x00,0x30,0x00,
  96  0x7C,0xC6,0xDE,0xDE,0xDE,0xC0,0x78,0x00,0x30,0x78,0xCC,0xCC,0xFC,0xCC,0xCC,0x00,
  97  0xFC,0x66,0x66,0x7C,0x66,0x66,0xFC,0x00,0x3C,0x66,0xC0,0xC0,0xC0,0x66,0x3C,0x00,
  98  0xF8,0x6C,0x66,0x66,0x66,0x6C,0xF8,0x00,0xFE,0x62,0x68,0x78,0x68,0x62,0xFE,0x00,
  99  0xFE,0x62,0x68,0x78,0x68,0x60,0xF0,0x00,0x3C,0x66,0xC0,0xC0,0xCE,0x66,0x3E,0x00,
 100  0xCC,0xCC,0xCC,0xFC,0xCC,0xCC,0xCC,0x00,0x78,0x30,0x30,0x30,0x30,0x30,0x78,0x00,
 101  0x1E,0x0C,0x0C,0x0C,0xCC,0xCC,0x78,0x00,0xE6,0x66,0x6C,0x78,0x6C,0x66,0xE6,0x00,
 102  0xF0,0x60,0x60,0x60,0x62,0x66,0xFE,0x00,0xC6,0xEE,0xFE,0xFE,0xD6,0xC6,0xC6,0x00,
 103  0xC6,0xE6,0xF6,0xDE,0xCE,0xC6,0xC6,0x00,0x38,0x6C,0xC6,0xC6,0xC6,0x6C,0x38,0x00,
 104  0xFC,0x66,0x66,0x7C,0x60,0x60,0xF0,0x00,0x78,0xCC,0xCC,0xCC,0xDC,0x78,0x1C,0x00,
 105  0xFC,0x66,0x66,0x7C,0x6C,0x66,0xE6,0x00,0x78,0xCC,0xE0,0x70,0x1C,0xCC,0x78,0x00,
 106  0xFC,0xB4,0x30,0x30,0x30,0x30,0x78,0x00,0xCC,0xCC,0xCC,0xCC,0xCC,0xCC,0xFC,0x00,
 107  0xCC,0xCC,0xCC,0xCC,0xCC,0x78,0x30,0x00,0xC6,0xC6,0xC6,0xD6,0xFE,0xEE,0xC6,0x00,
 108  0xC6,0xC6,0x6C,0x38,0x38,0x6C,0xC6,0x00,0xCC,0xCC,0xCC,0x78,0x30,0x30,0x78,0x00,
 109  0xFE,0xC6,0x8C,0x18,0x32,0x66,0xFE,0x00,0x78,0x60,0x60,0x60,0x60,0x60,0x78,0x00,
 110  0xC0,0x60,0x30,0x18,0x0C,0x06,0x02,0x00,0x78,0x18,0x18,0x18,0x18,0x18,0x78,0x00,
 111  0x10,0x38,0x6C,0xC6,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,
 112  0x30,0x30,0x18,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x78,0x0C,0x7C,0xCC,0x76,0x00,
 113  0xE0,0x60,0x60,0x7C,0x66,0x66,0xDC,0x00,0x00,0x00,0x78,0xCC,0xC0,0xCC,0x78,0x00,
 114  0x1C,0x0C,0x0C,0x7C,0xCC,0xCC,0x76,0x00,0x00,0x00,0x78,0xCC,0xFC,0xC0,0x78,0x00,
 115  0x38,0x6C,0x60,0xF0,0x60,0x60,0xF0,0x00,0x00,0x00,0x76,0xCC,0xCC,0x7C,0x0C,0xF8,
 116  0xE0,0x60,0x6C,0x76,0x66,0x66,0xE6,0x00,0x30,0x00,0x70,0x30,0x30,0x30,0x78,0x00,
 117  0x0C,0x00,0x0C,0x0C,0x0C,0xCC,0xCC,0x78,0xE0,0x60,0x66,0x6C,0x78,0x6C,0xE6,0x00,
 118  0x70,0x30,0x30,0x30,0x30,0x30,0x78,0x00,0x00,0x00,0xCC,0xFE,0xFE,0xD6,0xC6,0x00,
 119  0x00,0x00,0xF8,0xCC,0xCC,0xCC,0xCC,0x00,0x00,0x00,0x78,0xCC,0xCC,0xCC,0x78,0x00,
 120  0x00,0x00,0xDC,0x66,0x66,0x7C,0x60,0xF0,0x00,0x00,0x76,0xCC,0xCC,0x7C,0x0C,0x1E,
 121  0x00,0x00,0xDC,0x76,0x66,0x60,0xF0,0x00,0x00,0x00,0x7C,0xC0,0x78,0x0C,0xF8,0x00,
 122  0x10,0x30,0x7C,0x30,0x30,0x34,0x18,0x00,0x00,0x00,0xCC,0xCC,0xCC,0xCC,0x76,0x00,
 123  0x00,0x00,0xCC,0xCC,0xCC,0x78,0x30,0x00,0x00,0x00,0xC6,0xD6,0xFE,0xFE,0x6C,0x00,
 124  0x00,0x00,0xC6,0x6C,0x38,0x6C,0xC6,0x00,0x00,0x00,0xCC,0xCC,0xCC,0x7C,0x0C,0xF8,
 125  0x00,0x00,0xFC,0x98,0x30,0x64,0xFC,0x00,0x1C,0x30,0x30,0xE0,0x30,0x30,0x1C,0x00,
 126  0x18,0x18,0x18,0x00,0x18,0x18,0x18,0x00,0xE0,0x30,0x30,0x1C,0x30,0x30,0xE0,0x00,
 127  0x76,0xDC,0x00,0x00,0x00,0x00,0x00,0x00,0x55,0xAA,0x55,0xAA,0x55,0xAA,0x55,0xAA,
 128};
 129
 130#define EXPANDED_MEMORY 8
 131#define MEMSIZE ((EXPANDED_MEMORY+1)*0x10000)
 132
 133#define GET16(p) (((p)[0]<<8)|(p)[1])
 134#define PUT16(p,q) ((p)[0]=(q)>>8,(p)[1]=(q))
 135
 136typedef struct {
 137  Uint8 p;
 138  Uint8 d[256];
 139} Stack;
 140
 141typedef struct Device {
 142  Uint8 d[16];
 143  Uint8(*in)(struct Device*,Uint8);
 144  void(*out)(struct Device*,Uint8);
 145  void(*exp)(struct Device*,Uint16);
 146  void*aux;
 147} Device;
 148
 149typedef struct {
 150  FILE*file;
 151  DIR*dir;
 152  char name[4096];
 153  struct dirent*de;
 154  Uint8 mode;
 155} UxnFile;
 156
 157typedef struct {
 158  Uint8*data;
 159  Uint32 addr;
 160  Uint16 len;
 161  Uint8 kind,stage;
 162} Script;
 163
 164static SDL_Color colors[64]={
 165  // 0
 166  {0xFF,0xFF,0xFF,0},
 167  {0x00,0x00,0x00,0},
 168  {0x00,0xFF,0xFF,0},
 169  {0xFF,0x00,0x00,0},
 170  {0xAA,0xAA,0xAA,0},
 171  {0x43,0x43,0x33,0},
 172  {0x43,0xAA,0xAA,0},
 173  {0xAA,0x43,0x33,0},
 174  // 1
 175  {0x00,0x00,0x00,0},
 176  {0xFF,0xFF,0xFF,0},
 177  {0x00,0xFF,0xFF,0},
 178  {0xFF,0x00,0x00,0},
 179  {0x43,0x43,0x33,0},
 180  {0xAA,0xAA,0xAA,0},
 181  {0x43,0xAA,0xAA,0},
 182  {0xAA,0x43,0x33,0},
 183  // 2
 184  {0x10,0x30,0x10,0},
 185  {0x60,0xE0,0x60,0},
 186  {0x60,0x60,0xE0,0},
 187  {0xE0,0x60,0x60,0},
 188  {0x10,0x80,0x10,0},
 189  {0x38,0xFF,0xF8,0},
 190  {0xF8,0x38,0xFF,0},
 191  {0xFF,0xF8,0x38,0},
 192  // 3
 193  {0x55,0x55,0x55,0},
 194  {0xFF,0x55,0x00,0},
 195  {0x00,0x00,0x00,0},
 196  {0xFF,0xFF,0xFF,0},
 197  {0x55,0x55,0x55,0},
 198  {0xFF,0x55,0x00,0},
 199  {0x00,0x00,0x00,0},
 200  {0xFF,0xFF,0xFF,0},
 201  // 4
 202  {0x55,0x55,0x55,0},
 203  {0xFF,0x55,0x00,0},
 204  {0xFF,0xFF,0xFF,0},
 205  {0x00,0x00,0x00,0},
 206  {0x55,0x55,0x55,0},
 207  {0xFF,0x55,0x00,0},
 208  {0xFF,0xFF,0xFF,0},
 209  {0x00,0x00,0x00,0},
 210  // 5
 211  {0xFF,0xFF,0xFF,0},
 212  {0xAA,0xAA,0xAA,0},
 213  {0x55,0x55,0x55,0},
 214  {0x00,0x00,0x00,0},
 215  {0xFF,0x80,0x00,0},
 216  {0xC6,0x60,0x00,0},
 217  {0x94,0x40,0x00,0},
 218  {0x72,0x20,0x00,0},
 219  // 6
 220  {0xAA,0xAA,0xAA,0},
 221  {0x00,0x00,0x00,0},
 222  {0xAA,0xAA,0xAA,0},
 223  {0x00,0x00,0x00,0},
 224  {0x91,0xAA,0x9A,0},
 225  {0x0D,0x0E,0x0E,0},
 226  {0x91,0xAA,0x9A,0},
 227  {0x19,0x15,0x00,0},
 228  // 7
 229  {0xFF,0xFF,0xFF,0},
 230  {0x00,0x00,0x00,0},
 231  {0x77,0xCC,0xAA,0},
 232  {0x99,0x22,0x44,0},
 233  {0xFF,0xFF,0xFF,0},
 234  {0x00,0x00,0x00,0},
 235  {0x77,0xCC,0xAA,0},
 236  {0x99,0x22,0x44,0},
 237};
 238
 239static Uint8 blend[4][16]={
 240  {4,0,0,0,1,4,1,1,2,2,4,2,3,3,3,4},
 241  {0,1,2,3,0,1,2,3,0,1,2,3,0,1,2,3},
 242  {1,2,3,1,1,2,3,1,1,2,3,1,1,2,3,1},
 243  {2,3,1,2,2,3,1,2,2,3,1,2,2,3,1,2},
 244};
 245
 246static const Uint32 notes[12]={
 247  0x20000000,0x21e71f26,0x23eb3588,0x260dfc14,0x285145f3,0x2ab70212,
 248  0x2d413ccd,0x2ff221af,0x32cbfd4a,0x35d13f33,0x39047c0f,0x3c686fce,
 249};
 250
 251static Uint8 mem[MEMSIZE];
 252static Stack ds,rs;
 253static Device device[16];
 254static Uint16 scr_w,scr_h;
 255static Uint8*bglayer;
 256static Uint8*fglayer;
 257static UxnFile uxnfile0;
 258static UxnFile uxnfile1;
 259static FILE*inf;
 260static FILE*outf;
 261
 262static const char*rom_name;
 263static Uint8 use_console=0;
 264static Uint8 use_screen=1;
 265static Uint8 use_mouse=0;
 266static Uint8 use_debug=0;
 267static Uint8 use_utc=0;
 268static Uint8 use_extension=1;
 269static Uint8 use_thread=0;
 270static Uint8 hide_cursor=0;
 271static Uint8 allow_write=0;
 272static Uint8 joypad_repeat=0;
 273static Uint16 scroll_size=1;
 274static Uint8 zoom=1;
 275static Uint8 disallow_size_change=1; /* fixed 320x240 res */
 276static Sint16 default_width=320;
 277static Sint16 default_height=240;
 278static Uint32 scrflags=SDL_SWSURFACE;
 279static Uint16 timer_rate=16;
 280static Uint8 paused=0;
 281static Uint8 picture_changed=0;
 282static Uint8 size_changed=0;
 283static volatile Uint8 timer_expired=0;
 284static SDL_Surface*screen;
 285static Uint8 layers=0;
 286static Uint8 palet=7;
 287static Uint8 touch_mode=0;
 288static Uint8 screencompat=0x03;
 289
 290static Uint16 scriptc;
 291static Script*scriptv;
 292static FILE*scriptf;
 293
 294static Uint8 joy_mask;
 295static Uint8 joy_player[4];
 296static Uint8 joy_hat,joy_hat_mode;
 297static Uint8 joy_axis_x,joy_axis_y,joy_axis_mode;
 298static Uint8 joy_button_map[128];
 299static Uint16 joy_sensitivity=8192;
 300
 301static Uint8 debugger=0;
 302static Uint16 breakpoint=0;
 303static Uint16 break_pc=0x0100;
 304static Uint32 last_tick = 0;
 305static Uint8 debug_op=0;
 306static const touchpad_info_t *tpinfo=NULL;
 307
 308static void debug(int i) {
 309  int j;
 310  if(i) fprintf(stderr,"Debug(%02X)\n",i);
 311  fprintf(stderr,"ds:");
 312  for(i=0;i<ds.p;i++) fprintf(stderr," %02X",ds.d[i]);
 313  fputc('\n',stderr);
 314  fprintf(stderr,"rs:");
 315  for(i=0;i<rs.p;i++) fprintf(stderr," %02X",rs.d[i]);
 316  fputc('\n',stderr);
 317  fprintf(stderr,"   0. 1. 2. 3. 4. 5. 6. 7. 8. 9. A. B. C. D. E. F.\n");
 318  for(i=0;i<16;i++) {
 319    fprintf(stderr,"%1X:",i);
 320    for(j=0;j<16;j++) fprintf(stderr," %02X",device[i].d[j]);
 321    if(device[i].aux) fprintf(stderr," *");
 322    fputc('\n',stderr);
 323  }
 324}
 325
 326static void uxnerr(int n,int pc) {
 327  if(use_debug) debug(-1);
 328  fflush(outf);
 329  fflush(stderr);
 330  err(2,"Uxn error");
 331}
 332
 333#define Push8(z) ({ s->d[s->p++]=(z); })
 334#define Push16(z) ({ s->d[s->p++]=(z)>>8; s->d[s->p++]=(z); })
 335#define Push(z) ({ if(v&0x20) s->d[s->p++]=(z)>>8; s->d[s->p++]=(z); })
 336#define Pop8(z) ({ z=s->d[0xFF&--k]; if(v<0x80) s->p=k; })
 337#define Pop16(z) ({ z=s->d[0xFF&--k]; z|=s->d[0xFF&--k]<<8; if(v<0x80) s->p=k; })
 338#define Pop(z) ({ if(v&0x20) Pop16(z); else Pop8(z); })
 339
 340static void run(Uint16 pc) {
 341  Stack*s;
 342  Uint8 k,v;
 343  Uint16 x,y,z;
 344  if(!pc) return;
 345  start:
 346  while(v=mem[pc++]) {
 347    if(pc==breakpoint && use_screen) goto stop;
 348    s=(v&0x40?&rs:&ds);
 349    k=s->p;
 350    switch(v&0x1F?:v) {
 351      case 0x01: Pop(x); Push(x+1); break;
 352      case 0x02: Pop(x); break;
 353      case 0x03: Pop(x); Pop(y); Push(x); break;
 354      case 0x04: Pop(x); Pop(y); Push(x); Push(y); break;
 355      case 0x05: Pop(z); Pop(y); Pop(x); Push(y); Push(z); Push(x); break;
 356      case 0x06: Pop(x); Push(x); Push(x); break;
 357      case 0x07: Pop(x); Pop(y); Push(y); Push(x); Push(y); break;
 358      case 0x08: Pop(x); Pop(y); Push8(y==x?1:0); break;
 359      case 0x09: Pop(x); Pop(y); Push8(y!=x?1:0); break;
 360      case 0x0A: Pop(x); Pop(y); Push8(y>x?1:0); break;
 361      case 0x0B: Pop(x); Pop(y); Push8(y<x?1:0); break;
 362      case 0x0C: Pop(x); jump: if(v&0x20) pc=x; else pc+=(Sint8)x; break;
 363      case 0x0D: Pop(x); Pop8(y); if(y) goto jump; break;
 364      case 0x0E: Pop(x); y=pc; goto stash;
 365      case 0x0F: Pop(x); goto stash;
 366      case 0x10: Pop8(x); peek: if(v&0x20) x=GET16(mem+x); else x=mem[x]; Push(x); break;
 367      case 0x11: Pop8(x); Pop(y); poke: if(v&0x20) PUT16(mem+x,y); else mem[x]=y; break;
 368      case 0x12: Pop8(x); x=pc+(Sint8)x; goto peek; break;
 369      case 0x13: Pop8(x); Pop(y); x=pc+(Sint8)x; goto poke; break;
 370      case 0x14: Pop16(x); goto peek; break;
 371      case 0x15: Pop16(x); Pop(y); goto poke; break;
 372      case 0x16:
 373        Pop8(x);
 374        z=(x>>4)&15; x&=15;
 375        y=device[z].in(device+z,x);
 376        if(v&0x20) y=(y<<8)|device[z].in(device+z,x+1);
 377        Push(y);
 378        break;
 379      case 0x17:
 380        Pop8(x); Pop(y);
 381        z=(x>>4)&15; x&=15;
 382        if(v&0x20) {
 383          device[z].d[x]=y>>8;
 384          device[z].d[(x+1)&15]=y;
 385          device[z].out(device+z,x);
 386          device[z].out(device+z,x+1);
 387        } else {
 388          device[z].d[x]=y;
 389          device[z].out(device+z,x);
 390        }
 391        break;
 392      case 0x18: Pop(x); Pop(y); Push(y+x); break;
 393      case 0x19: Pop(x); Pop(y); Push(y-x); break;
 394      case 0x1A: Pop(x); Pop(y); Push(y*x); break;
 395      case 0x1B: Pop(x); Pop(y); Push(x?y/x:0); break;
 396      case 0x1C: Pop(x); Pop(y); Push(y&x); break;
 397      case 0x1D: Pop(x); Pop(y); Push(y|x); break;
 398      case 0x1E: Pop(x); Pop(y); Push(y^x); break;
 399      case 0x1F: Pop8(x); Pop(y); z=x>>4; x&=15; Push((y>>x)<<z); break;
 400      case 0x20: Pop8(x); pc+=x?GET16(mem+pc)+2:2; break;
 401      case 0x40: pc+=GET16(mem+pc)+2; break;
 402      case 0x60: Push16(pc+2); pc+=GET16(mem+pc)+2; break;
 403      case 0x80: case 0xC0: Push8(mem[pc++]); break;
 404      case 0xA0: case 0xE0: Push8(mem[pc++]); Push8(mem[pc++]); break;
 405    }
 406    continue;
 407    stash:
 408    s=(v&0x40?&ds:&rs);
 409    if(v&1) {
 410      Push(x);
 411    } else {
 412      Push16(y);
 413      if(v&0x20) pc=x; else pc+=(Sint8)x;
 414    }
 415  }
 416  if(pc==breakpoint && use_screen) {
 417    stop:
 418    paused=debugger=picture_changed=1;
 419    break_pc=pc-1;
 420  }
 421  fflush(outf);
 422}
 423
 424static void load_rom(void) {
 425  FILE*fp=fopen(rom_name,"r");
 426  if(!fp){
 427    err(1, "Cannot load program file");
 428  }
 429  memset(mem,0,MEMSIZE);
 430  fread(mem+0x0100,1,MEMSIZE-0x0100,fp);
 431  if(ferror(fp)){
 432    err(1, "Cannot load program file");
 433  };
 434  fclose(fp);
 435}
 436
 437static Uint8 default_in(Device*misc1,Uint8 misc2) { return misc1->d[misc2&15]; }
 438static void default_out(Device*misc1,Uint8 misc2) {}
 439static void default_exp(Device*misc1,Uint16 misc2) {}
 440
 441static Uint8 system_in(Device*dev,Uint8 id) {
 442  switch(id) {
 443    case 4: return ds.p;
 444    case 5: return rs.p;
 445    default: return dev->d[id];
 446  }
 447}
 448
 449static void system_out(Device*dev,Uint8 id) {
 450  Uint16 x;
 451  switch(id) {
 452    case 3:
 453      x=GET16(dev->d+2);
 454      device[mem[x]>>4].exp(device+(mem[x]>>4),x);
 455      break;
 456    case 4:
 457      ds.p=dev->d[4];
 458      break;
 459    case 5:
 460      rs.p=dev->d[5];
 461      break;
 462    case 8 ... 13:
 463      colors[070].r=colors[074].r=(dev->d[8]>>4)*0x11;
 464      colors[071].r=colors[075].r=(dev->d[8]&15)*0x11;
 465      colors[072].r=colors[076].r=(dev->d[9]>>4)*0x11;
 466      colors[073].r=colors[077].r=(dev->d[9]&15)*0x11;
 467      colors[070].g=colors[074].g=(dev->d[10]>>4)*0x11;
 468      colors[071].g=colors[075].g=(dev->d[10]&15)*0x11;
 469      colors[072].g=colors[076].g=(dev->d[11]>>4)*0x11;
 470      colors[073].g=colors[077].g=(dev->d[11]&15)*0x11;
 471      colors[070].b=colors[074].b=(dev->d[12]>>4)*0x11;
 472      colors[071].b=colors[075].b=(dev->d[12]&15)*0x11;
 473      colors[072].b=colors[076].b=(dev->d[13]>>4)*0x11;
 474      colors[073].b=colors[077].b=(dev->d[13]&15)*0x11;
 475      if(screen && palet==7) {
 476        SDL_SetColors(screen,colors+palet*8,0,8);
 477        picture_changed=1;
 478      }
 479      break;
 480    case 14: if(dev->d[14] && use_debug) debug(dev->d[14]); break;
 481    case 15: if(dev->d[15]) exit(dev->d[15]&0x7F); break;
 482  }
 483}
 484
 485// Subprocess support
 486// End of subprocess support
 487
 488static void console_out(Device*dev,Uint8 id) {
 489  switch(id) {
 490    case 8:  break;
 491    case 9: break;
 492  }
 493}
 494
 495static Uint16 files_stat(int fd,char*name,Uint16 len,Uint8*out) {
 496  struct stat st;
 497  if(strlen(name)+7>len || stat(name,&st)) return 0;
 498  if(S_ISDIR(st.st_mode)) return snprintf(out,len,"---- %s\n",name);
 499  else if(st.st_size<0x10000) return snprintf(out,len,"%04x %s\n",(unsigned int)st.st_size,name);
 500  else return snprintf(out,len,"???? %s\n",name);
 501  return 0;
 502}
 503
 504static Uint32 files_seek(UxnFile*uf,Uint8 mode,Uint32 len) {
 505  switch(uf->mode) {
 506    case 1: case 2: case 4:
 507      switch(mode) {
 508        case 0: fseek(uf->file,len,SEEK_SET); break;
 509        case 1: fseek(uf->file,(Sint32)len,SEEK_CUR); break;
 510        case 2: fseek(uf->file,(Sint32)len,SEEK_END); break;
 511        case 3: return ftell(uf->file);
 512      }
 513      break;
 514    case 3:
 515      //      if(!mode && !len) rewinddir(uf->dir);
 516      uf->de=0;
 517      break;
 518  }
 519  return 0;
 520}
 521
 522static int files_set(Device*dev,int rw) {
 523  // rw: 0(close) 1(read) 2(write) 3(both)
 524  // return: 1 if file opened, 0 if not opened
 525  Uint8 m=dev->d[7];
 526  UxnFile*uf=dev->aux;
 527  if(uf->file) fclose(uf->file);
 528  if(uf->dir) closedir(uf->dir);
 529  uf->file=0;
 530  uf->dir=0;
 531  uf->de=0;
 532  uf->mode=0;
 533  dev->d[2]=dev->d[3]=0;
 534  if(!uf->name[0]) return 0;
 535  if(rw==1) {
 536    if(uf->dir=opendir(uf->name)) {
 537      uf->mode=3;
 538      return 1;
 539    } else if(uf->file=fopen(uf->name,"r")) {
 540      uf->mode=1;
 541      return 1;
 542    } else {
 543      warn("Cannot open file '%s' for reading",uf->name);
 544    }
 545  } else if(rw==2 && allow_write) {
 546    if(uf->file=fopen(uf->name,m?"a":"w")) {
 547      uf->mode=2;
 548      return 1;
 549    } else {
 550      warn("Cannot open file '%s' for writing",uf->name);
 551    }
 552  } else if(rw==3) {
 553    if(allow_write && (uf->file=fopen(uf->name,"r+"))) {
 554      uf->mode=4;
 555      return 1;
 556    } else if(uf->file=fopen(uf->name,"r")) {
 557      uf->mode=1;
 558      return 1;
 559    } else {
 560      warn("Cannot open file '%s' for reading/writing",uf->name);
 561    }
 562  }
 563  return 0;
 564}
 565
 566static void files_out(Device*dev,Uint8 id) {
 567  UxnFile*uf=dev->aux;
 568  Uint16 len=GET16(dev->d+10);
 569  Uint16 addr,x,y;
 570  int i;
 571  switch(id) {
 572    case 5:
 573      addr=GET16(dev->d+4);
 574      dev->d[2]=dev->d[3]=0;
 575      if(!uf->name[0]) break;
 576      y=files_stat(AT_FDCWD,uf->name,len,mem+addr);
 577      if(y<=len) PUT16(dev->d+2,y);
 578      break;
 579    case 7:
 580      if(use_extension) files_set(dev,0);
 581      break;
 582    case 9:
 583      addr=GET16(dev->d+8);
 584      files_set(dev,0);
 585      x=GET16(dev->d+8);
 586      for(i=0;i<4095;i++) {
 587        if(!(uf->name[i]=mem[x+i])) break;
 588        // if(mem[x+i]=='/') uf->name[i]=0;
 589      }
 590      if(use_debug) fprintf(stderr,"! File: %s\n",uf->name);
 591      break;
 592    case 13:
 593      addr=GET16(dev->d+12);
 594      dev->d[2]=dev->d[3]=0;
 595      read:
 596      if(uf->mode==1 || uf->mode==4) {
 597        if(!len) break;
 598        x=fread(mem+addr,1,len,uf->file);
 599        PUT16(dev->d+2,x);
 600        if(ferror(uf->file)) {
 601          warn("Error reading file '%s'",uf->name);
 602          clearerr(uf->file);
 603        }
 604      } else if(uf->mode==3) {
 605        do {
 606	  /*
 607          if(uf->de) {
 608	                if(x=files_stat(dirfd(uf->dir),uf->de->d_name,len,mem+addr)) {
 609              addr+=x;
 610              len-=x;
 611            } else {
 612              break;
 613            }
 614          }*/
 615        } while(uf->de=readdir(uf->dir));
 616        PUT16(dev->d+2,addr-GET16(dev->d+12));
 617      } else {
 618        if(files_set(dev,1)) goto read;
 619      }
 620      break;
 621    case 15:
 622      addr=GET16(dev->d+14);
 623      dev->d[2]=dev->d[3]=0;
 624      if(!allow_write) {
 625        disallow:
 626        warn("Not allowed to write to file '%s'.",uf->name);
 627        break;
 628      }
 629      write:
 630      if(uf->mode==2 || uf->mode==4) {
 631        if(!len) break;
 632        x=fwrite(mem+addr,1,len,uf->file);
 633        PUT16(dev->d+2,x);
 634        if(ferror(uf->file)) {
 635          warn("Error writing file '%s'",uf->name);
 636          clearerr(uf->file);
 637        }
 638      } else {
 639        if(files_set(dev,2)) goto write;
 640      }
 641      break;
 642  }
 643}
 644
 645static Uint8 files_in(Device*dev,Uint8 id) {
 646  UxnFile*uf=dev->aux;
 647  int c;
 648  switch(id) {
 649    case 12: case 13:
 650      dev->d[2]=dev->d[3]=0;
 651      read:
 652      if(uf->mode==1 || uf->mode==4) {
 653        c=fgetc(uf->file);
 654        if(c==EOF) {
 655          if(ferror(uf->file)) {
 656            warn("Error reading file '%s'",uf->name);
 657            clearerr(uf->file);
 658          }
 659          c=0;
 660        } else {
 661          dev->d[3]=1;
 662        }
 663        return c;
 664      } else if(uf->mode==3) {
 665        return 0;
 666      } else {
 667        if(files_set(dev,1)) goto read;
 668      }
 669      return 0;
 670    default: return dev->d[id];
 671  }
 672}
 673
 674static void files_exp(Device*dev,Uint16 addr) {
 675  UxnFile*uf=dev->aux;
 676  Uint32 x;
 677  if(use_extension) switch(mem[addr]&15) {
 678    case 0 ... 2:
 679      x=GET16(mem+addr+1)<<16;
 680      x|=GET16(mem+addr+3);
 681      if(!uf->mode) files_set(dev,3);
 682      files_seek(uf,mem[addr]&15,x);
 683      break;
 684    case 3:
 685      x=files_seek(uf,3,0);
 686      PUT16(mem+addr+1,x>>16);
 687      PUT16(mem+addr+3,x);
 688      break;
 689  }
 690}
 691
 692static Uint8 datetime_in(Device*dev,Uint8 id) {
 693  time_t t=time(0);
 694  struct tm r;
 695  if(use_utc) gmtime_r(&t,&r); else localtime_r(&t,&r);
 696  switch(id) {
 697    case 0: return (r.tm_year+1900)>>8;
 698    case 1: return r.tm_year+1900;
 699    case 2: return r.tm_mon;
 700    case 3: return r.tm_mday;
 701    case 4: return r.tm_hour;
 702    case 5: return r.tm_min;
 703    case 6: return r.tm_sec;
 704    case 7: return r.tm_wday;
 705    case 8: return r.tm_yday>>8;
 706    case 9: return r.tm_yday;
 707    case 10: return r.tm_isdst;
 708    default: return dev->d[id];
 709  }
 710}
 711
 712/*
 713static Uint8 random_in(Device*dev,Uint8 id) {
 714  Uint8 x[1];
 715  if(read(randomfd,x,1)!=1) err(1,"Cannot read random numbers");
 716  return *x;
 717  }*/
 718
 719static Uint8 random_in(Device *dev, Uint8 id)
 720{
 721  (void)dev; (void)id;
 722  return (Uint8)(rand() & 0xFF);
 723}
 724
 725static void set_cursor(void) {
 726  if(hide_cursor) SDL_ShowCursor(paused || !use_mouse);
 727}
 728
 729static void set_screen_mode(int w,int h) {
 730  picture_changed=size_changed=0;
 731  free(bglayer);
 732  if(screen) SDL_FreeSurface(screen);
 733  w=320; h=240;
 734  scr_w=w;
 735  scr_h=h;
 736  
 737  screen=SDL_SetVideoMode(w*zoom,h*zoom,8,SDL_SWSURFACE);
 738  if(!screen) { err(1, "Cannot set screen mode"); }
 739  SDL_SetColors(screen,colors+palet*8,0,8);
 740  bglayer=calloc(w*h,2);
 741  if(!bglayer) { err(1, "Memory allocation error"); }
 742  fglayer=bglayer+w*h;
 743  
 744  PUT16(device[2].d + 2, scr_w);
 745  PUT16(device[2].d + 4, scr_h);
 746}
 747
 748static void screen_fill(Uint8*p,Uint8 v,Uint16 x0,Uint16 y0,Uint16 x1,Uint16 y1) {
 749  if(y1>scr_h) y1=scr_h;
 750  if(x1>scr_w) x1=scr_w;
 751  if(y0>=y1 || x0>=x1) return;
 752  p+=y0*scr_w+x0;
 753  x1-=x0;
 754  while(y0++<y1) memset(p,v,x1),p+=scr_w;
 755}
 756
 757static void draw_sprite(Uint16 x,Uint16 y,Uint16 addr,Uint8 v) {
 758  Uint8 b=v&0x80;
 759  Uint8 u;
 760  Uint16 c;
 761  Uint8*p=v&0x40?fglayer:bglayer;
 762  Uint8*m;
 763  if(x+8>scr_w || y+8>scr_h || addr>0x10000-(b?16:8)) return;
 764  p+=y*scr_w+x;
 765  m=mem+addr;
 766  for(y=0;y<8;y++) {
 767    c=m[y]|(b?m[y+8]<<8:0);
 768    for(x=0;x<8;x++) {
 769      u=(c&1)|((c>>7)&2);
 770      c>>=1;
 771      if((u=blend[u][v&0x0F])!=4) p[(y^(v&0x20?7:0))*scr_w+(x^(v&0x10?0:7))]=u;
 772    }
 773  }
 774}
 775
 776static void screen_out(Device*dev,Uint8 id) {
 777  int i,fx,fy;
 778  Uint16 w;
 779  Uint16 x=GET16(dev->d+8);
 780  Uint16 y=GET16(dev->d+10);
 781  Uint8 z=dev->d[id];
 782  if(id<6 && disallow_size_change) {
 783    PUT16(dev->d+2,scr_w);
 784    PUT16(dev->d+4,scr_h);
 785    return;
 786  }
 787  switch(id) {
 788    case 2: case 3:
 789      x=GET16(dev->d+2);
 790      if(x!=scr_w) scr_w=x,size_changed=1;
 791      break;
 792    case 4: case 5:
 793      x=GET16(dev->d+4);
 794      if(x!=scr_h) scr_h=x,size_changed=1;
 795      break;
 796    case 14:
 797      if(size_changed) set_screen_mode(scr_w,scr_h);
 798      picture_changed=1;
 799      if(z<0x80) {
 800        if(x<scr_w && y<scr_h) (z&0x40?fglayer:bglayer)[x+y*scr_w]=z&3;
 801        if(dev->d[6]&0x01) PUT16(dev->d+8,x+1);
 802        if(dev->d[6]&0x02) PUT16(dev->d+10,y+1);
 803      } else {
 804        screen_fill(z&0x40?fglayer:bglayer,z&3,dev->d[14]&0x10?0:x,dev->d[14]&0x20?0:y,dev->d[14]&0x10?x:scr_w,dev->d[14]&0x20?y:scr_h);
 805      }
 806      break;
 807    case 15:
 808      if(size_changed) set_screen_mode(scr_w,scr_h);
 809      picture_changed=1;
 810      w=GET16(dev->d+12);
 811      fx=((z&0x10)&&(screencompat&0x02)?-8:8);
 812      fy=((z&0x20)&&(screencompat&0x02)?-8:8);
 813      for(i=0;i<=(dev->d[6]>>4);i++) {
 814        draw_sprite(x+i*(dev->d[6]&0x02?fx:0),y+i*(dev->d[6]&0x01?fy:0),w,z);
 815        if(dev->d[6]&0x04) w+=z&0x80?16:8;
 816      }
 817      if(dev->d[6]&0x01) PUT16(dev->d+8,x+fx);
 818      if(dev->d[6]&0x02) PUT16(dev->d+10,y+fy);
 819      if(dev->d[6]&0x04) PUT16(dev->d+12,w);
 820      break;
 821  }
 822}
 823
 824static void system_exp(Device *dev, Uint16 addr)
 825{
 826  Uint32 src, dst;
 827  Uint16 len, n;
 828  switch (mem[addr]) {
 829    case 0x00:
 830      len = GET16(mem + addr + 1);
 831      dst = (GET16(mem + addr + 3) << 16L) | GET16(mem + addr + 5);
 832      if (dst < MEMSIZE && dst + len <= MEMSIZE)
 833        memset(mem + dst, mem[addr + 7], len);
 834      break;
 835    case 0x01: case 0x02:
 836      len = GET16(mem + addr + 1);
 837      src = (GET16(mem + addr + 3) << 16L) | GET16(mem + addr + 5);
 838      dst = (GET16(mem + addr + 7) << 16L) | GET16(mem + addr + 9);
 839      if (dst < MEMSIZE && src + len <= MEMSIZE && dst + len <= MEMSIZE) {
 840        if (mem[addr] == 0x01)
 841          for (n = 0; n < len; n++) mem[dst + n] = mem[src + n];
 842        else
 843          for (n = len; n; n--) mem[dst + n - 1] = mem[src + n - 1];
 844      }
 845      break;
 846    case 0x04:
 847      if (use_extension) PUT16(mem + addr + 1, EXPANDED_MEMORY);
 848      break;
 849  }
 850}
 851
 852static void debug_screen(Uint8*s,Uint8 k) {
 853  static Sint8 page=0;
 854  static Uint8 port=0;
 855  static Uint8 din;
 856  static Uint32 addr=0;
 857  static Uint32 scrol=0;
 858  static const char opname[]=
 859    "?IPNSRDOENGLJJJSLSLSLSDDASMDAOES"
 860    "?NOIWOUVQETTMCSTDTDTDTEEDUUINROF"
 861    "?CPPPTPRUQHHPNRHZZRRAAIODBLVDART"
 862  ;
 863  char buf[81];
 864  int i,j;
 865  Stack*z;
 866  FILE*f;
 867  UxnFile*u;
 868  if(s) {
 869    memset(s,32,80*25);
 870    show:
 871    switch(page) {
 872      case 0:
 873        memcpy(s+80*0,buf,snprintf(buf,81,"* %s",rom_name));
 874        memcpy(s+80*1,buf,snprintf(buf,81,"<c> Console: %s",use_console?"On":"Off"));
 875        memcpy(s+80*2,buf,snprintf(buf,81,"<d> Debug: %s",use_debug?"On":"Off"));
 876        memcpy(s+80*4,buf,snprintf(buf,81,"<F3> Mouse: %s",use_mouse?"On":"Off"));
 877        memcpy(s+80*5,buf,snprintf(buf,81,"<F4> Layers: %s",layers?(layers==1?"fg":"bg"):"fg/bg"));
 878        memcpy(s+80*6,buf,snprintf(buf,81,"<F6/F7> Palette: %d",palet));
 879        memcpy(s+80*7,buf,snprintf(buf,81,"<F9> Restart"));
 880        memcpy(s+80*8,buf,snprintf(buf,81,"<F10> Quit"));
 881        memcpy(s+80*10,buf,snprintf(buf,81,"<PAUSE> Resume"));
 882        memcpy(s+80*13,buf,snprintf(buf,81,"Pages: CTRL+"));
 883        memcpy(s+80*14,buf,snprintf(buf,81," A:Stack   B:Return  C:Device  D:Memory  E:Files "));
 884        memcpy(s+80*15,buf,snprintf(buf,81," F:Code    G:        N:        O:        P:      "));
 885        memcpy(s+80*16,buf,snprintf(buf,81," Q:        R:        S:        T:        U:      "));
 886        memcpy(s+80*17,buf,snprintf(buf,81," V:        W:        X:        Y:        Z:Main  "));
 887        break;
 888      case 1: case 2:
 889        if(page==1) z=&ds; else z=&rs;
 890        memcpy(s,buf,snprintf(buf,81,"%s Stack (%02x)",page==1?"Data":"Return",z->p));
 891        for(i=0;i<256;i++) memcpy(s+80*(i/16+1)+(i&15)*4,buf,snprintf(buf,81,"%c%02x%c",z->p==i?'[':' ',z->d[i],z->p==i?']':' '));
 892        break;
 893      case 3:
 894        memcpy(s,buf,snprintf(buf,81,"--   -0  -1  -2  -3  -4  -5  -6  -7  -8  -9  -a  -b  -c  -d  -e  -f"));
 895        for(i=0;i<16;i++) memcpy(s+80*(i+1),buf,snprintf(buf,81,"%x0:",i));
 896        for(i=0;i<256;i++) memcpy(s+80*(i/16+1)+(i&15)*4+4,buf,snprintf(buf,81,"%c%02x%c",port==i?'[':' ',device[i>>4].d[i&15],port==i?']':' '));
 897        memcpy(s+80*17,buf,snprintf(buf,81,"<i> In=%02x  <o> Out  <x> Execute",din));
 898        break;
 899      case 4:
 900        if(addr<0) addr=0;
 901        if(addr>=MEMSIZE) addr=MEMSIZE-1;
 902        if(scrol<0) scrol=0;
 903        if(addr>=0x100 && scrol<addr-0xF0) scrol=(addr-0xF0)&~15;
 904        if(scrol>addr) scrol=addr&~15;
 905        memcpy(s,buf,snprintf(buf,81,"-----   -0  -1  -2  -3  -4  -5  -6  -7  -8  -9  -a  -b  -c  -d  -e  -f"));
 906        for(i=0;i<16;i++) memcpy(s+80*(i+1),buf,snprintf(buf,81,"%05x:",i*16+scrol));
 907        for(i=0;i<256;i++) if(i+scrol<MEMSIZE) memcpy(s+80*(i/16+1)+(i&15)*4+7,buf,snprintf(buf,81,"%c%02x%c",addr==i+scrol?'[':' ',mem[i+scrol],addr==i+scrol?']':' '));
 908        memcpy(s+80*17,buf,snprintf(buf,81,"<x> Execute  <s> Save"));
 909        break;
 910      case 5:
 911        for(i=0;i<2;i++) {
 912          u=i?&uxnfile1:&uxnfile0;
 913          memcpy(s+80*(7*i+1),buf,snprintf(buf,81,"[File%d]",i));
 914          memcpy(s+80*(7*i+2),buf,snprintf(buf,81," name=%s",u->name));
 915          if(u->file) memcpy(s+80*(7*i+3),buf,snprintf(buf,81," File fd=%d offset=0x%lX",fileno(u->file),ftell(u->file)));
 916	  //          if(u->dir) memcpy(s+80*(7*i+4),buf,snprintf(buf,81," Dir fd=%d offset=0x%lX",dirfd(u->dir),telldir(u->dir)));
 917          memcpy(s+80*(7*i+5),buf,snprintf(buf,81," mode=0x%02X",u->mode));
 918          memcpy(s+80*(7*i+6),buf,snprintf(buf,81," <%c> Close  <%c> Rewind","cC"[i],"rR"[i]));
 919        }
 920        break;
 921      case 6:
 922        j=break_pc;
 923        for(i=0;i<16 && j<0x10000;i++) {
 924          memcpy(s+80*i,buf,snprintf(buf,81,"%04x: %c%c %c%c%c%c%c%c",j,breakpoint==j+1?'*':' ',i?' ':'>',
 925           opname[mem[j]&0x1F],opname[(mem[j]&0x1F)+0x20],opname[(mem[j]&0x1F)+0x40]
 926           ,mem[j]&0x20?'2':'_',mem[j]&0x40?'r':'_',mem[j]&0x80?'k':'_' ));
 927          if(mem[j]==0x80 || mem[j]==0xC0) {
 928            memcpy(s+80*i+9,buf,snprintf(buf,81,"LIT_%c  %02x",mem[j]&0x40?'r':'_',mem[j+1]));
 929            j+=2;
 930          } else if(mem[j]==0xA0 || mem[j]==0xE0) {
 931            memcpy(s+80*i+9,buf,snprintf(buf,81,"LIT2%c  %02x%02x",mem[j]&0x40?'r':'_',mem[j+1],mem[j+2]));
 932            j+=3;
 933          } else {
 934            if(!mem[j]) memcpy(s+80*i+9,"BRK",3);
 935            j++;
 936          }
 937        }
 938        memcpy(s+80*17,buf,snprintf(buf,81,"ds:%02x rs:%02x",ds.p,rs.p));
 939        memcpy(s+80*19,buf,snprintf(buf,81,"<SPACE> Step  <RET> Continue  <x> Execute"));
 940        memcpy(s+80*20,buf,snprintf(buf,81,"<,> Set Breakpoint  <.> Clear Breakpoint"));
 941        break;
 942      default:
 943        if(page<0) page=6; else page=0;
 944        goto show;
 945    }
 946  } else {
 947    if(k==9 || k==']') {
 948      ++page;
 949    } else if(k=='[') {
 950      --page;
 951    } else if(k>=1 && k<=7) {
 952      page=k;
 953    } else if(k>=14 && k<=25) {
 954      page=k-6;
 955    } else if(k==26) {
 956      page=0;
 957    } else if(k==27) {
 958      debugger=0;
 959      return;
 960    } else switch(page) {
 961      case 0:
 962        if(k=='c') use_console^=1;
 963        if(k=='d') use_debug^=1;
 964        break;
 965      case 1: case 2:
 966        if(page==1) z=&ds; else z=&rs;
 967        if(k==8) z->p--;
 968        if(k==12) z->p++;
 969        if(k==10) z->p+=16;
 970        if(k==11) z->p-=16;
 971        if(k>='0' && k<='9') z->d[z->p]=(z->d[z->p]<<4)+(k-'0');
 972        if(k>='a' && k<='f') z->d[z->p]=(z->d[z->p]<<4)+(k+10-'a');
 973        break;
 974      case 3:
 975        i=port;
 976        if(k==8) port--;
 977        if(k==12) port++;
 978        if(k==10) port+=16;
 979        if(k==11) port-=16;
 980        if(k>='0' && k<='9') device[i>>4].d[i&15]=(device[i>>4].d[i&15]<<4)+(k-'0');
 981        if(k>='a' && k<='f') device[i>>4].d[i&15]=(device[i>>4].d[i&15]<<4)+(k+10-'a');
 982        if(k=='i') din=device[i>>4].in(device+(i>>4),i&15);
 983        if(k=='o') device[i>>4].out(device+(i>>4),i&15);
 984        if(k=='x') run(GET16(device[i>>4].d));
 985        break;
 986      case 4:
 987        if(k==8) addr--;
 988        if(k==12) addr++;
 989        if(k==10) addr+=16;
 990        if(k==11) addr-=16;
 991        if(k==',') scrol-=0x10,addr-=0x10;
 992        if(k=='.') scrol+=0x10,addr+=0x10;
 993        if(k=='<') scrol-=0x100,addr-=0x100;
 994        if(k=='>') scrol+=0x100,addr+=0x100;
 995        if(k=='q') scrol-=0x1000,addr-=0x1000;
 996        if(k=='w') scrol+=0x1000,addr+=0x1000;
 997        if(k=='Q') scrol-=0x10000,addr-=0x10000;
 998        if(k=='W') scrol+=0x10000,addr+=0x10000;
 999        if(k=='=') scrol=addr=0;
1000        if(addr>=MEMSIZE+0x1000) scrol=addr=0;
1001        if(k>='0' && k<='9') mem[addr]=(mem[addr]<<4)+(k-'0');
1002        if(k>='a' && k<='f') mem[addr]=(mem[addr]<<4)+(k+10-'a');
1003        if(k=='x' && addr<0x10000) run(addr);
1004        if(k=='s') {
1005          f=fopen("_uxn_memory","w");
1006          if(f) {
1007            fwrite(mem,1,MEMSIZE,f);
1008            fprintf(stderr,"Saved 0x%X bytes to file _uxn_memory\n",(int)(MEMSIZE));
1009            fclose(f);
1010          } else {
1011            warn("Cannot open file _uxn_memory");
1012          }
1013        }
1014        break;
1015      case 5:
1016        if(k=='c') files_set(device+10,0);
1017        if(k=='C') files_set(device+11,0);
1018        if(k=='r') files_seek(&uxnfile0,0,0);
1019        if(k=='R') files_seek(&uxnfile1,0,0);
1020        break;
1021      case 6:
1022        if(k==8 || k==11) --break_pc;
1023        if(k==12 || k==10) ++break_pc;
1024        if(k>='0' && k<='9') break_pc=(break_pc<<4)+(k-'0');
1025        if(k>='a' && k<='f') break_pc=(break_pc<<4)+(k+10-'a');
1026        if(k==',') breakpoint=break_pc+1;
1027        if(k=='.') breakpoint=0;
1028        if(k=='x') run(break_pc);
1029        if(k==' ') {
1030          j=breakpoint;
1031          breakpoint=break_pc+2;
1032          i=mem[break_pc];
1033          if(i==0x80 || i==0xC0) {
1034            breakpoint=break_pc+3;
1035          } else if(i==0xA0 || i==0xE0) {
1036            breakpoint=break_pc+4;
1037          } else if((i&0x1F)==0x0C || (i&0x1F)==0x0D || (i&0x1F)==0x0E) {
1038            if((i&0x1F)==0x0D) {
1039              i&=0x60;
1040              if(i==0x00 && ds.p>1 && !ds.d[ds.p-2]) goto no_jump;
1041              if(i==0x20 && ds.p>2 && !ds.d[ds.p-3]) goto no_jump;
1042              if(i==0x40 && rs.p>1 && !rs.d[rs.p-2]) goto no_jump;
1043              if(i==0x60 && rs.p>2 && !rs.d[rs.p-3]) goto no_jump;
1044            }
1045            i&=0x60;
1046            if(i==0x00 && ds.p) breakpoint=break_pc+((Sint8)ds.d[ds.p-1])+2;
1047            else if(i==0x20 && ds.p>1) breakpoint=GET16(ds.d+ds.p-2)+1;
1048            else if(i==0x40 && rs.p) breakpoint=break_pc+((Sint8)rs.d[rs.p-1])+2;
1049            else if(i==0x60 && rs.p>1) breakpoint=GET16(rs.d+rs.p-2)+1;
1050            else goto no_run;
1051          } else if(i) {
1052            no_jump:
1053            breakpoint=break_pc+2;
1054          } else {
1055            goto no_run;
1056          }
1057          run(break_pc);
1058          no_run:
1059          breakpoint=j;
1060          break;
1061        }
1062        if(k==13) {
1063          paused=debugger=0;
1064          run(break_pc);
1065          return;
1066        }
1067        break;
1068    }
1069  }
1070}
1071
1072static void redraw(void) {
1073  static Uint8*s=0;
1074  int a,r,u,v,x,y;
1075  Uint8*p;
1076  
1077  if(debugger) {
1078    if(!s) s=calloc(80,26);
1079    if(!s) err(1,"Allocation failed");
1080    debug_screen(s,0);
1081  }
1082  if(size_changed) set_screen_mode(scr_w,scr_h);
1083  picture_changed=0;
1084  SDL_LockSurface(screen);
1085  a=0;
1086  p=screen->pixels;
1087  r=screen->pitch;
1088  
1089  /*
1090  if(debugger) {
1091    for(y=0;y<scr_h*zoom;y++) {
1092      for(x=0;x<scr_w*zoom;x++) {
1093        if(y>=25*8-7 || x>=80*8 || (v=s[(y/8)*80+x/8]-32)<=0) p[x]=0;
1094        else p[x]=(fontdata[v*8+(y&7)]>>(7&~x))&1;
1095      }
1096      p+=r;
1097    }
1098    if(scr_h*zoom>9) {
1099      p=screen->pixels+r*(scr_h*zoom-8);
1100      for(y=0;y<8;y++) {
1101        for(a=0;a<scr_w*zoom;a++) p[a]=(a>>4)&7;
1102        p+=r;
1103      }
1104    }
1105  } else */ if(layers) {
1106    for(y=0;y<scr_h;y++) {
1107      for(x=0;x<scr_w;x++) {
1108        u=(layers==1?fglayer:bglayer)[a];
1109        for(v=0;v<zoom;v++) memset(p+v*r+x*zoom,u,zoom);
1110        a++;
1111      }
1112      p+=r*zoom;
1113    }
1114  } else {
1115    for(y=0;y<scr_h;y++) {
1116      for(x=0;x<scr_w;x++) {
1117        u=fglayer[a]?fglayer[a]+4:bglayer[a];
1118        for(v=0;v<zoom;v++) memset(p+v*r+x*zoom,u,zoom);
1119        a++;
1120      }
1121      p+=r*zoom;
1122    }
1123  }
1124  SDL_UnlockSurface(screen);
1125  SDL_Flip(screen);
1126}
1127
1128static inline int select_joystick(Uint8 n) {
1129  if((joy_mask&1) && n==joy_player[0]) return 2;
1130  if((joy_mask&2) && n==joy_player[1]) return 5;
1131  if((joy_mask&4) && n==joy_player[2]) return 6;
1132  if((joy_mask&8) && n==joy_player[3]) return 7;
1133  return 0;
1134}
1135
1136static Sint16 last_x = -1, last_y = -1;
1137
1138#define SENSITIVITY 4
1139void update_mouse_from_touchpad(void) {
1140    if (!tpinfo) {
1141        tpinfo = touchpad_getinfo();
1142        if (!tpinfo) return;
1143    }
1144
1145    touchpad_report_t report;
1146    if (touchpad_scan(&report) != 0) return;
1147
1148    if (!report.contact) {
1149        device[9].d[6] = 0;
1150        last_x = -1; last_y = -1;
1151        return;
1152    }
1153
1154    if (last_x < 0) {
1155        last_x = report.x; last_y = report.y;
1156        return;
1157    }
1158
1159    /* relative delta */
1160    Sint16 dx = (Sint16)report.x - last_x;
1161    Sint16 dy = last_y - (Sint16)report.y;
1162    last_x = report.x;
1163    last_y = report.y;
1164    dx /= SENSITIVITY; /* higher divisor, higher sensitivity */
1165    dy /= SENSITIVITY;
1166
1167    Uint16 mx = (device[9].d[2] << 8) | device[9].d[3];
1168    Uint16 my = (device[9].d[4] << 8) | device[9].d[5];
1169  
1170    /* apply delta to actual mouse coords */
1171    if (dx < 0 && mx < (Uint16)(-dx)) mx = 0;
1172    else mx += dx;
1173    if (dy < 0 && my < (Uint16)(-dy)) my = 0;
1174    else my += dy;
1175
1176    if (mx >= scr_w) mx = scr_w - 1;
1177    if (my >= scr_h) my = scr_h - 1;
1178
1179    device[9].d[2] = mx >> 8;
1180    device[9].d[3] = mx & 0xff;
1181    device[9].d[4] = my >> 8;
1182    device[9].d[5] = my & 0xff;
1183
1184    run(GET16(device[9].d));
1185}
1186
1187static int run_screen(void) {
1188  SDL_Event e;
1189  int i,j,k;
1190  last_tick = SDL_GetTicks();
1191  
1192    while (1) {
1193    Uint32 now = SDL_GetTicks();
1194    if (now - last_tick >= timer_rate) {
1195      last_tick = now;
1196      if (!paused) run(GET16(device[2].d));
1197    }
1198  
1199  while(SDL_PollEvent(&e)) {
1200    switch(e.type) {
1201      case SDL_QUIT: return 0;
1202      case SDL_VIDEOEXPOSE: redraw(); break;
1203      case SDL_KEYDOWN:
1204        i=0;
1205        switch(e.key.keysym.sym) {
1206	case SDLK_ESCAPE: return 0; break;
1207	case SDLK_7: case SDLK_LCTRL: case SDLK_END: i=0x01; break;
1208	case SDLK_9: case SDLK_LSHIFT: i=0x02; break;
1209          case SDLK_5: i=0x08; break;
1210	case SDLK_8: case SDLK_UP: i=0x10; break;
1211	case SDLK_2: case SDLK_DOWN: i=0x20; break;
1212	case SDLK_4: case SDLK_LEFT: i=0x40; break;
1213	case SDLK_6: case SDLK_RIGHT: i=0x80; break;
1214          case SDLK_PAGEUP: i=-scroll_size; goto scroll;
1215          case SDLK_PAGEDOWN: i=scroll_size; goto scroll;
1216	case SDLK_F1: run(GET16(device[2].d));  break;
1217	case SDLK_1:  device[9].d[6] = 0x01; break;
1218	case SDLK_PERIOD:  device[9].d[6] = 0x03; break;
1219	case SDLK_3:  device[9].d[6] = 0x02; break;
1220          case SDLK_F3: use_mouse^=1; set_cursor(); picture_changed=debugger; break;
1221          case SDLK_F4: layers=(layers+1)%3; redraw(); break;
1222          case SDLK_F5: redraw(); if(SDL_GetModState()&KMOD_RSHIFT) debug(-1); break;
1223          case SDLK_F6: palet=(palet+1)&7; SDL_SetColors(screen,colors+palet*8,0,8); redraw(); break;
1224          case SDLK_F7: palet=(palet-1)&7; SDL_SetColors(screen,colors+palet*8,0,8); redraw(); break;
1225          case SDLK_F8: paused=picture_changed=debugger=1; break;
1226          case SDLK_F9: return 1;
1227          case SDLK_F10: return 0;
1228          case SDLK_PAUSE: debugger=0; paused^=1; set_cursor(); picture_changed=1; break;
1229        }
1230        if(i) {
1231          if(joypad_repeat || !(device[8].d[2]&i)) {
1232            device[8].d[2]|=i;
1233            run(GET16(device[8].d));
1234          }
1235        } else if(i=e.key.keysym.unicode) {
1236          device[8].d[3]=i;
1237          run(GET16(device[8].d));
1238          if(!debugger) device[8].d[3]=0; else debug_op=8;
1239        }
1240        break;
1241      scroll:
1242        if(paused || !i) break;
1243        PUT16(device[9].d+12,i);
1244        run(GET16(device[9].d));
1245        if(!debugger) PUT16(device[9].d+12,0); else debug_op=9;
1246        break;
1247      case SDL_KEYUP:
1248        if(paused) break;
1249        i=0;
1250        switch(e.key.keysym.sym) {
1251	case SDLK_7: case SDLK_LCTRL: case SDLK_END: i=0x01; break;
1252	case SDLK_9: case SDLK_LSHIFT: i=0x02; break;
1253          case SDLK_5: i=0x08; break;
1254	case SDLK_8: case SDLK_UP: i=0x10; break;
1255	case SDLK_2: case SDLK_DOWN: i=0x20; break;
1256	case SDLK_4: case SDLK_LEFT: i=0x40; break;
1257	case SDLK_6: case SDLK_RIGHT: i=0x80; break;
1258	case SDLK_PERIOD: case SDLK_3: case SDLK_1:  device[9].d[6] = 0x00; break;
1259        }
1260        if(device[8].d[2]&i) {
1261          device[8].d[2]&=~i;
1262          run(GET16(device[8].d));
1263        }
1264        break;
1265    }
1266  }
1267  if(picture_changed) {
1268    redraw();
1269    picture_changed=0;
1270  }
1271  update_mouse_from_touchpad();
1272  SDL_Delay(1);
1273    }
1274    return 0;
1275}
1276
1277int main(int argc, char **argv)
1278{
1279  int i;
1280
1281  /* seed RNG */
1282  srand(SDL_GetTicks() ^ (unsigned)time(NULL));
1283
1284  /* initialise devices */
1285  for (i = 0; i < 16; i++) {
1286    device[i].in  = default_in;
1287    device[i].out = default_out;
1288    device[i].exp = default_exp;
1289  }
1290  device[0].in  = system_in;
1291  device[0].out = system_out;
1292  device[0].exp = system_exp;
1293  device[10].aux = &uxnfile0;
1294  device[11].aux = &uxnfile1;
1295  device[10].out = files_out;
1296  device[10].in  = files_in;
1297  device[10].exp = files_exp;
1298  device[11].out = files_out;
1299  device[11].in  = files_in;
1300  device[11].exp = files_exp;
1301  device[12].in  = datetime_in;
1302
1303  /* optional random device on port 0xE */
1304  device[0xE].in = random_in;
1305
1306  if (use_console) device[1].out = console_out;
1307
1308  use_screen = 1;
1309  use_console = 0;
1310  use_mouse = 0;
1311  allow_write = 0;
1312  zoom = 1;
1313  disallow_size_change = 1;
1314  timer_rate = 16;
1315  scr_w = 320; scr_h = 240;
1316  tpinfo=touchpad_getinfo();
1317
1318  if (argc > 1) rom_name = argv[1];
1319
1320  load_rom();
1321
1322  if (use_screen) {
1323    if (SDL_Init(SDL_INIT_VIDEO) < 0)
1324      show_msgbox("SDL Error", "SDL_Init failed");
1325    atexit(SDL_Quit);
1326
1327    device[2].out = screen_out;
1328    PUT16(device[2].d + 2, scr_w);
1329    PUT16(device[2].d + 4, scr_h);
1330    size_changed = 1;
1331    set_screen_mode(scr_w, scr_h);
1332  }
1333
1334restart:
1335  size_changed = 1;
1336  ds.p = rs.p = 0;
1337  memset(device[1].d, 0, 16);
1338  run(0x0100);
1339
1340  if (use_screen) {
1341    while (run_screen()) {
1342      /* restart requested (F9) */
1343      for (i = 0; i < 15; i++)
1344        memset(device[i].d, 0, 16);
1345      load_rom();
1346      goto restart;
1347    }
1348    SDL_Quit();
1349  }
1350
1351  return 0;
1352}