Minimal changes to get m6502 to compile (won't work)
[Apout.git] / v1trap.c
1 /* v1trap.c - Deal with 1st Edition trap instructions.
2  *
3  * $Revision: 1.16 $
4  * $Date: 2008/05/19 13:26:42 $
5  */
6 #ifdef EMUV1
7 #include "defines.h"
8 #include <sys/stat.h>
9 #include <sys/time.h>
10 #include <time.h>
11 #include <stdlib.h>
12 #include <unistd.h>
13 #include <fcntl.h>
14 #include <dirent.h>
15 #include <sys/wait.h>
16 #include <signal.h>
17 #include <termios.h>
18 #include <utime.h>
19 #include "v1trap.h"
20
21 #ifdef __linux__
22 #undef STREAM_BUFFERING         /* It seems to work */
23 #else
24 #define STREAM_BUFFERING        /* but not for Linux */
25 #endif
26
27
28 /* Forward prototypes */
29 #ifdef __STDC__
30 #define P(s) s
31 #else
32 #define P(s) ()
33 #endif
34 static int v1trap_exec P((void));
35 static int v1open_dir P((char *name));
36 static u_int32_t sectosixty P((time_t tim));
37
38 #undef P
39
40
41 /* V1 keeps some of the arguments to syscalls in registers, and some
42  * after the `sys' instruction itself. The list below gives the number
43  * of words, and the number in registers.
44  */
45 struct v1sysent {
46     int nwords;
47     int nregs;
48 };
49 static struct v1sysent v1arg[] = {
50     {0, 0}, {0, 0}, {0, 0}, {3, 1}, {3, 1}, {2, 0}, {1, 1}, {1, 1},
51     {2, 0}, {2, 0}, {1, 0}, {2, 0}, {1, 0}, {0, 0}, {2, 0}, {2, 0},
52     {2, 0}, {1, 0}, {2, 0}, {3, 1}, {3, 1}, {2, 0}, {1, 0}, {1, 1},
53     {1, 1}, {0, 0}, {1, 0}, {1, 0}, {2, 1}, {1, 0}, {1, 0}, {2, 1},
54     {2, 1}, {1, 0}
55 };
56
57 /* Seeks on files in /dev are done in 512-byte blocks, not bytes.
58  * If a fd's entry in the following table is 1, then it's a device
59  * and not a file.
60  */
61 int8_t isdev[NFILE] = {
62     0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
63     0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
64 };
65
66 static arglist V1A;
67
68 void v1trap()
69 {
70     extern int32_t AC, MQ;      /* in ke11a.c */
71     int i, mode, pid;
72     int status, exitval, errval;        /* used in V2 wait */
73     int whence;
74     u_int16_t argbase;
75     int trapnum;
76     long larg;
77     char *buf, *buf2;
78     char *fmode;                /* used with fdopen only */
79
80     struct stat stbuf;          /* used in STAT */
81     struct tr_v1stat *t1;       /* used in STAT */
82     struct timeval tval[2];     /* used in SMTIME */
83
84
85
86     /* Work out the actual trap number, and */
87     /* shift the PC up past any arguments */
88     /* to the syscall. Calculate base of args */
89     trapnum = ir & 077;
90     argbase = regs[PC];
91     regs[PC] += 2 * (v1arg[trapnum].nwords - v1arg[trapnum].nregs);
92
93     /* Move arguments into V1A so we can use them */
94     for (i = 0; i < v1arg[trapnum].nregs; i++)
95         V1A.uarg[i] = regs[i];
96     for (; i < v1arg[trapnum].nwords; i++, argbase += 2)
97         ll_word(argbase, V1A.uarg[i]);
98
99     TrapDebug((dbg_file, "pid %d %s: ", (int) getpid(),
100                v1trap_name[trapnum]));
101
102     switch (trapnum) {
103
104     /* XXX STILL TO DO: V1_GTTY, V1_STTY, V1_TELL */
105
106     /* These syscalls are ignored, and return */
107     /* with no effect on the caller */
108     case V1_BREAK:
109     case V1_CEMT:
110     case V1_ILGINS:
111     case V1_INTR:
112     case V1_QUIT:
113     case V1_RELE:
114         return;
115
116     /* These syscalls are not implemented, and */
117     /* always return no error to the caller */
118     case V1_GTTY:
119     case V1_STTY:
120         i = 0;
121         break;
122     /* These syscalls are not implemented, and */
123     /* always return an error to the caller */
124     case V1_MOUNT:
125     case V1_UMOUNT:
126         i = -1;
127         break;
128
129     case V1_EXIT:
130         if (Binary == IS_V1)
131             exit(0);
132         if (Binary == IS_V2) {
133             exitval = regs[0] & 0xff;
134             if (regs[PC] == 16790)
135                 exitval = 0;    /* s2-tape /bin/as doesn't set r0 */
136             TrapDebug((dbg_file, " with exitval %d\n", exitval));
137             exit(exitval);
138         }
139         i = -1;
140         break;
141
142 #define EPOCH71 31536000        /* # seconds from 1970 to 1971 */
143 #define EPOCH72 63072000        /* # seconds from 1970 to 1972 */
144     case V1_SMDATE:
145         buf = xlate_filename((char *) &dspace[uarg1]);
146         if (buf[0] == '\0')
147             buf = ".";  /* Not documented anywhere */
148         if (uarg1 == 0)
149             buf = ".";  /* Who knows? for V1 */
150         i = stat(buf, &stbuf);
151         TrapDebug((dbg_file, " on %s (stat %d) ", buf, i));
152         if (i == -1)
153             break;
154
155         /* Copy access time to preserve it */
156         tval[0].tv_sec = stbuf.st_atime;
157         tval[0].tv_usec = 0;
158         larg = (AC << 16) | (MQ & 0xffff);      /* Get mod time in 60ths of a second */
159         TrapDebug((dbg_file, " %ld -> ", larg));
160         larg = larg / 60 + EPOCH72;     /* Convert to seconds since 1970 */
161         TrapDebug((dbg_file, " 0x%lx ", larg));
162         tval[1].tv_sec = larg;
163         tval[1].tv_usec = 0;
164         i = utimes(buf, tval);
165         TrapDebug((dbg_file, " and %d for utimes ", i));
166         break;
167
168     case V1_TIME:
169         /* Kludge: treat start of this year as the epoch. */
170         /* Find #seconds from yearstart to now, multiply */
171         /* by 60 so as to be in V1 units */
172         larg = sectosixty(time(NULL));
173         MQ = (int) larg & 0xffff;
174         AC = ((int) larg >> 16) & 0xffff;
175         i = 0;
176         break;
177     case V1_SEEK:
178         /* Work out the args before we do the lseek */
179         whence = uarg3;
180         switch (uarg3) {
181         case 0:
182             larg = uarg2;
183             break;
184         case 1:
185         case 2:
186             larg = sarg2;
187             break;
188         }
189
190         if (ValidFD(sarg1) && isdev[sarg1])
191             larg *= 512;
192
193 #ifdef STREAM_BUFFERING
194         if (ValidFD(sarg1) && stream[sarg1]) {
195             i = fseek(stream[sarg1], larg, whence);
196             if (i == 0)
197                 i = ftell(stream[sarg1]);
198         } else
199 #endif
200             i = lseek(sarg1, larg, whence);
201
202         TrapDebug((dbg_file, " on fd %d amt %ld whence %d return %d ",
203                    sarg1, larg, whence, i));
204         if (i != -1)
205             i = 0;
206         regs[0] = i;
207         break;
208     case V1_READ:
209         buf = (char *) &dspace[uarg2];
210 #ifdef STREAM_BUFFERING
211         if (ValidFD(sarg1) && stream[sarg1])
212             i = fread(buf, 1, sarg3, stream[sarg1]);
213         else
214 #endif
215             i = read(sarg1, buf, sarg3);
216         TrapDebug((dbg_file, " on fd %d return %d ", sarg1, i));
217         regs[0] = i;
218         break;
219     case V1_LINK:
220         buf = xlate_filename((char *) &dspace[uarg1]);
221         buf2 = xlate_filename((char *) &dspace[uarg2]);
222         i = link(buf, buf2);
223         regs[0] = i;
224         break;
225     case V1_WRITE:
226         buf = (char *) &dspace[uarg2];
227 #ifdef STREAM_BUFFERING
228         if (ValidFD(sarg1) && stream[sarg1])
229             i = fwrite(buf, 1, sarg3, stream[sarg1]);
230         else
231 #endif
232             i = write(sarg1, buf, sarg3);
233         TrapDebug((dbg_file, " on fd %d return %d ", sarg1, i));
234         regs[0] = i;
235         break;
236     case V1_CLOSE:
237 #ifdef STREAM_BUFFERING
238         if (ValidFD(sarg1) && stream[sarg1]) {
239             i = fclose(stream[sarg1]);
240             stream[sarg1] = NULL;
241         } else
242 #endif
243             i = close(sarg1);
244         if ((i == 0) && ValidFD(sarg1))
245             isdev[sarg1] = 0;
246         TrapDebug((dbg_file, " on fd %d return %d ", sarg1, i));
247         break;
248     case V1_STAT:
249         buf = xlate_filename((char *) &dspace[uarg1]);
250         if (buf[0] == '\0')
251             buf = ".";  /* Not documented anywhere */
252         if (uarg1 == 0)
253             buf = ".";  /* Who knows? for V1 */
254         buf2 = (char *) &dspace[uarg2];
255         i = stat(buf, &stbuf);
256         TrapDebug((dbg_file, " on %s return %d ", buf, i));
257         goto dostat;
258     case V1_FSTAT:
259         buf2 = (char *) &dspace[uarg2];
260         i = fstat(sarg1, &stbuf);
261         TrapDebug((dbg_file, " on fd %d return %d ", sarg1, i));
262
263 dostat:
264         if (i == -1)
265             break;
266         t1 = (struct tr_v1stat *) buf2;
267         /* Inode numbers <41 are reserved for */
268         /* device files. Ensure we don't use them */
269         t1->inum = stbuf.st_ino & 0x7fff;
270         if (t1->inum < 41)
271             t1->inum += 100;
272         t1->inl = stbuf.st_nlink;
273         t1->iuid = stbuf.st_uid;
274         t1->isize = (u_int16_t) (stbuf.st_size & 0xffff);
275         t1->iflags = (u_int16_t) (V1_ST_USED | V1_ST_MODIFIED);
276         if (stbuf.st_size > 4095)
277             t1->iflags |= V1_ST_LARGE;
278         if (stbuf.st_mode & S_IFDIR)
279             t1->iflags |= V1_ST_ISDIR;
280         if (stbuf.st_mode & S_ISUID)
281             t1->iflags |= V1_ST_SETUID;
282         if (stbuf.st_mode & S_IXUSR)
283             t1->iflags |= V1_ST_EXEC;
284         if (stbuf.st_mode & S_IRUSR)
285             t1->iflags |= V1_ST_OWNREAD;
286         if (stbuf.st_mode & S_IWUSR)
287             t1->iflags |= V1_ST_OWNWRITE;
288         if (stbuf.st_mode & S_IROTH)
289             t1->iflags |= V1_ST_WRLDREAD;
290         if (stbuf.st_mode & S_IWOTH)
291             t1->iflags |= V1_ST_WRLDWRITE;
292
293         larg = sectosixty(stbuf.st_ctime);
294         copylong(t1->ctime, larg);
295         larg = sectosixty(stbuf.st_mtime);
296         copylong(t1->mtime, larg);
297         break;
298     case V1_UNLINK:
299         buf = xlate_filename((char *) &dspace[uarg1]);
300         i = unlink(buf);
301         break;
302     case V1_OPEN:
303         buf = xlate_filename((char *) &dspace[uarg1]);
304
305         i = stat(buf, &stbuf);  /* If file is a directory */
306         if (i == 0 && (stbuf.st_mode & S_IFDIR)) {
307             i = v1open_dir(buf);
308             fmode = "w+";
309             TrapDebug((dbg_file, "(dir) on %s return %d ", buf, i));
310         } else {
311             switch (sarg2) {
312             case 0:
313                 sarg2 = O_RDONLY;
314                 fmode = "r";
315                 break;
316             case 1:
317                 sarg2 = O_WRONLY;
318                 fmode = "w";
319                 break;
320             default:
321                 sarg2 = O_RDWR;
322                 fmode = "w+";
323                 break;
324             }
325             i = open(buf, sarg2);
326             TrapDebug((dbg_file, " on %s return %d ", buf, i));
327         }
328         regs[0] = i;
329
330         if (ValidFD(i)
331                 && !strncmp((char *) &dspace[uarg1], "/dev/", 5)) {
332             TrapDebug((dbg_file, " (device file) "));
333             isdev[i] = 1;
334         }
335 #ifdef STREAM_BUFFERING
336         if (i == -1)
337             break;
338 #if 0
339         /* Now get its stream pointer if possible */
340         /* Can someone explain why fdopen doesn't work for O_RDWR? */
341         if (ValidFD(i) && !isatty(i) && (sarg2 != O_RDWR)) {
342             stream[i] = fdopen(i, fmode);
343             streammode[i] = fmode;
344         }
345 #endif
346         stream[i] = fdopen(i, fmode);
347         streammode[i] = fmode;
348 #endif
349         break;
350     case V1_CHMOD:
351         buf = xlate_filename((char *) &dspace[uarg1]);
352         mode = 0;
353         if (uarg2 & V1_ST_SETUID)
354             mode |= S_ISUID;
355         if (uarg2 & V1_ST_EXEC)
356             mode |= S_IXUSR | S_IXGRP | S_IXOTH;
357         if (uarg2 & V1_ST_OWNREAD)
358             mode |= S_IRUSR;
359         if (uarg2 & V1_ST_OWNWRITE)
360             mode |= S_IWUSR;
361         if (uarg2 & V1_ST_WRLDREAD)
362             mode |= S_IRGRP | S_IROTH;
363         if (uarg2 & V1_ST_WRLDWRITE)
364             mode |= S_IWGRP | S_IWOTH;
365         i = chmod(buf, mode);
366         break;
367     case V1_MKDIR:
368         buf = xlate_filename((char *) &dspace[uarg1]);
369         mode = 0;
370         if (uarg2 & V1_ST_SETUID)
371             mode |= S_ISUID;
372         if (uarg2 & V1_ST_EXEC)
373             mode |= S_IXUSR | S_IXGRP | S_IXOTH;
374         if (uarg2 & V1_ST_OWNREAD)
375             mode |= S_IRUSR;
376         if (uarg2 & V1_ST_OWNWRITE)
377             mode |= S_IWUSR;
378         if (uarg2 & V1_ST_WRLDREAD)
379             mode |= S_IRGRP | S_IROTH;
380         if (uarg2 & V1_ST_WRLDWRITE)
381             mode |= S_IWGRP | S_IWOTH;
382         i = mkdir(buf, mode);
383         break;
384     case V1_CHOWN:
385         buf = xlate_filename((char *) &dspace[uarg1]);
386         uarg2 &= 0x3fff;        /* Why are uids > 16384? */
387         i = chown(buf, uarg2, 0);
388         TrapDebug((dbg_file, " %d on %s return %d", uarg2, buf, i));
389         break;
390     case V1_CHDIR:
391         buf = xlate_filename((char *) &dspace[uarg1]);
392         i = chdir(buf);
393         break;
394     case V1_CREAT:
395         buf = xlate_filename((char *) &dspace[uarg1]);
396         mode = 0;
397         if (uarg2 & V1_ST_SETUID)
398             mode |= S_ISUID;
399         if (uarg2 & V1_ST_EXEC)
400             mode |= S_IXUSR | S_IXGRP | S_IXOTH;
401         if (uarg2 & V1_ST_OWNREAD)
402             mode |= S_IRUSR;
403         if (uarg2 & V1_ST_OWNWRITE)
404             mode |= S_IWUSR;
405         if (uarg2 & V1_ST_WRLDREAD)
406             mode |= S_IRGRP | S_IROTH;
407         if (uarg2 & V1_ST_WRLDWRITE)
408             mode |= S_IWGRP | S_IWOTH;
409         i = creat(buf, mode);
410         TrapDebug((dbg_file, " on %s return %d ", buf, i));
411 #ifdef STREAM_BUFFERING
412         if (ValidFD(i)) {
413             stream[i] = fdopen(i, "w");
414             streammode[i] = "w";
415         }
416 #endif
417         regs[0] = i;
418         break;
419     case V1_EXEC:
420         i = v1trap_exec();
421         break;
422     case V1_WAIT:
423         i = wait(&status);
424         if (Binary == IS_V1)
425             break;
426         /* 2nd Edition wait is different */
427         regs[0] = i;    /* Save pid found in r0 */
428         if (i == -1) {
429             MQ = 0;
430             break;
431         }
432         exitval = WEXITSTATUS(status);
433         TrapDebug((dbg_file, "exitval %d ", exitval));
434         errval = 0;
435         if (WIFSIGNALED(status)) {
436             switch (WTERMSIG(status)) {
437             case SIGBUS:
438                 errval = 1;
439                 break;
440             case SIGTRAP:
441                 errval = 2;
442                 break;
443             case SIGILL:
444                 errval = 3;
445                 break;
446             case SIGIOT:
447                 errval = 4;
448                 break;
449             case SIGEMT:
450                 errval = 6;
451                 break;
452             case SIGQUIT:
453                 errval = 8;
454                 break;
455             case SIGINT:
456                 errval = 9;
457                 break;
458             case SIGKILL:
459                 errval = 10;
460                 break;
461             }
462             if (WCOREDUMP(status))
463                 errval += 16;
464         }
465         TrapDebug((dbg_file, "errval %d ", errval));
466         MQ = (exitval & 0xff) | (errval << 16);
467         TrapDebug((dbg_file, "v2 return pid is %d, MQ is 0x%x ", i,
468                    MQ));
469         break;
470     case V1_FORK:
471         pid = getpid();
472         i = fork();
473         switch (i) {
474         /* Error, inform the parent */
475         case -1:
476             break;
477         /* Child gets ppid in r0 */
478         case 0:
479             i = pid;
480             break;
481         /* Parent: Skip child `bf', pid into r0 */
482         default:
483             regs[PC] += 2;
484             if (Binary == IS_V2)
485                 regs[0] = i;
486         }
487         break;
488     case V1_GETUID:
489         i = getuid();
490         break;
491         regs[0] = i;
492     case V1_SETUID:
493         i = setuid(sarg1);
494         break;
495     default:
496         if (trapnum > V1_ILGINS) {
497             fprintf(stderr, "Apout - unknown syscall %d at PC 0%o\n",
498                     trapnum, regs[PC]);
499         } else {
500             fprintf(stderr,
501                     "Apout - the %s syscall is not yet implemented\n",
502                     v1trap_name[trapnum]);
503         }
504         exit(1);
505     }
506
507     /* Clear C bit if no error, or */
508     /* set C bit as there was an error */
509
510     if (i == -1) {
511         SET_CC_C();
512         TrapDebug((dbg_file, "errno is %s\n", strerror(errno)));
513     } else {
514         CLR_CC_C();
515         TrapDebug((dbg_file, "return %d\n", i));
516     }
517 #ifdef DEBUG
518     fflush(dbg_file);
519 #endif
520     return;
521 }
522
523
524 static int v1trap_exec(void)
525 {
526     u_int16_t cptr, cptr2;
527     char *buf, *name, *origpath;
528
529     origpath = strdup((char *) &dspace[uarg1]);
530     name = xlate_filename(origpath);
531     TrapDebug((dbg_file, "%s Execing %s ", progname, name));
532
533     cptr = uarg2;
534
535     Argc = 0;
536     while (Argc < MAX_ARGS) {
537         ll_word(cptr, cptr2);
538         if (cptr2 == 0)
539             break;
540         buf = (char *) &dspace[cptr2];
541         Argv[Argc++] = strdup(buf);
542         cptr += 2;
543         TrapDebug((dbg_file, "%s ", buf));
544     }
545     Argv[Argc] = NULL;
546     TrapDebug((dbg_file, "\n"));
547
548     if (load_a_out(name, origpath, 0) == -1) {
549         for (Argc--; Argc >= 0; Argc--)
550             free(Argv[Argc]);
551         return (-1);
552     }
553     run();                      /* Ok, so it's recursive, I dislike setjmp */
554     return (0);
555 }
556
557 /* 1st Edition reads directories as if they were ordinary files.
558  * The solution is to read the directory entries, and build a
559  * real file, which is passed back to the open call.
560  * Limitation: 32-bit inode numbers are truncated to 16-bit ones.
561  */
562 static int v1open_dir(char *name)
563 {
564     DIR *d;
565     char *tmpname;
566     int i;
567     struct dirent *dent;
568
569     struct v1_direct {
570         int16_t d_ino;
571         int8_t d_name[8];
572     } v1dent;
573
574     d = opendir(name);
575     if (d == NULL)
576         return (-1);
577     tmpname = strdup(TMP_PLATE);
578     i = mkstemp(tmpname);
579     if (i == -1) {
580         fprintf(stderr, "Apout - open_dir couldn't open %s\n", tmpname);
581         exit(1);
582     }
583     unlink(tmpname);
584     free(tmpname);
585
586     while ((dent = readdir(d)) != NULL) {
587         v1dent.d_ino = dent->d_fileno & 0x7fff;
588         if (v1dent.d_ino < 41)
589             v1dent.d_ino += 100;
590         strncpy((char *) v1dent.d_name, dent->d_name, 8);
591         write(i, &v1dent, 10);
592     }
593     closedir(d);
594     lseek(i, 0, SEEK_SET);
595     return (i);
596 }
597
598 /* Given a time, work out the number of 1/60ths of seconds since
599  * the start of that time's year
600  */
601 u_int32_t sectosixty(time_t tim)
602 {
603     time_t epoch;
604     u_int32_t diff;
605     struct tm *T;
606
607     T = gmtime(&tim);
608     T->tm_sec = T->tm_min = T->tm_hour = T->tm_mon = 0;
609     T->tm_mday = 1;
610
611     epoch = timegm(T);          /* Find time at start of year */
612     diff = 60 * (tim - epoch);
613     if (diff > 0x71172C00) {
614         fprintf(stderr, "Apout - V1 sectosixty too big by %d\n",
615                 diff - 0x71172C00);
616     }
617     return (diff);
618 }
619 #endif                          /* EMUV1 */