view io.c @ 1:3c736a81b886

add disk
author Shinji KONO <kono@ie.u-ryukyu.ac.jp>
date Mon, 02 Jul 2018 19:07:39 +0900
parents 9a224bd9b45f
children 31d96e2b364e
line wrap: on
line source

/* 6809 Simulator V09.

   created 1993,1994 by L.C. Benschop.
   copyleft (c) 1994-2014 by the sbc09 team, see AUTHORS for more details.
   license: GNU General Public License version 2, see LICENSE for more details.

   This program simulates a 6809 processor.

   System dependencies: short must be 16 bits.
   char  must be 8 bits.
   long must be more than 16 bits.
   arrays up to 65536 bytes must be supported.
   machine must be twos complement.
   Most Unix machines will work. For MSODS you need long pointers
   and you may have to malloc() the mem array of 65536 bytes.

   Define BIG_ENDIAN if you have a big-endian machine (680x0 etc)

   Special instructions:
   SWI2 writes char to stdout from register B.
   SWI3 reads char from stdout to register B, sets carry at EOF.
   (or when no key available when using term control).
   SWI retains its normal function.
   CWAI and SYNC stop simulator.

*/

#include<stdio.h>
#include<stdlib.h>
#include<ctype.h>
#include<signal.h>
#include<sys/time.h>

#include <unistd.h>
#include <fcntl.h>
#include <string.h>
#include <time.h>

#ifdef USE_TERMIOS
#include <termios.h>
#endif

#define engine extern
#include "v09.h"

/*
 *      IO Map
 *
 *   0xe000 - 0xe100
 *
 *   0xe000   ACIA  control
 *   0xe001   ACIA  data
 *
 *   0xe010   Timer control       0x8f start timer/0x80 stop timer/0x04 update date
 *   0xe011-  YY/MM/DD/HH/MM/SS
 *
 *   0xe020   Disk control        0x81 read/0x55 write   0 ... ok / 0xff .. error
 *   0xe021   drive no
 *   0xe022   LSN2
 *   0xe023   LSN1
 *   0xe024   LSN0
 *   0xe025   ADR2
 *   0xe026   ADR1
 *
 */

#define SECSIZE 256

int tflags;
int timer = 1;
struct termios termsetting;

int xmstat; /* 0= no XMODEM transfer, 1=send, 2=receiver */
unsigned char xmbuf[132];
int xidx;
int acknak;
int rcvdnak;
int blocknum;

FILE *logfile;
FILE *infile;
FILE *xfile;
FILE *disk[] = {0,0};

extern void hexadump( unsigned char *b, int l, int loc, int w);
extern void disasm(int,int);

void do_timer(int,int);
void do_disk(int,int);

int char_input(void) {
        int c, w, sum;
        if (!xmstat) {
                if (infile) {
                        c = getc(infile);
                        if (c == EOF) {
                                fclose(infile);
                                infile = 0;
                                return char_input();
                        }
                        if (c == '\n')
                                c = '\r';
                        return c;
                } else
                        return getchar();
        } else if (xmstat == 1) {
                if (xidx) {
                        c = xmbuf[xidx++];
                        if (xidx == 132) {
                                xidx = 0;
                                rcvdnak = EOF;
                                acknak = 6;
                        }
                } else {
                        if ((acknak == 21 && rcvdnak == 21) || (acknak == 6 && rcvdnak == 6)) {
                                rcvdnak = 0;
                                memset(xmbuf, 0, 132);
                                w = fread(xmbuf + 3, 1, 128, xfile);
                                if (w) {
                                        printf("Block %3d transmitted, ", blocknum);
                                        xmbuf[0] = 1;
                                        xmbuf[1] = blocknum;
                                        xmbuf[2] = 255 - blocknum;
                                        blocknum = (blocknum + 1) & 255;
                                        sum = 0;
                                        for (w = 3; w < 131; w++)
                                                sum = (sum + xmbuf[w]) & 255;
                                        xmbuf[131] = sum;
                                        acknak = 6;
                                        c = 1;
                                        xidx = 1;
                                } else {
                                        printf("EOT transmitted, ");
                                        acknak = 4;
                                        c = 4;
                                }
                        } else if (rcvdnak == 21) {
                                rcvdnak = 0;
                                printf("Block %3d retransmitted, ", xmbuf[1]);
                                c = xmbuf[xidx++]; /*retransmit the same block */
                        } else
                                c = EOF;
                }
                return c;
        } else {
                if (acknak == 4) {
                        c = 6;
                        acknak = 0;
                        fclose(xfile);
                        xfile = 0;
                        xmstat = 0;
                } else if (acknak) {
                        c = acknak;
                        acknak = 0;
                } else
                        c = EOF;
                if (c == 6)
                        printf("ACK\n");
                if (c == 21)
                        printf("NAK\n");
                return c;
        }
}

int do_input( a) {
        static int c, f = EOF;
        if (a == 0) {
                if (f == EOF)
                        f = char_input();
                if (f != EOF)
                        c = f;
                return 2 + (f != EOF);
        } else if (a == 1) { /*data port*/
                if (f == EOF)
                        f = char_input();
                if (f != EOF) {
                        c = f;
                        f = EOF;
                }
                return c;
        } else if ((a&0xf0) == 0x10) { /* timer */
                return mem[IOPAGE + a];
        } else if ((a&0xf0) == 0x20) { /* disk */
                return mem[IOPAGE + a];
        }
        return 0;
}

void do_output(int a, int c) {
        int i, sum;
        if (a == 1) { /* ACIA data port,ignore address */
                if (!xmstat) {
                        if (logfile && c != 127 && (c >= ' ' || c == '\n'))
                                putc(c, logfile);
                        putchar(c);
                        fflush(stdout);
                } else if (xmstat == 1) {
                        rcvdnak = c;
                        if (c == 6 && acknak == 4) {
                                fclose(xfile);
                                xfile = 0;
                                xmstat = 0;
                        }
                        if (c == 6)
                                printf("ACK\n");
                        if (c == 21)
                                printf("NAK\n");
                        if (c == 24) {
                                printf("CAN\n");
                                fclose(xfile);
                                xmstat = 0;
                                xfile = 0;
                        }
                } else {
                        if (xidx == 0 && c == 4) {
                                acknak = 4;
                                printf("EOT received, ");
                        }
                        xmbuf[xidx++] = c;
                        if (xidx == 132) {
                                sum = 0;
                                for (i = 3; i < 131; i++)
                                        sum = (sum + xmbuf[i]) & 255;
                                if (xmbuf[0] == 1 && xmbuf[1] == 255 - xmbuf[2]
                                                && sum == xmbuf[131])
                                        acknak = 6;
                                else
                                        acknak = 21;
                                printf("Block %3d received, ", xmbuf[1]);
                                if (blocknum == xmbuf[1]) {
                                        blocknum = (blocknum + 1) & 255;
                                        fwrite(xmbuf + 3, 1, 128, xfile);
                                }
                                xidx = 0;
                        }
                }
        } else if ((a&0xf0) == 0x10) { /* timer */
             do_timer(a,c);
        } else if ((a&0xf0) == 0x20) { /* disk */
             do_disk(a,c);
        }
}

void restore_term(void) {
        tcsetattr(0, TCSAFLUSH, &termsetting);
        fcntl(0, F_SETFL, tflags);
        signal(SIGALRM, SIG_IGN);
}

void do_exit(void) {
        restore_term();
        exit(0);
}

void do_timer(int a, int c) {
   struct itimerval timercontrol;
   if (a==0x10 && c==0x8f) {
        timercontrol.it_interval.tv_sec = 0;
        timercontrol.it_interval.tv_usec = 20000;
        timercontrol.it_value.tv_sec = 0;
        timercontrol.it_value.tv_usec = 20000;
        setitimer(ITIMER_REAL, &timercontrol, NULL);
   } else if (a==0x10 && c==0x80) {
        timercontrol.it_interval.tv_sec = 0;
        timercontrol.it_interval.tv_usec = 0;
        setitimer(ITIMER_REAL, &timercontrol, NULL);
   } else if (a==0x10 && c==0x04) {
      time_t tm = time(0);
      struct tm *t = localtime(&tm);
      mem[IOPAGE+0x11] = t->tm_year;
      mem[IOPAGE+0x12] = t->tm_mon;
      mem[IOPAGE+0x13] = t->tm_mday;
      mem[IOPAGE+0x14] = t->tm_hour;
      mem[IOPAGE+0x15] = t->tm_min;
      mem[IOPAGE+0x16] = t->tm_sec;
   } else {
      mem[IOPAGE+a]=c;
   }
}

void do_disk(int a, int c) {
   if (a!=0x20) {
      mem[IOPAGE+a]=c;
      return;
   }
   int drv = mem[IOPAGE+0x21];
   int lsn = (mem[IOPAGE+0x22]<<16) + (mem[IOPAGE+0x23]<<8) + mem[IOPAGE+0x24];
   int buf = (mem[IOPAGE+0x25]<<8) + mem[IOPAGE+0x26];
   if (drv > 1 || disk[drv]==0) goto error;
   if (c==0x81) {
      if (lseek(fileno(disk[drv]),lsn*SECSIZE,SEEK_SET)==-1) goto error;
      if (read(fileno(disk[drv]),&mem[buf],SECSIZE)==-1) goto error;
   } else if (c==0x55) {
      if (lseek(fileno(disk[drv]),lsn*SECSIZE,SEEK_SET)==-1) goto error;
      if (write(fileno(disk[drv]),&mem[buf],SECSIZE)==-1) goto error;
   }
   mem[IOPAGE+0x20] = 0;
   return;
error :
   mem[IOPAGE+0x20] = 0xff;
}

typedef struct bp {
  int address;
  int count;
  struct bp *next;
} BP, *BPTR;

BPTR breakpoint = 0;
int bpskip = 0;
int trskip = 0;
int stkskip = 0;

int getarg(char *buf, char** next) {
        return strtol(buf,(char**)next,0);
}

void printhelp(void)
{
  printf( 
     "  s [count]  one step trace\n"
     "  n          step over\n"
     "  f          finish this call (until stack pop)\n"
     "  b [adr]    set break point\n"
     "  l          break point list\n"
     "  d [n]      delte break point list\n"
     "  c  [count] continue;\n"
     "  x  [adr]   dump\n"
     "  xi [adr]   disassemble\n"
     "  0  file    disk drive 0 image\n"
     "  1  file    disk drive 1 image\n"
     "  L  file    start log to file\n"
     "  S  file    set input file\n"
     "  X  exit\n"
     "  q  exit\n"
     "  U  file    upload from srecord file \n"
     "  D  file    download to srecord file \n"
     "  R  do reset\n"
     "  h,?  print this\n"
    );
}

void do_escape(void) {
        char s[80];
        int adr,skip;
        if (bpskip) { // skip unbreak instruction
            bpskip--;
            for(BPTR b = breakpoint; b ; b=b->next) {
                if (pcreg==b->address) {
                    if (b->count) b->count--;
                    if (b->count==0) {
                        goto restart0;
                    }
                }
            }
            return;
        }
        if (stkskip) { // skip until return
           if (sreg < stkskip ) return;
        }
restart0:
        stkskip = 0;
        restore_term();
        do_trace(stdout);
        if (trskip>1) { // show trace and step
            trskip--;
            set_term(escchar);
            return; 
        }
restart:
        printf("v09>");
        fgets(s, 80, stdin);
        if (s[0])
                s[strlen(s) - 1] = 0;
        switch (s[0]) {
        case 's':   // one step trace
                trskip = 1;
                if (s[1]) {
                   trskip = getarg(s+1,0);
                }
                bpskip = 0;
                attention = escape = 1;
                break;
        case 'n':   // step over
                stkskip = sreg;
                attention = escape = 1;
                break;
        case 'f':   // finish this call (until stack pop)
                stkskip = sreg + 2;
                attention = escape = 1;
                break;
        case 'b':   // set break point
                {
                  BPTR bp = calloc(1,sizeof(BP));
                  bp->next = breakpoint;
                  breakpoint = bp;
                  bp->count = 1;
                  if (s[1]) {
                     char *next;
                     bp->address = getarg(s+1,&next);
                     if (next[0]) {
                         bp->count = getarg(next,&next);
                     }
                  } else {
                     bp->address = pcreg;
                  }
                }
                bpskip = -1;
                goto restart;
        case 'l':   // break point list
                for(BPTR bp = breakpoint; bp ; bp = bp->next) {
                    printf("%x %i\n", bp->address, bp->count);
                }
                goto restart;
        case 'd':   // delte break point list
                if (s[1]) {
                   int trskip = getarg(s+1,0);
                   BPTR *prev = &breakpoint;
                   for(BPTR bp = breakpoint; bp ; bp = bp->next) {
                       if (trskip-- == 0) {
                          if (bp) {
                              *prev = bp->next;
                          }
                          break;
                       }
                       prev = &bp->next;
                   }
                }
                goto restart;
        case 'c':   // continue;
                bpskip = -1;
                attention = escape = 1;
                if (s[1]) {
                   bpskip = getarg(s+1,0);
                }
                break;
        case 'x':   // dump
                skip = 1;
                if (s[1]=='i') skip=2;
                if (s[skip]) {
                   char *next;
                   int adr = getarg(s+skip,&next);
                   int len = 32;
                   if (next[0]) {
                      len =  getarg(next,&next);
                   }
                   if (skip==2) {
                        disasm(adr,adr+len);
                   } else {
                     for(int i=0; len > 0 ; i+=16, len-=16) {
                        hexadump(mem+adr+i,len>16?16:len,adr+i,16);
                     }
                   }
                } else 
                   disasm(pcreg,pcreg+32);
                goto restart;
        case 'L':
                if (logfile)
                        fclose(logfile);
                logfile = 0;
                if (s[1]) {
                        int i=1; while(s[i]==' ') i++;
                        logfile = fopen(s + i, "w");
                }
                break;
        case 'S':
                if (infile)
                        fclose(infile);
                infile = 0;
                if (s[1]) {
                        int i=1; while(s[i]==' ') i++;
                        infile = fopen(s + i, "r");
                }
                break;
        case 'h':
        case '?':
                printhelp();
                goto restart;
        case 'X':
        case 'q':
                if (!xmstat)
                        do_exit();
                else {
                        xmstat = 0;
                        fclose(xfile);
                        xfile = 0;
                }
                break;
        case '0':
        case '1':
                {   FILE **drv = &disk[ s[0]-'0'] ;
                if (*drv)
                        fclose(*drv);
                *drv = 0;
                if (s[1]) {
                        int i=1; while(s[i]==' ') i++;
                        *drv = fopen(s + i, "r+b");
                        if ( *drv == 0 ) { printf("can't open %s\n", &s[i]); }
                }
                }
                break;
        case 'U':
                if (xfile)
                        fclose(xfile);
                xfile = 0;
                if (s[1]) {
                        int i=1; while(s[i]==' ') i++;
                        xfile = fopen(s + i, "rb");
                        if ( xfile == 0 ) { printf("can't open %s\n", &s[i]); }
                }
                if (xfile)
                        xmstat = 1;
                else
                        xmstat = 0;
                xidx = 0;
                acknak = 21;
                rcvdnak = EOF;
                blocknum = 1;
                break;
        case 'D':
                if (xfile)
                        fclose(xfile);
                xfile = 0;
                if (s[1]) {
                        int i=1; while(s[i]==' ') i++;
                        xfile = fopen(s + i, "wb");
                        if ( xfile == 0 ) { printf("can't open %s\n", &s[i]); }
                }
                if (xfile)
                        xmstat = 2;
                else
                        xmstat = 0;
                xidx = 0;
                acknak = 21;
                blocknum = 1;
                break;
        case 'R':
                pcreg = (mem[0xfffe] << 8) + mem[0xffff];
                break;
        }
        if (tracing||breakpoint||trskip||bpskip||stkskip) { attention = escape = 1; }
        else attention = 0;
        set_term(escchar);
}

void timehandler(int sig) {
        attention = 1;
        irq = 2;
        signal(SIGALRM, timehandler);
}

void handler(int sig) {
        escape = 1;
        attention = 1;
        bpskip = 0;
        stkskip = 0;
}

void set_term(char c) {
        struct termios newterm;
        struct itimerval timercontrol;
        signal(SIGQUIT, SIG_IGN);
        signal(SIGTSTP, SIG_IGN);
        signal(SIGINT, handler);
        signal(SIGUSR1, handler);
        tcgetattr(0, &termsetting);
        newterm = termsetting;
        newterm.c_iflag = newterm.c_iflag & ~INLCR & ~ICRNL;
        newterm.c_lflag = newterm.c_lflag & ~ECHO & ~ICANON;
        newterm.c_cc[VTIME] = 0;
        newterm.c_cc[VMIN] = 1;
        newterm.c_cc[VINTR] = escchar;
        tcsetattr(0, TCSAFLUSH, &newterm);
        tflags = fcntl(0, F_GETFL, 0);
        fcntl(0, F_SETFL, tflags | O_NDELAY); /* Make input from stdin non-blocking */
        signal(SIGALRM, timehandler);
        timercontrol.it_interval.tv_sec = 0;
        timercontrol.it_interval.tv_usec = 20000;
        timercontrol.it_value.tv_sec = 0;
        timercontrol.it_value.tv_usec = 20000;
        if (timer)
            setitimer(ITIMER_REAL, &timercontrol, NULL);
}