├── .hgignore ├── README.macos_launchd ├── .gitignore ├── util ├── makefile ├── intsize.c ├── istext.h ├── amlcout.py ├── strip8.c ├── smad.py ├── smag.py ├── cmraw.c ├── utextp.c ├── untap_vin.c ├── mtread.c ├── untap.c ├── mtwrite.c ├── untap16.c ├── emlink.c ├── ptextu.c ├── istext.c ├── magsav.c └── magrst.c ├── .editorconfig ├── LICENSE ├── .github └── workflows │ ├── macbuild.yml │ ├── linuxbuild.yml │ └── freebsdbuild.yml ├── swap.h ├── em19.launchd ├── makefile ├── geom.h ├── ea32i.h ├── notes.2012 ├── README.md ├── ea64v.h ├── tm.note ├── em.1 ├── regs.h ├── fp.h └── dispatch.h /.hgignore: -------------------------------------------------------------------------------- 1 | # use glob syntax. 2 | syntax: glob 3 | 4 | *~ 5 | -------------------------------------------------------------------------------- /README.macos_launchd: -------------------------------------------------------------------------------- 1 | Sample startup for the emulator on MacOS 2 | 3 | Customize the em19.launchd file, place it in /Library/LaunchDaemons as 4 | com.prirun.em19, reboot, and the emulator should start automagically. 5 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *~ 2 | *.o 3 | *.log 4 | *.swp 5 | tags 6 | cmraw 7 | cscope.* 8 | em 9 | emlink 10 | intsize 11 | magrst 12 | magsav 13 | mtread 14 | mtwrite 15 | ptextu 16 | strip8 17 | untap 18 | untap16 19 | untap_vin 20 | utextp 21 | -------------------------------------------------------------------------------- /util/makefile: -------------------------------------------------------------------------------- 1 | default: emlink intsize magrst magsav mtread mtwrite ptextu strip8 \ 2 | untap untap16 untap_vin utextp 3 | 4 | # Unix version of Prime's magrst 5 | magrst: magrst.c istext.c 6 | 7 | # Unix version of Prime's magsav 8 | magsav: magsav.c istext.c 9 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # .editorconfig, Boone, 02/11/21 2 | # Editor behavior settings 3 | 4 | root = true 5 | 6 | [*] 7 | charset = utf-8 8 | end_of_line = lf 9 | indent_size = 2 10 | indent_style = space 11 | insert_final_newline = true 12 | trim_trailing_whitespace = true 13 | 14 | [makefile] 15 | indent_style = tab 16 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | This work is licensed under the Creative Commons Attribution-NonCommercial 2 | 4.0 International License. To view a copy of this license, visit 3 | http://creativecommons.org/licenses/by-nc/4.0/ or send a letter to 4 | Creative Commons, PO Box 1866, Mountain View, CA 94042, USA. 5 | 6 | For a commercial license, contact Jim Wilcoxson, prirun@gmail.com 7 | -------------------------------------------------------------------------------- /util/intsize.c: -------------------------------------------------------------------------------- 1 | /* show size of C integers */ 2 | 3 | #include 4 | 5 | main() { 6 | int i; 7 | short s; 8 | long l; 9 | long long ll; 10 | 11 | printf("size of int is %d\n", sizeof(i)); 12 | printf("size of short is %d\n", sizeof(s)); 13 | printf("size of long is %d\n", sizeof(l)); 14 | printf("size of long long is %d\n", sizeof(ll)); 15 | } 16 | -------------------------------------------------------------------------------- /util/istext.h: -------------------------------------------------------------------------------- 1 | /* istext.h, Jim Wilcoxson, April 5, 2007 2 | Text conversion routines for the Prime emulator. 3 | */ 4 | 5 | #define OBUFMAX 4096 6 | 7 | typedef struct { 8 | int oddbyte; /* true if next char written is in right byte */ 9 | int col; /* column # of next byte to be written, 0-based */ 10 | int spaces; /* # of held-back spaces */ 11 | unsigned char obuf[OBUFMAX]; 12 | } utextp_t; 13 | -------------------------------------------------------------------------------- /util/amlcout.py: -------------------------------------------------------------------------------- 1 | # Send bytes 0-0xff to Prime emulator to verify transparent connection 2 | # NOTE: Enable the printf at storech: in devamlc to perform the test 3 | 4 | import socket 5 | 6 | client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 7 | client_socket.connect(('localhost', 9000)) 8 | data = '' 9 | for i in xrange(256): 10 | data += chr(i) 11 | data += chr(255) 12 | client_socket.send(data) 13 | client_socket.close() 14 | -------------------------------------------------------------------------------- /util/strip8.c: -------------------------------------------------------------------------------- 1 | /* strip8.c, J. Wilcoxson, March 16, 2005 2 | Reads a Prime data file and strips the high bit to make text readable. 3 | 4 | NOTE: this program is not useful for production data, because Prime 5 | text files have a specific format. Use prtoux to convert Prime text 6 | files to Unix text files. 7 | */ 8 | 9 | #include 10 | 11 | main () { 12 | int ch; 13 | 14 | while ((ch = getchar()) != EOF) 15 | putchar(ch & 0x7f); 16 | } 17 | -------------------------------------------------------------------------------- /util/smad.py: -------------------------------------------------------------------------------- 1 | # decode a Prime pdev number on the command line 2 | 3 | import sys 4 | 5 | if len(sys.argv) < 2: 6 | raise Exception, 'Usage: smad ' 7 | 8 | pdev = int(sys.argv[1], 8) 9 | if not (pdev & 0x10): 10 | raise Exception, 'Not a valid pdev' 11 | cont = (pdev & 0xE0) >> 5 12 | 13 | print 'Controller %d @ \'%d' % (cont, [24, 26, 25, 22, 45, 27, 46, 23][cont]) 14 | print 'Unit %d' % ((pdev & 0xf) >> 1) 15 | print 'Head offset %d' % ((pdev >> 12) * 2) 16 | print 'Surfaces %d' % (((pdev >> 7) & 0x1E) + (pdev & 1)) 17 | -------------------------------------------------------------------------------- /.github/workflows/macbuild.yml: -------------------------------------------------------------------------------- 1 | name: macbuild 2 | 3 | on: workflow_dispatch 4 | 5 | jobs: 6 | build: 7 | runs-on: macos-latest 8 | steps: 9 | - uses: "actions/checkout@v2" 10 | - name: make 11 | run: make 12 | - uses: "marvinpinto/action-automatic-releases@latest" 13 | with: 14 | repo_token: "${{ secrets.GITHUB_TOKEN }}" 15 | automatic_release_tag: "latest" 16 | prerelease: false 17 | title: "Bleeding Edge Binary - MacOS" 18 | files: | 19 | LICENSE 20 | README.md 21 | em.1 22 | em 23 | -------------------------------------------------------------------------------- /.github/workflows/linuxbuild.yml: -------------------------------------------------------------------------------- 1 | name: linuxbuild 2 | 3 | on: workflow_dispatch 4 | 5 | jobs: 6 | build: 7 | runs-on: ubuntu-latest 8 | steps: 9 | - uses: "actions/checkout@v2" 10 | - name: make 11 | run: make 12 | - uses: "marvinpinto/action-automatic-releases@latest" 13 | with: 14 | repo_token: "${{ secrets.GITHUB_TOKEN }}" 15 | automatic_release_tag: "latest" 16 | prerelease: false 17 | title: "Bleeding Edge Binary - Ubuntu" 18 | files: | 19 | LICENSE 20 | README.md 21 | em.1 22 | em 23 | -------------------------------------------------------------------------------- /.github/workflows/freebsdbuild.yml: -------------------------------------------------------------------------------- 1 | name: freebsdbuild 2 | 3 | on: workflow_dispatch 4 | 5 | jobs: 6 | build: 7 | runs-on: macos-latest 8 | steps: 9 | - uses: "actions/checkout@v2" 10 | - uses: "vmactions/freebsd-vm@v0.1.3" 11 | with: 12 | usesh: true 13 | prepare: pkg install -y curl git 14 | run: | 15 | make 16 | - uses: "marvinpinto/action-automatic-releases@latest" 17 | with: 18 | repo_token: "${{ secrets.GITHUB_TOKEN }}" 19 | automatic_release_tag: "latest" 20 | prerelease: false 21 | title: "Bleeding Edge Binary - FreeBSD" 22 | files: | 23 | LICENSE 24 | README.md 25 | em.1 26 | em 27 | -------------------------------------------------------------------------------- /swap.h: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #define __LITTLE_ENDIAN__ 1 4 | 5 | #if !defined(__BIG_ENDIAN__) && !defined(__LITTLE_ENDIAN__) 6 | # error Either __BIG_ENDIAN__ or __LITTLE_ENDIAN__ must be defined. 7 | #endif 8 | 9 | #if defined(__BIG_ENDIAN__) && defined(__LITTLE_ENDIAN__) 10 | # error __BIG_ENDIAN__ and __LITTLE_ENDIAN__ are both defined. 11 | #endif 12 | 13 | #if defined(__LITTLE_ENDIAN__) 14 | inline uint16_t swap16 (uint16_t in) { 15 | return (in<<8) | (in>>8); 16 | } 17 | #define swap32(x) (uint32_t) (__builtin_bswap32(x)) 18 | #define swap64(x) (uint64_t) (__builtin_bswap64(x)) 19 | #else 20 | #define swap16(x) (uint16_t) (x) 21 | #define swap32(x) (uint32_t) (x) 22 | #define swap64(x) (uint64_t) (x) 23 | #endif 24 | -------------------------------------------------------------------------------- /util/smag.py: -------------------------------------------------------------------------------- 1 | # create Prime pdev numbers from prompts 2 | 3 | import sys 4 | caddr = [24, 26, 25, 22, 45, 27, 46, 23] 5 | 6 | def getn(prompt, valid): 7 | while 1: 8 | try: 9 | n = int(input(prompt + '? ')) 10 | except Exception: 11 | sys.exit(0) 12 | if valid(n): 13 | return n 14 | 15 | while 1: 16 | pdev = 0x10 17 | h = getn('Head offset (0-30)', lambda h: 0 <= h <= 30 and not h & 1) 18 | pdev |= h >> 1 << 12 19 | s = getn('Surfaces (0-31)', lambda s: 0 <= s <= 31) 20 | pdev |= (s & 1) | ((s & 0x1e) << 7) 21 | c = getn('Controller address %s' % repr(caddr), 22 | lambda c: c in caddr) 23 | pdev |= caddr.index(c) << 5 24 | u = getn('Unit (0-7)', lambda u: 0 <= u <= 7) 25 | pdev |= u << 1 26 | print 'Prime pdev \'%o' % pdev 27 | print 28 | -------------------------------------------------------------------------------- /em19.launchd: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | Label 6 | com.prirun.em19 7 | 8 | ServiceDescription 9 | Rev 19 Prime Emulator 10 | 11 | Disabled 12 | 13 | 14 | OnDemand 15 | 16 | 17 | LowPriorityIO 18 | 19 | 20 | UserName 21 | jim 22 | 23 | GroupName 24 | staff 25 | 26 | Program 27 | /Users/jim/prime/pub/runem 28 | 29 | WorkingDirectory 30 | /Users/jim/prime/pub/ 31 | 32 | ProgramArguments 33 | 34 | runem 35 | 19 36 | 8001 37 | 8101 38 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /util/cmraw.c: -------------------------------------------------------------------------------- 1 | /* Show approximate minimum clock resolution 2 | Derived from the example in the linux man page for clock_gettime() */ 3 | 4 | #define _XOPEN_SOURCE 600 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | static void displayClock(clockid_t clock, char *name) 12 | { 13 | struct timespec ts; 14 | time_t oldsec; 15 | long oldnsec; 16 | 17 | clock_gettime(clock, &ts); 18 | oldsec = ts.tv_sec; 19 | oldnsec = ts.tv_nsec; 20 | while (ts.tv_sec == oldsec && ts.tv_nsec == oldnsec) 21 | { 22 | if (clock_gettime(clock, &ts) == -1) { 23 | perror("clock_gettime"); 24 | exit(EXIT_FAILURE); 25 | } 26 | } 27 | printf("%-15s: %10ld sec %09ld nsec\n", name, 28 | (long) ts.tv_sec - oldsec, ts.tv_nsec - oldnsec); 29 | } 30 | 31 | int main(int argc, char *argv[]) 32 | { 33 | displayClock(CLOCK_MONOTONIC_RAW, "CLOCK_MONOTONIC_RAW"); 34 | exit(0); 35 | } 36 | -------------------------------------------------------------------------------- /makefile: -------------------------------------------------------------------------------- 1 | # makefile to create various emulator builds 2 | 3 | REV=${shell [ -d .hg ] && hg id -n || git rev-parse --short HEAD} 4 | 5 | all_deps = makefile 6 | 7 | em_objs = em.o em 8 | em_deps = \ 9 | em.c regs.h emdev.h ea64v.h ea32i.h fp.h dispatch.h geom.h \ 10 | devpnc.h devamlc.h devsmlc.h swap.h 11 | 12 | CFLAGS = 13 | # Uncomment for building on SmartOS/Solaris: 14 | # CFLAGS += -lsocket -lnsl 15 | 16 | .PHONY: emwarn debug trace fixed 17 | 18 | # normal 19 | em: $(em_deps) $(all_deps) 20 | $(CC) -DREV=\"${REV}\" ${CFLAGS} -DNOTRACE -DFAST -O -Winline -Wno-return-type em.c -o em 21 | 22 | # lots of compiler warnings 23 | emwarn: $(em_deps) $(all_deps) 24 | $(CC) -DREV=\"${REV}\" ${CFLAGS} -DNOTRACE -DFAST -O -Wall -Wextra -pedantic -Wconversion em.c -o em 25 | 26 | # gdb 27 | debug: $(em_deps) $(all_deps) 28 | $(CC) -DREV=\"${REV}\" ${CFLAGS} -DNOTRACE -DFAST -g -O0 em.c -o em 29 | 30 | # tracing 31 | trace: $(em_deps) $(all_deps) 32 | $(CC) -DREV=\"${REV}\" ${CFLAGS} -DFAST -O em.c -o em 33 | 34 | # the fixed clock rate build is useful for making problems reproduceable. 35 | # 36 | # If the emulator crashes on a specific program, run it at the end of 37 | # PRIMOS.COMI to get a more consistent instruction count for the 38 | # failure, then enable tracing a little before that with -trace 39 | 40 | # fixed clock rate 41 | fixed: $(em_deps) $(all_deps) 42 | $(CC) -DREV=\"${REV}\" ${CFLAGS} -DFIXEDCLOCK -DNOIDLE -DFAST -O em.c -o em 43 | 44 | clean: 45 | rm -f $(em_objs) 46 | -------------------------------------------------------------------------------- /geom.h: -------------------------------------------------------------------------------- 1 | /* static structure for disk file suffixes and config data */ 2 | 3 | #define NUMGEOM 25 4 | #define MAXDRIVES 8 5 | #define MAXCTRL 8 6 | 7 | static struct { 8 | short model; 9 | char suffix[5]; 10 | short heads; 11 | short spt; 12 | short maxtrack; 13 | } geom[NUMGEOM] = { 14 | 1, "80M", 5, 9, 823, 15 | 1, "300M", 19, 9, 823, 16 | 2, "CMD", 21, 9, 823, 17 | 4, "68M", 3, 9, 1120, 18 | 5, "158M", 7, 9, 1120, 19 | 6, "160M", 10, 9, 822, 20 | 7, "675M", 40, 9, 842, 21 | 7, "600M", 40, 9, 842, 22 | 9, "315M", 19, 9, 823, /* MODEL_4475 */ 23 | 10, "84M", 5, 8, 1016, /* MODEL_4714 */ 24 | 11, "60M", 4, 7, 1020, /* MODEL_4711 */ 25 | 12, "120M", 8, 7, 1020, /* MODEL_4715 */ 26 | 13, "496M", 24, 14, 712, /* MODEL_4735 */ 27 | 14, "258M", 17, 6, 1221, /* MODEL_4719 */ 28 | 15, "770M", 23, 19, 850, /* MODEL_4845 */ 29 | 16, "1.1G", 27, 19, 1022, /* MODEL_4935 */ 30 | 17, "328A", 12, 8, 1641, /* MODEL_4721 */ 31 | 17, "328B", 31, 254, 20, /* MODEL_4721 (7210 SCSI controller) */ 32 | 18, "817M", 15, 19, 1381, /* MODEL_4860 */ 33 | 19, "673M", 31, 254, 42, /* MODEL_4729 */ 34 | 20, "213M", 31, 254, 14, /* MODEL_4730 */ 35 | 22, "421M", 31, 254, 26, /* MODEL_4731 */ 36 | 23, "1.3G", 31, 254, 82, /* MODEL_4732 */ 37 | 24, "1G", 31, 254, 65, /* MODEL_4734 */ 38 | 25, "2G", 31, 254, 122, /* MODEL_4736 */ 39 | }; 40 | -------------------------------------------------------------------------------- /util/utextp.c: -------------------------------------------------------------------------------- 1 | /* utextp.c, Jim Wilcoxson, March 16, 2005 2 | Reads a Unix text file and converts it to a compressed Prime text file. 3 | This means: 4 | - turn high bit on 5 | - expand Unix tabs to spaces 6 | - change multiple spaces to rcp (Prime text compression) 7 | - ignore carriage returns (Windoze) 8 | - ensure each lines begins on a 16-bit word boundary 9 | */ 10 | 11 | #include 12 | 13 | main () { 14 | int n,i,ch,col,space,nsp; 15 | 16 | n = 0; 17 | col = 0; 18 | space = 0; 19 | while ((ch=getchar()) != EOF) { 20 | if (ch == '\t') { /* expand Unix tabs */ 21 | nsp = 8 - (col & 7); 22 | space += nsp; 23 | col += nsp; 24 | } else if (ch == ' ') { 25 | space++; 26 | col++; 27 | } else if (ch == '\r') 28 | ; 29 | else { /* not a space character */ 30 | while (space) { /* dump held-back spaces first */ 31 | if (space < 3) { /* write regular spaces */ 32 | putchar(0240); 33 | space--; 34 | n++; 35 | } else { /* write compressed spaces */ 36 | putchar(0221); 37 | if (space > 255) /* limited to 255 spaces per compression */ 38 | nsp = 255; 39 | else 40 | nsp = space; 41 | putchar(nsp); 42 | n = n+2; 43 | space = space - nsp; 44 | } 45 | } 46 | putchar(ch | 0x80); /* write the text char w/parity */ 47 | col++; 48 | n++; 49 | if (ch == '\n') { 50 | col = 0; 51 | if (n&1) { /* Prime lines must start on a 16-bit word boundary */ 52 | putchar(0); 53 | n++; 54 | } 55 | } 56 | } 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /util/untap_vin.c: -------------------------------------------------------------------------------- 1 | /* untap.c, J. Wilcoxson, March 19, 2005 2 | Reads a tap magtape file and removes the tap information. 3 | tap reference: http://simh.trailing-edge.com/docs/simh_magtape.pdf 4 | */ 5 | 6 | #include 7 | #include 8 | #define BUFSIZE 128*1024 9 | 10 | /* in the tap format, record lengths are always stored little-endian */ 11 | 12 | int readint_le () { 13 | int n,ch; 14 | 15 | n = getchar() | getchar()<<8; 16 | if (feof(stdin)) { 17 | fprintf(stderr,"End of file reading record length, position = %d\n", ftell(stdin)); 18 | exit(0); 19 | } 20 | return n; 21 | } 22 | 23 | main (int argc, char** argv) { 24 | int fp; /* input file position, zero based */ 25 | int recno; /* record number, 1st record is 1 */ 26 | int reclen; /* length of record (bytes) */ 27 | int i,ch; 28 | int ones; /* count of held-back "all ones" bytes */ 29 | int allff; /* true if record was all 1 bits */ 30 | int verbose; 31 | unsigned char buf[BUFSIZE]; 32 | unsigned char bufhdr[5]; /* hack to display Prime 4-char tape hdr */ 33 | 34 | /* init */ 35 | 36 | recno = 0; 37 | ones = 0; 38 | fp = 0; 39 | verbose = 0; 40 | 41 | /* check args */ 42 | 43 | for (i=1; i= 1) 54 | fprintf(stderr,"End of file at record %d\n", recno); 55 | exit(0); 56 | } 57 | fp=ftell(stdin); 58 | 59 | reclen = readint_le(); 60 | if (reclen == 0) { /* end of medium */ 61 | if (verbose >= 1) 62 | fprintf(stderr,"Zero rec length at record %d, position %d\n", recno, fp); 63 | exit(0); 64 | } 65 | if (reclen == 0xFFFF) { /* tape mark */ 66 | if (verbose >= 1) 67 | fprintf(stderr,"Tape mark at record %d, position %d\n", recno, fp); 68 | continue; 69 | } 70 | if (reclen & 0x80000000) { /* error in record */ 71 | fprintf(stderr,"Record %d flagged as error, position %d\n", recno, fp); 72 | #if 0 73 | if (recno > 1) 74 | exit(1); 75 | #endif 76 | continue; 77 | } 78 | reclen = reclen & 0x00FFFFFF; 79 | if (reclen > BUFSIZE) { 80 | fprintf(stderr,"Record too big at record %d, position %d: increase BUFSIZE to %d\n", recno, fp, reclen); 81 | exit(1); 82 | } 83 | 84 | allff = 1; 85 | for (i=0; i= 2) { 105 | for (i=0; i<4; i++) 106 | bufhdr[i] = buf[i] & 0x7f; 107 | bufhdr[4] = 0; 108 | fprintf(stderr,"Record %d, position %d, length %d, hdr %s\n", recno, fp, reclen, bufhdr); 109 | } 110 | 111 | /* sometimes tap files end with a stream of 0xff bytes when there 112 | is no more data on the tape. To prevent writing this trailing 113 | stream of garbage, keep a count of records full of 0xff bytes 114 | and write them out when real data is seen again */ 115 | 116 | if (allff) { 117 | if (verbose >= 2) 118 | fprintf(stderr,"Record %d is all ones, position %d\n", recno, fp); 119 | ones += reclen; 120 | } else { 121 | while (ones-- > 0) 122 | putchar(0xff); 123 | ones = 0; 124 | for (i=0; i 10 | #include 11 | #include 12 | #include 13 | 14 | main (int argc, char **argv) { 15 | 16 | char *outfile; 17 | int tapefd, fdout; 18 | struct mtop mt_cmd; 19 | struct mtget mt_status; 20 | struct mtpos mt_pos; 21 | int filenr, relrecnr; 22 | int n,n2; 23 | int outpos; 24 | int mark; 25 | char buf[20000]; 26 | 27 | if (argc < 2) { 28 | printf("Usage: mtread \n"); 29 | exit(1); 30 | } 31 | 32 | outpos = 0; 33 | filenr = 1; 34 | relrecnr = 0; 35 | mark = 0; 36 | 37 | outfile = argv[1]; 38 | if ((fdout=open(outfile, O_WRONLY+O_CREAT+O_EXCL, 0770)) == -1) { 39 | perror("Error opening output file"); 40 | exit(1); 41 | } 42 | 43 | if ((tapefd=open("/dev/st0", O_RDONLY)) == -1) { 44 | perror("Error opening tape drive"); 45 | goto done; 46 | } 47 | 48 | #if 0 49 | 50 | /* set tape to do fast EOM. This can be useful if a tape is "loose" 51 | on the reel, to tighten up the tape */ 52 | 53 | mt_cmd.mt_op = MTSETDRVBUFFER; 54 | mt_cmd.mt_count = MT_ST_SETBOOLEANS | MT_ST_FAST_MTEOM; 55 | if (ioctl(tapefd, MTIOCTOP, &mt_cmd) != 0) { 56 | perror("Error setting fast EOM"); 57 | goto done; 58 | } 59 | 60 | /* skip to EOM */ 61 | 62 | mt_cmd.mt_op = MTEOM; 63 | mt_cmd.mt_count = 0; 64 | if (ioctl(tapefd, MTIOCTOP, &mt_cmd) != 0) { 65 | perror("Error skipping to EOM"); 66 | goto done; 67 | } 68 | #endif 69 | 70 | /* rewind tape */ 71 | 72 | #if 1 73 | mt_cmd.mt_op = MTREW; 74 | mt_cmd.mt_count = 0; 75 | if (ioctl(tapefd, MTIOCTOP, &mt_cmd) != 0) { 76 | perror("Error rewinding"); 77 | goto done; 78 | } 79 | #endif 80 | 81 | /* set tape blocksize to 0 */ 82 | 83 | mt_cmd.mt_op = MTSETDRVBUFFER; 84 | mt_cmd.mt_count = 0; 85 | if (ioctl(tapefd, MTIOCTOP, &mt_cmd) != 0) { 86 | perror("Error setting variable blocksize"); 87 | goto done; 88 | } 89 | 90 | while(1) { 91 | n=read(tapefd, buf+4, sizeof(buf)-8); 92 | relrecnr++; 93 | if (n == -1) { 94 | #if 1 95 | if (mark == 2) { 96 | printf("Read error after double tape mark, assuming tape EOF\n"); 97 | goto done; 98 | } 99 | #endif 100 | perror("Read error"); 101 | printf("File %d, record %d: read error\n", filenr, relrecnr); 102 | *(int *)buf = 0; 103 | buf[3] = 0x80; 104 | if (write(fdout, buf, 4) != 4) { 105 | perror("Error writing error mark"); 106 | goto done; 107 | } 108 | #if 0 109 | goto done; 110 | #else 111 | printf("Hit Enter to continue..."); 112 | getchar(); 113 | #endif 114 | } else if (n == 0) { 115 | printf("File mark %d at outpos %d\n", filenr, outpos); 116 | *(int *)buf = 0; 117 | if (write(fdout, buf, 4) != 4) { 118 | perror("Error writing file mark"); 119 | goto done; 120 | } 121 | mark += 1; 122 | filenr++; 123 | relrecnr = 0; 124 | } else { 125 | *(int *)buf = n; 126 | *(int *)(buf+n+4) = n; 127 | mark = 0; 128 | #if 0 129 | printf("File %d record %d outpos %d size %d\n", filenr, relrecnr, outpos, n); 130 | #endif 131 | if ((n2=write(fdout, buf, n+8)) != n+8) { 132 | if (n2 == -1) { 133 | perror("Error writing output file"); 134 | goto done; 135 | } 136 | printf("Error writing output file, read %d, wrote %d\n", n, n2); 137 | goto done; 138 | } 139 | outpos += n+8; 140 | } 141 | } 142 | 143 | done: 144 | if (outpos == 0) 145 | unlink(outfile); 146 | sleep(2); 147 | mt_cmd.mt_op = MTREW; 148 | mt_cmd.mt_count = 0; 149 | if (ioctl(tapefd, MTIOCTOP, &mt_cmd) != 0) { 150 | perror("Error rewinding"); 151 | } 152 | close(tapefd); 153 | exit(1); 154 | 155 | } 156 | -------------------------------------------------------------------------------- /ea32i.h: -------------------------------------------------------------------------------- 1 | #define IMM_EA 0x80000000 2 | 3 | static inline ea_t ea32i (ea_t earp, unsigned short inst, unsigned int *immu32, unsigned long long *immu64) { 4 | 5 | int tm, sr, br, ring; 6 | unsigned short d; 7 | ea_t ea, ip; 8 | 9 | *immu32 = 0xAAAAAAAA; 10 | *immu64 = 0xAAAAAAAAAAAAAAAALL; 11 | 12 | tm = (inst >> 5) & 3; 13 | sr = (inst >> 2) & 7; 14 | br = inst & 3; 15 | ring = RP & RINGMASK32; 16 | TRACE(T_EAI, " tm=%d, sr=%d, dr=%d, br=%d\n", tm, sr, (inst >> 7) & 7, br); 17 | 18 | switch (tm) { 19 | case 0: 20 | switch (br) { 21 | case 0: /* reg-reg */ 22 | *immu32 = getgr32(sr); 23 | return IMM_EA; 24 | 25 | case 1: 26 | d = iget16(RP); 27 | RPL++; 28 | if (sr == 0) /* imm type 1 */ 29 | *immu32 = d << 16; 30 | else /* imm type 2 */ 31 | *(int *)immu32 = *(short *)&d; 32 | return IMM_EA; 33 | 34 | case 2: 35 | switch (sr) { 36 | case 0: /* imm type 3 */ 37 | d = iget16(RP); 38 | INCRP; 39 | *immu64 = (((long long)(d & 0xFF00)) << 48) | (d & 0xFF); 40 | return IMM_EA; 41 | case 1: /* FAC0 source */ 42 | *immu64 = getgr64(FAC0); 43 | return IMM_EA; 44 | case 3: /* FAC1 source */ 45 | *immu64 = getgr64(FAC1); 46 | return IMM_EA; 47 | case 2: 48 | case 4: 49 | case 5: 50 | case 6: 51 | case 7: 52 | fault(UIIFAULT, RPL, RP); 53 | fatal("ea32i: return from UII fault!"); 54 | default: 55 | fatal("ea32i: sr < 0 or > 7?"); 56 | } 57 | fatal("ea32i: case tm=0 br=2 fall-through"); 58 | 59 | case 3: /* GR relative */ 60 | d = iget16(RP); 61 | INCRP; 62 | ea = (getgr32(sr) & 0xFFFF0000) | ((getgr32(sr) + d) & 0xFFFF); 63 | TRACE(T_EAI, " GRR, d=%x, [sr]=%o/%o, ea=%o/%o\n", d, getgr32(sr)>>16, getgr32(sr)&0xFFFF, ea>>16, ea&0xFFFF); 64 | if (ea & 0x80000000) 65 | fault(POINTERFAULT, ea>>16, ea); 66 | return ea | ring; 67 | 68 | default: 69 | fatal("ea32i: tm=0, br < 0 or > 3?"); 70 | } 71 | fatal("ea32i: tm=0 fall-through"); 72 | 73 | case 1: /* TM=1: Direct and Indexed */ 74 | d = iget16(RP); 75 | INCRP; 76 | if (sr == 0) 77 | ea = (getgr32(BR+br) & 0xFFFF0000) | ((getgr32(BR+br) + d) & 0xFFFF); 78 | else 79 | ea = (getgr32(BR+br) & 0xFFFF0000) | ((getgr32(BR+br) + d + getgr16(sr)) & 0xFFFF); 80 | return ea | ring; 81 | 82 | case 2: /* TM=2: Indirect and Indirect Preindexed */ 83 | d = iget16(RP); 84 | INCRP; 85 | if (sr == 0) 86 | ea = (getgr32(BR+br) & 0xFFFF0000) | ((getgr32(BR+br) + d) & 0xFFFF); 87 | else 88 | ea = (getgr32(BR+br) & 0xFFFF0000) | ((getgr32(BR+br) + d + getgr16(sr)) & 0xFFFF); 89 | ip = get32(ea | ring); 90 | if (ip & 0x80000000) 91 | fault(POINTERFAULT, ip>>16, ea); 92 | return ip | ring; 93 | 94 | case 3: /* TM=3: Indirect and Indirect Postindexed */ 95 | TRACE(T_EAI, " TM=3: Indirect [Postindexed]"); 96 | d = iget16(RP); 97 | INCRP; 98 | ea = (getgr32(BR+br) & 0xFFFF0000) | ((getgr32(BR+br) + d) & 0xFFFF); 99 | TRACE(T_EAI, " BR[%d]=%o/%o, d=%o, ip ea=%o/%o\n", br, getgr32(BR+br)>>16, getgr32(BR+br)&0xFFFF, d, ea>>16, ea&0xFFFF); 100 | ip = get32(ea | ring); 101 | TRACE(T_EAI, " after indirect, ea=%o/%o\n", ip>>16, ip&0xFFFF); 102 | if (ip & 0x80000000) 103 | fault(POINTERFAULT, ip>>16, ea); 104 | if (sr > 0) { 105 | ip = (ip & 0xFFFF0000) | ((ip + getgr16(sr)) & 0xFFFF); 106 | TRACE(T_EAI, " index by gr%d='%o/%d, ea=%o/%o\n", sr, getgr16(sr), getgr16(sr), ea>>16, ea&0xFFFF); 107 | } 108 | return ip | ring; 109 | 110 | default: 111 | fatal("ea32i: tm out of range!"); 112 | } 113 | fatal("ea32i: main switch fall through"); 114 | } 115 | -------------------------------------------------------------------------------- /util/untap.c: -------------------------------------------------------------------------------- 1 | /* untap.c, J. Wilcoxson, March 19, 2005 2 | Reads a tap magtape file and removes the tap information. 3 | tap reference: http://simh.trailing-edge.com/docs/simh_magtape.pdf 4 | */ 5 | 6 | #include 7 | #include 8 | #define BUFSIZE 128*1024 9 | 10 | /* in the tap format, record lengths are always stored little-endian */ 11 | 12 | int readint_le () { 13 | int n,ch; 14 | 15 | n = getchar() | getchar()<<8 | getchar()<<16 | getchar()<<24; 16 | if (feof(stdin)) { 17 | fprintf(stderr,"End of file reading record length, position = %d\n", ftell(stdin)); 18 | exit(0); 19 | } 20 | return n; 21 | } 22 | 23 | main (int argc, char** argv) { 24 | int fp; /* input file position, zero based */ 25 | int recno; /* record number, 1st record is 1 */ 26 | int reclen; /* length of record (bytes) */ 27 | int i,ch; 28 | int ones; /* count of held-back "all ones" bytes */ 29 | int allff; /* true if record was all 1 bits */ 30 | int verbose; 31 | unsigned char buf[BUFSIZE]; 32 | unsigned char bufhdr[5]; /* hack to display Prime 4-char tape hdr */ 33 | 34 | /* init */ 35 | 36 | recno = 0; 37 | ones = 0; 38 | fp = 0; 39 | verbose = 0; 40 | 41 | /* check args */ 42 | 43 | for (i=1; i= 1) 54 | fprintf(stderr,"End of file at record %d\n", recno); 55 | exit(0); 56 | } 57 | fp=ftell(stdin); 58 | 59 | reclen = readint_le(); 60 | if (reclen == 0xFFFFFFFF) { /* end of medium */ 61 | if (verbose >= 1) 62 | fprintf(stderr,"End of medium at record %d, position %d\n", recno, fp); 63 | exit(0); 64 | } 65 | if (reclen == 0) { /* tape mark */ 66 | if (verbose >= 1) 67 | fprintf(stderr,"Tape mark at record %d, position %d\n", recno, fp); 68 | continue; 69 | } 70 | if (reclen & 0x80000000) { /* error in record */ 71 | fprintf(stderr,"Record %d flagged as error, position %d\n", recno, fp); 72 | #if 0 73 | if (recno > 1) 74 | exit(1); 75 | #endif 76 | continue; 77 | } 78 | reclen = reclen & 0x00FFFFFF; 79 | if (reclen > BUFSIZE) { 80 | fprintf(stderr,"Record too big at record %d, position %d: increase BUFSIZE to %d\n", recno, fp, reclen); 81 | exit(1); 82 | } 83 | 84 | allff = 1; 85 | for (i=0; i= 2) { 105 | for (i=0; i<4; i++) 106 | bufhdr[i] = buf[i] & 0x7f; 107 | bufhdr[4] = 0; 108 | fprintf(stderr,"Record %d, position %d, length %d, hdr %s\n", recno, fp, reclen, bufhdr); 109 | } 110 | 111 | /* sometimes tap files end with a stream of 0xff bytes when there 112 | is no more data on the tape. To prevent writing this trailing 113 | stream of garbage, keep a count of records full of 0xff bytes 114 | and write them out when real data is seen again */ 115 | 116 | if (allff) { 117 | if (verbose >= 2) 118 | fprintf(stderr,"Record %d is all ones, position %d\n", recno, fp); 119 | ones += reclen; 120 | } else { 121 | while (ones-- > 0) 122 | putchar(0xff); 123 | ones = 0; 124 | for (i=0; i 6 | #include 7 | #include 8 | #include 9 | 10 | #define BUFSIZE 20000 11 | 12 | /* in the tap format, record lengths are always stored little-endian */ 13 | 14 | int readint_le () { 15 | int n,ch; 16 | 17 | n = getchar() | getchar()<<8 | getchar()<<16 | getchar()<<24; 18 | if (feof(stdin)) { 19 | fprintf(stderr,"End of file reading record length, position = %d\n", ftell(stdin)); 20 | exit(0); 21 | } 22 | return n; 23 | } 24 | 25 | main (int argc, char **argv) { 26 | 27 | char *infile; 28 | int tapefd, fdin; 29 | struct mtop mt_cmd; 30 | struct mtget mt_status; 31 | struct mtpos mt_pos; 32 | int filenr, relrecnr; 33 | int reclen, reclen2; 34 | int n,n2; 35 | int outpos; 36 | int mark; 37 | char buf[BUFSIZE]; 38 | 39 | filenr = 1; 40 | relrecnr = 0; 41 | mark = 0; 42 | 43 | if ((tapefd=open("/dev/st0", O_RDWR)) == -1) { 44 | perror("Error opening tape drive"); 45 | goto done; 46 | } 47 | 48 | /* set tape blocksize to 0 */ 49 | 50 | mt_cmd.mt_op = MTSETDRVBUFFER; 51 | mt_cmd.mt_count = 0; 52 | if (ioctl(tapefd, MTIOCTOP, &mt_cmd) != 0) { 53 | perror("Error setting variable blocksize"); 54 | goto done; 55 | } 56 | 57 | /* set buffering */ 58 | 59 | mt_cmd.mt_op = MTSETDRVBUFFER; 60 | mt_cmd.mt_count = 1; 61 | if (ioctl(tapefd, MTIOCTOP, &mt_cmd) != 0) { 62 | perror("Error setting buffering params"); 63 | goto done; 64 | } 65 | mt_cmd.mt_op = MTSETDRVBUFFER; 66 | mt_cmd.mt_count = 1; 67 | mt_cmd.mt_count |= MT_ST_BOOLEANS | MT_ST_BUFFER_WRITES | MT_ST_ASYNC_WRITES; 68 | if (ioctl(tapefd, MTIOCTOP, &mt_cmd) != 0) { 69 | perror("Error setting buffering params"); 70 | goto done; 71 | } 72 | 73 | #if 0 74 | /* set density: 1=800bpi; 2=1600bpi; 3=6250bpi */ 75 | 76 | mt_cmd.mt_op = MTSETDENSITY; 77 | mt_cmd.mt_count = 2; 78 | if (ioctl(tapefd, MTIOCTOP, &mt_cmd) != 0) { 79 | perror("Error setting variable blocksize"); 80 | goto done; 81 | } 82 | #endif 83 | 84 | while(1) { 85 | relrecnr++; 86 | reclen = readint_le(); 87 | if (reclen == 0xFFFFFFFF) /* end of medium */ 88 | goto done; 89 | else if (reclen & 0x80000000) { /* error in record */ 90 | printf("File %d, record %d contains error; writing zero record\n", filenr, relrecnr); 91 | *(int *)buf = 0; 92 | buf[0] = 4; 93 | *(int *)(buf+4) = 0; 94 | *(int *)(buf+8) = *(int *)buf; 95 | if (write(tapefd, buf, 12) != 12) { 96 | perror("Error writing error record"); 97 | goto done; 98 | } 99 | } else if (reclen == 0) { 100 | printf("File mark %d\n", filenr); 101 | mt_cmd.mt_op = MTWEOF; 102 | mt_cmd.mt_count = 1; 103 | if (ioctl(tapefd, MTIOCTOP, &mt_cmd) != 0) { 104 | perror("Error writing tape mark"); 105 | goto done; 106 | } 107 | filenr++; 108 | relrecnr = 0; 109 | } else { 110 | reclen = reclen & 0x00FFFFFF; 111 | if (reclen > BUFSIZE) { 112 | fprintf(stderr,"Record too big at record %d: increase BUFSIZE to %d\n", relrecnr, reclen); 113 | exit(1); 114 | } 115 | n=fread(buf, 1, reclen, stdin); 116 | if (n != reclen) { 117 | fprintf(stderr,"Read error at record %d, expected %d, got %d bytes\n", relrecnr, reclen, n); 118 | exit(1); 119 | } 120 | reclen2 = readint_le(); 121 | if (reclen2 != reclen) { 122 | fprintf(stderr,"Read error at record %d, expected trailing reclen %d, got %d\n", relrecnr, reclen, reclen2); 123 | exit(1); 124 | } 125 | if ((n2=write(tapefd, buf, reclen)) != reclen) { 126 | if (n2 == -1) { 127 | perror("Error writing tape device"); 128 | goto done; 129 | } 130 | printf("Error writing tape device, read %d, wrote %d\n", reclen, n2); 131 | goto done; 132 | } 133 | } 134 | } 135 | 136 | done: 137 | sleep(2); 138 | mt_cmd.mt_op = MTREW; 139 | mt_cmd.mt_count = 0; 140 | if (ioctl(tapefd, MTIOCTOP, &mt_cmd) != 0) { 141 | perror("Error rewinding"); 142 | } 143 | close(tapefd); 144 | exit(1); 145 | } 146 | -------------------------------------------------------------------------------- /notes.2012: -------------------------------------------------------------------------------- 1 | July 7, 2012 2 | Emulator Version 247 3 | 4 | Updates to the Prime Emulator since rev 102: 5 | 6 | - the emulator runs on both PowerPC and Intel CPU architectures. 7 | It cannot be built as a "Universal" program (runs on both Intel and 8 | PPC); there are separate PPC and Intel versions. 9 | 10 | - the format of the licensing information stored on the dongle has 11 | changed. It is possible to run either the old emulator or new 12 | emulator with the same dongle, but when switching between old and 13 | new, the machine must be connected to the Internet. 14 | 15 | - one copy of the emulator can run on one machine. 16 | 17 | - the dongle is updated as soon as the Prime starts. If your Prime 18 | is rebooting very often for some reason, like many times per hour, 19 | and is not connected to the Internet, it may deplete the dongle 20 | charge sooner than expected. Connect to the Internet if this 21 | happens and the dongle will recharge. 22 | 23 | - AMLC handling is much more efficient (uses less CPU time) and 24 | allocates resources more fairly among active AMLC lines 25 | 26 | - AMLC telnet negotiation has been fixed, so it's not necessary to use 27 | /NOWAIT with Kermit 28 | 29 | - AMLC baud rates have changed from: 30 | 1200, 2400, 4800, 9600, 19200, 38400, 57600, 115200 31 | to: 32 | 300, 1200, 9600, 19200, 9600*, 38400, 57600, 115200 33 | * this is the AMLCLK speed; the default is 9600 like a real Prime 34 | 35 | - AMLCLK can be used to set the 4th (starting at 0) baud rate, 36 | like on a real Prime; the default speed is 9600 37 | 38 | - the AMLC config filename has been changed from "amlc" to "amlc.cfg" 39 | 40 | - assigned AMLC lines can be used for TCP/IP printing in amlc.cfg. 41 | Here is an example config line in amlc.cfg: 42 | 010 192.168.1.1:9100 43 | This will connect AMLC line 8 (decimal) to this IP address & port. 44 | If the printer disconnects, the emulator will try to re-establish 45 | the connection the next time there is data to send. If DTR is 46 | dropped, for example by T$AMLC, the emulator will drop the connection. 47 | When DTR is raised, the emulator will re-establish the connection 48 | the next time there is data to send. 49 | 50 | - supports PNC emulation (Prime "RingNet") over TCP/IP 51 | 52 | - support added for old-style Prime T&M hardware diagnostics; 53 | new-style DIAG diagnostics already ran. The emulator passes 54 | nearly all CPU diagnostics. It is difficult to pass all Prime 55 | diagnostics because many of them expect certain microcode bugs 56 | to be present for certain models. 57 | 58 | - if a SIGTERM signal is received, the emulator will try to do a 59 | graceful Primos shutdown. This is useful when a UPS battery 60 | backup is used and the UPS is connected to a USB port: when 61 | the UPS battery gets low, OSX will send SIGTERM to all running 62 | processes. The emulator sees SIGTERM, passes it on to Primos, 63 | and Primos shuts down all disk drives. This prevents disk 64 | corruption that can occur when a Prime is abruptly halted. 65 | It is also possible to send SIGTERM with: kill -TERM n, where 66 | n is the emulator's process id. 67 | 68 | - hitting Ctrl \ on the system console will cause the emulator 69 | to shutdown Primos. If this doesn't work for some reason, a 70 | second Ctrl \ will cause an immediate halt. 71 | 72 | IMPORTANT NOTE: these last 2 features do not work with the faster, 73 | dedicated register PowerPC build, because signals trash the 74 | registers. 75 | 76 | - hitting Ctrl ] on the system console caused the old emulator 77 | to suspend, and "fg" would continue. But it changed the 78 | system console terminal settings. This key has been disabled. 79 | 80 | - the emulator prints a reminder to check error.log if it is 81 | not empty. Error and warning messages are written here. 82 | 83 | - indirect loops in address calculations are now limited like on a 84 | real Prime. Before, there was no limit, so it was possible to 85 | "hang" the emulator with a specially constructed indirect 86 | instruction. 87 | 88 | - previously, the -cpuid option took a number from 0 to 44 to 89 | indicate the CPU model. Now it also accepts model numbers 90 | like 750, 2250, 4050, 9950, etc. 91 | 92 | - Ctrl Y, Ctrl V, and Ctrl O work on the system console 93 | 94 | - several Prime disk drives had track limits that were off 95 | by 1 or 2. This could cause seek errors, especially when 96 | running Prime diagnostics. 97 | -------------------------------------------------------------------------------- /util/untap16.c: -------------------------------------------------------------------------------- 1 | /* untap.c, J. Wilcoxson, March 19, 2005 2 | Reads a tap magtape file and removes the tap information. 3 | tap reference: http://simh.trailing-edge.com/docs/simh_magtape.pdf 4 | NOTE: for this modified version, a 16-bit Prime word length is used 5 | to delimit records. 6 | */ 7 | 8 | #include 9 | #include 10 | #define BUFSIZE 16*1024 11 | 12 | /* reads a 16-bit, big-endian record length, which is the number of 16-bit 13 | words in the tape record. The upper 3 bits are apparently flag bits, 14 | and these are moved the the upper 3 bits of the 32-bit word */ 15 | 16 | int readint_be () { 17 | int n,ch; 18 | 19 | n = (getchar()<<8 | getchar()); 20 | n = ((n & 0xE000) << 16) | ((n & 0x1FFF) * 2); 21 | if (feof(stdin)) { 22 | fprintf(stderr,"End of file reading record length, position = %d\n", ftell(stdin)); 23 | exit(0); 24 | } 25 | return n; 26 | } 27 | 28 | main (int argc, char** argv) { 29 | int fp; /* input file position, zero based */ 30 | int recno; /* record number, 1st record is 1 */ 31 | int reclen; /* length of record (bytes) */ 32 | int flag1,flag2; /* leading & trailing flags */ 33 | int i,ch; 34 | int ones; /* count of held-back "all ones" bytes */ 35 | int allff; /* true if record was all 1 bits */ 36 | int verbose; 37 | unsigned char buf[BUFSIZE]; 38 | unsigned char bufhdr[5]; /* hack to display Prime 4-char tape hdr */ 39 | 40 | /* init */ 41 | 42 | recno = 0; 43 | ones = 0; 44 | fp = 0; 45 | verbose = 0; 46 | 47 | /* check args */ 48 | 49 | for (i=1; i= 1) 60 | fprintf(stderr,"End of file at record %d\n", recno); 61 | exit(0); 62 | } 63 | fp=ftell(stdin); 64 | 65 | reclen = readint_be(); 66 | if (reclen == 0xFFFFFFFF) { /* end of medium */ 67 | if (verbose >= 1) 68 | fprintf(stderr,"End of medium at record %d, position %d\n", recno, fp); 69 | exit(0); 70 | } 71 | 72 | if (reclen == 0) { /* tape mark */ 73 | if (verbose >= 1) 74 | fprintf(stderr,"Tape mark at record %d, position %d\n", recno, fp); 75 | continue; 76 | } 77 | flag1 = reclen & 0xF0000000; 78 | if (flag1 != 0) fprintf(stderr,"Leading flags are 0x%08x, position %d\n", flag1, fp); 79 | reclen = reclen & 0x0FFFFFFF; 80 | if (reclen > BUFSIZE) { 81 | fprintf(stderr,"Record too big at record %d, position %d: increase BUFSIZE to %d\n", recno, fp, reclen); 82 | exit(1); 83 | } 84 | 85 | allff = 1; 86 | for (i=0; i= 2) { 114 | for (i=0; i<4; i++) 115 | bufhdr[i] = buf[i] & 0x7f; 116 | bufhdr[4] = 0; 117 | fprintf(stderr,"Record %d, position %d, length %d, hdr %s\n", recno, fp, reclen, bufhdr); 118 | } 119 | 120 | /* sometimes tap files end with a stream of 0xff bytes when there 121 | is no more data on the tape. To prevent writing this trailing 122 | stream of garbage, keep a count of records full of 0xff bytes 123 | and write them out when real data is seen again */ 124 | 125 | if (allff) { 126 | if (verbose >= 2) 127 | fprintf(stderr,"Record %d is all ones, position %d\n", recno, fp); 128 | ones += reclen; 129 | } else { 130 | while (ones-- > 0) 131 | putchar(0xff); 132 | ones = 0; 133 | for (i=0; i 8 | */ 9 | 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | 21 | #define ESCAPE 035 22 | 23 | main (int argc, char **argv) { 24 | 25 | int port; 26 | int sockfd; 27 | 28 | int i; 29 | struct hostent *server; 30 | struct sockaddr_in addr; 31 | unsigned int addrlen; 32 | static struct termios terminfo,resetinfo; 33 | static fd_set fdread,fdexcp; 34 | struct timeval timeout; 35 | unsigned char ch; 36 | int ttydev; 37 | int ttyflags, newflags; 38 | int n,n2; 39 | #define BUFCHARS 16*1024 40 | unsigned char buf[BUFCHARS]; 41 | 42 | sockfd = socket(AF_INET, SOCK_STREAM, 0); 43 | if (sockfd == -1) { 44 | perror ("Unable to create socket"); 45 | exit(1); 46 | } 47 | sscanf(argv[2],"%d", &port); 48 | printf ("Connecting to %s port %d...\n", argv[1], port); 49 | 50 | server = gethostbyname(argv[1]); 51 | if (server == NULL) { 52 | fprintf(stderr,"ERROR, no such host\n"); 53 | exit(1); 54 | } 55 | bzero((char *) &addr, sizeof(addr)); 56 | addr.sin_family = AF_INET; 57 | bcopy((char *)server->h_addr, (char *)&addr.sin_addr.s_addr, server->h_length); 58 | addr.sin_port = htons(port); 59 | if (connect(sockfd, (void *) &addr,(socklen_t) sizeof(addr)) < 0) { 60 | fprintf(stderr,"Error connecting to server\n"); 61 | exit(1); 62 | } 63 | 64 | /* setup terminal in raw mode */ 65 | 66 | ttydev = 0; 67 | if (fcntl(ttydev, F_GETFL, ttyflags) == -1) { 68 | perror(" unable to get tty flags"); 69 | exit(1); 70 | } 71 | if (tcgetattr(ttydev, &terminfo) == -1) { 72 | perror(" unable to get tty attributes"); 73 | exit(1); 74 | } 75 | resetinfo = terminfo; 76 | 77 | terminfo.c_iflag = 0; 78 | terminfo.c_lflag = 0; 79 | terminfo.c_cflag = 0; 80 | terminfo.c_oflag = 0; 81 | #if 0 82 | terminfo.c_cc[VMIN] = 0; 83 | terminfo.c_cc[VTIME] = 0; 84 | #endif 85 | if (tcsetattr(ttydev, TCSANOW, &terminfo) == -1) { 86 | perror(" unable to set tty attributes"); 87 | exit(1); 88 | } 89 | 90 | printf("Connected! Use ^] to disconnect.\r\n"); 91 | 92 | /* read/write loop, with tty in raw mode: 93 | read from stdin, write to socket 94 | read from socket, write to stdout 95 | */ 96 | 97 | FD_ZERO(&fdread); 98 | FD_ZERO(&fdexcp); 99 | while (1) { 100 | 101 | /* wait until socket or stdin have data */ 102 | 103 | FD_SET(0, &fdread); /* stdin */ 104 | FD_SET(0, &fdexcp); 105 | FD_SET(sockfd, &fdread); /* socket */ 106 | FD_SET(sockfd, &fdexcp); 107 | #if 0 108 | timeout.tv_sec = 100; 109 | timeout.tv_usec = 0; 110 | #endif 111 | n = select(sockfd+1, &fdread, NULL, &fdexcp, NULL); 112 | if (n == -1) { 113 | if (errno == EINTR) 114 | continue; 115 | tcsetattr(ttydev, TCSANOW, &resetinfo); 116 | perror("Unable to do read select"); 117 | exit(1); 118 | } 119 | if (n == 0) 120 | continue; 121 | 122 | if (FD_ISSET(0, &fdexcp)) { 123 | tcsetattr(ttydev, TCSANOW, &resetinfo); 124 | fprintf(stderr,"Exception on tty\n"); 125 | exit(1); 126 | } 127 | if (FD_ISSET(0, &fdread)) { 128 | n = read(0, buf, sizeof(buf)); 129 | if (n == -1) { 130 | tcsetattr(ttydev, TCSANOW, &resetinfo); 131 | fprintf(stderr,"Error reading from stdin\n"); 132 | exit(1); 133 | } 134 | if (n == 0) { 135 | tcsetattr(ttydev, TCSANOW, &resetinfo); 136 | fprintf(stderr,"TTY disconnected\n"); 137 | exit(1); 138 | } 139 | if (n > 0) { 140 | for (i=0; i 0) { 173 | n2 = write(1, buf, n); /* send socket data to stdout */ 174 | if (n2 != n) { 175 | tcsetattr(ttydev, TCSANOW, &resetinfo); 176 | fprintf(stderr,"Only wrote %d of %d bytes to stdout\n", n2, n); 177 | exit(1); 178 | } 179 | } 180 | } 181 | } 182 | if (tcsetattr(ttydev, TCSANOW, &resetinfo) == -1) 183 | fprintf(stderr,"Unable to reset terminal\n"); 184 | } 185 | -------------------------------------------------------------------------------- /util/ptextu.c: -------------------------------------------------------------------------------- 1 | /* ptextu.c, J. Wilcoxson, March 16, 2005 2 | Reads a Prime text file from stdin and writes a Unix text file to stdout. 3 | 4 | Prime text files have 3 unique features: 5 | 6 | - the text characters all have the high bit set, including newline 7 | 8 | - text files may be compressed or uncompressed - compressed is 9 | typical. Compressed files represent runs of 3-255 spaces as a 2-byte 10 | sequence: 0221 (control-q w/parity) followed by the number of actual 11 | spaces in the next byte. Uncompressed files are typically used in 12 | record-oriented programs, where individual records may need to be 13 | randomly updated or positioned. Compressed text files can't be 14 | updated in general, other than to append or truncate, because the line 15 | length may change if the number of spaces changes. When the Prime 16 | compressed file input routines are used, blanks are expanded. Any 17 | Prime program that handles compressed text files will also work with 18 | uncompressed text files (just slower), but programs expecting 19 | uncompressed files won't work with compressed files since blank 20 | expansion doesn't happen. 21 | 22 | - all text file lines must start on a 16-bit word boundary. To 23 | enforce this restriction, when a newline character occurs in the left 24 | byte of a 16-bit word, ie, it's in an odd file position (with counting 25 | starting at 1), it is followed by a dummy character, typically a zero, 26 | that isn't part of the text file. This null has to be skipped for 27 | Unix since Unix treats every byte as part of the text file. 28 | 29 | Notes: 30 | 31 | - if the Prime text file ends with a compression character, that 32 | character is not written to the output file. This is an invalid Prime 33 | text file. 34 | 35 | Here is an octal dump of a Prime text file. Lines 1, 2, and 4 needed 36 | padding after the newline, but not line 3 or 5. Line 6 begins with the 37 | compression character sequence, representing 3 spaces in 2 characters, 38 | and has the same sequence later in the line. 39 | 40 | 0000000 354 351 363 364 346 272 240 360 362 357 343 273 212 \0 212 \0 41 | 0000020 245 351 356 343 354 365 344 345 240 247 363 371 363 343 357 355 42 | 0000040 276 353 345 371 363 256 351 356 363 256 360 354 261 247 273 212 43 | 0000060 212 \0 344 343 354 212 221 003 345 362 362 360 362 244 221 003 44 | 0000100 345 356 364 362 371 240 250 342 351 356 254 240 342 351 356 254 45 | 0000120 240 343 350 341 362 250 252 251 254 240 342 351 356 254 240 343 46 | 0000140 350 341 362 250 252 251 254 240 342 351 356 251 254 212 221 003 47 | 0000160 354 351 363 364 346 364 221 003 345 356 364 362 371 240 357 360 48 | 0000200 364 351 357 356 363 240 250 366 341 362 351 341 342 354 345 251 49 | 0000220 254 212 221 003 364 356 357 365 341 221 004 345 356 364 362 371 50 | 0000240 240 250 343 350 341 362 250 252 251 254 240 342 351 356 251 273 51 | 0000260 212 \0 212 \0 344 343 354 212 221 003 261 240 342 365 346 254 52 | 0000300 212 \0 221 006 262 240 354 345 356 240 342 351 356 254 212 \0 53 | 0000320 221 006 262 240 363 364 362 240 343 350 341 362 250 265 260 260 54 | 0000340 260 251 254 212 221 003 360 357 363 221 003 342 351 356 250 263 55 | 0000360 261 251 254 212 221 003 343 357 344 345 240 240 342 351 356 273 56 | 0000400 212 \0 212 \0 221 003 360 357 363 240 275 240 260 273 212 \0 57 | 0000420 221 003 344 357 240 365 356 364 351 354 240 250 342 365 346 256 58 | 0000440 354 345 356 240 276 240 260 251 273 212 221 006 343 341 354 354 59 | 0000460 240 354 351 363 364 346 364 240 250 342 365 346 254 240 262 265 60 | 0000500 260 260 254 240 360 357 363 254 240 343 357 344 345 251 273 212 61 | 0000520 221 006 351 346 240 343 357 344 345 240 336 275 240 260 240 364 62 | 0000540 350 345 356 212 221 \t 343 341 354 354 240 345 362 362 360 362 63 | 0000560 244 240 250 353 244 356 362 364 356 254 240 343 357 344 345 254 64 | 0000600 240 247 247 254 240 260 254 240 247 314 311 323 324 306 247 254 65 | 0000620 240 265 251 273 212 \0 221 006 343 341 354 354 240 364 356 357 66 | 0000640 365 341 240 250 342 365 346 256 363 364 362 254 240 341 342 363 67 | 0000660 240 250 342 365 346 256 354 345 356 251 251 273 212 \0 221 006 68 | 0000700 345 356 344 273 212 \0 212 \0 345 356 344 273 212 \0 69 | 0000716 70 | 71 | --- Here is the text version: --- 72 | listf: proc; 73 | 74 | %include 'syscom>keys.ins.pl1'; 75 | 76 | dcl 77 | errpr$ entry (bin, bin, char(*), bin, char(*), bin), 78 | listft entry options (variable), 79 | tnoua entry (char(*), bin); 80 | 81 | dcl 82 | 1 buf, 83 | 2 len bin, 84 | 2 str char(5000), 85 | pos bin(31), 86 | code bin; 87 | 88 | pos = 0; 89 | do until (buf.len > 0); 90 | call listft (buf, 2500, pos, code); 91 | if code ^= 0 then 92 | call errpr$ (k$nrtn, code, '', 0, 'LISTF', 5); 93 | call tnoua (buf.str, abs (buf.len)); 94 | end; 95 | 96 | end; 97 | --- End of Unix text file --- 98 | 99 | */ 100 | 101 | #include 102 | 103 | main () { 104 | int n,ch; 105 | 106 | n = 0; 107 | while ((ch=getchar()) != EOF) { 108 | n++; 109 | if (ch == 0221) { 110 | n++; 111 | if ((ch=getchar()) != EOF) /* avoid adding tons of blanks here */ 112 | while (ch--) 113 | putchar(' '); 114 | } else { 115 | ch &= 0x7f; /* turn off high bit for Unix text */ 116 | putchar(ch); 117 | if (ch == '\n' && n&1) { /* skip pad byte following newline */ 118 | getchar(); 119 | n++; 120 | } 121 | } 122 | } 123 | } 124 | -------------------------------------------------------------------------------- /ea64v.h: -------------------------------------------------------------------------------- 1 | /* this version is derived from the flowchart in the preliminary P400 2 | release notes */ 3 | 4 | static inline ea_t ea64v (unsigned short inst, ea_t earp) { 5 | 6 | ea_t ea; /* full seg/word va */ 7 | unsigned short ea_s; /* eff address segno */ 8 | unsigned short ea_w; /* eff address wordno */ 9 | unsigned short br; 10 | unsigned short i; 11 | unsigned short x; 12 | unsigned short xok; 13 | unsigned short a; 14 | unsigned short ixy; 15 | unsigned short m; 16 | unsigned short rph,rpl; 17 | 18 | 19 | i = inst & 0100000; /* indirect is bit 1 (left/MS bit) */ 20 | x = ((inst & 036000) != 032000) ? (inst & 040000) : 0; 21 | 22 | /* earp is usually = RPH/RPL in the register file, except for the 23 | case of an XEC instruction; in that case, earp points to 1 24 | after the instruction being executed */ 25 | 26 | rph = earp >> 16; 27 | rpl = earp & 0xFFFF; 28 | //TRACE(T_EAV, " inst=%o, rph=%o, rpl=%o\n", inst, rph, rpl); 29 | 30 | ea_s = rph; 31 | 32 | /* first check for long, 2-word, V-mode memory references. About 33 | half of V-mode references are in this form */ 34 | 35 | if ((inst & 01740) == 01400) { 36 | a = iget16(RP); 37 | INCRP; 38 | TRACE(T_EAV, " 2-word format, a=%o\n", a); 39 | ixy = (i >> 13) | (x >> 13) | ((inst & 020) >> 4); 40 | xok = (inst & 036000) != 032000; /* true if indexing is okay */ 41 | 42 | br = (inst & 3); 43 | eap = &gv.brp[br]; 44 | 45 | #ifndef NOTRACE 46 | int opcode; 47 | 48 | opcode = ((inst & 036000) != 032000) ? ((inst & 036000) >> 4) : ((inst & 076000) >> 4); 49 | opcode |= ((inst >> 2) & 3); /* opcode extension */ 50 | TRACE(T_EAV, " new opcode=%#05o, br=%d, ixy=%d, xok=%d\n", opcode, br, ixy, xok); 51 | #endif 52 | 53 | ea_s = getcrs16(PBH+br*2) | (ea_s & RINGMASK16); 54 | ea_w = getcrs16(PBL+br*2) + a; 55 | 56 | if (xok) 57 | if (ixy == 2 || ixy == 6) 58 | ea_w += getcrs16(X); 59 | else if (ixy == 1 || ixy == 4) 60 | ea_w += getcrs16(Y); 61 | 62 | #if 0 63 | /* if this is a PB% address, use RPBR instead if it's in range 64 | 65 | NOTE: this has been disabled, because gcov showed it only 66 | occurred 0.5% of the time */ 67 | 68 | if (br == 0 && ((((ea_s & 0x8FFF) << 16) | (ea_w & 0xFC00)) == gv.brp[RPBR].vpn)) 69 | eap = &gv.brp[RPBR]; 70 | #endif 71 | 72 | if (ixy >= 3) { 73 | ea = MAKEVA(ea_s, ea_w); 74 | TRACE(T_EAV, " Long indirect, ea=%o/%o, ea_s=%o, ea_w=%o\n", ea>>16, ea&0xFFFF, ea_s, ea_w); 75 | m = get16(ea); 76 | if (m & 0x8000) 77 | fault(POINTERFAULT, m, ea); 78 | ea_s = m | (ea_s & RINGMASK16); 79 | ea_w = get16(INCVA(ea,1)); 80 | TRACE(T_EAV, " After indirect, ea_s=%o, ea_w=%o\n", ea_s, ea_w); 81 | 82 | /* when passing stack variables, callee references will be 83 | SB%+20,*, which may still be in the same page. Don't switch to 84 | UNBR if the new ea is still in the current page */ 85 | 86 | if ((((ea_s & 0x8FFF) << 16) | (ea_w & 0xFC00)) != (eap->vpn & 0x0FFFFFFF)) 87 | eap = &gv.brp[UNBR]; 88 | 89 | if (xok) 90 | if (ixy == 7) { 91 | TRACE(T_EAV, " Postindex, old ea_w=%o, X='%o/%d\n", ea_w, getcrs16(X), getcrs16s(X)); 92 | ea_w += getcrs16(X); 93 | } else if (ixy == 5) { 94 | TRACE(T_EAV, " Postindex, old ea_w=%o, Y='%o/%d\n", ea_w, getcrs16(Y), getcrs16s(Y)); 95 | ea_w += getcrs16(Y); 96 | } 97 | } 98 | return MAKEVA(ea_s, ea_w); 99 | } 100 | 101 | /* now check for direct short-form - the 2nd-most frequent case */ 102 | 103 | if ((inst & 0101000) == 0) { 104 | ea_w = (inst & 0777); 105 | if (x) { 106 | TRACE(T_EAV, " Postindex, old ea_w=%o, X='%o/%d\n", ea_w, getcrs16(X), getcrs16s(X)); 107 | ea_w += getcrs16(X); 108 | TRACE(T_EAV, " Postindex, new ea_w=%o\n", ea_w); 109 | } 110 | if (inst & 0400) { 111 | TRACE(T_EAV, " Short LB relative, LB=%o/%o\n", getcrs16(LBH), getcrs16(LBL)); 112 | eap = &gv.brp[LBBR]; 113 | ea_s = getcrs16(LBH) | (ea_s & RINGMASK16); 114 | ea_w += getcrs16(LBL); 115 | return MAKEVA(ea_s, ea_w); 116 | } 117 | if (ea_w >= gv.livereglim) { 118 | eap = &gv.brp[SBBR]; 119 | ea_s = getcrs16(SBH) | (ea_s & RINGMASK16); 120 | ea_w += getcrs16(SBL); 121 | TRACE(T_EAV, " Short SB relative, SB=%o/%o\n", getcrs16(SBH), getcrs16(SBL)); 122 | return MAKEVA(ea_s, ea_w); 123 | } 124 | TRACE(T_EAV, " Live register '%o\n", ea_w); 125 | return 0x80000000 | (rph << 16) | ea_w; 126 | } 127 | 128 | /* here for short, PC-relative instructions: 129 | - if bit 7 set, it's PC relative 130 | - if bit 7 clear, it's sector 0 (or register) 131 | - instruction may be indirect or not 132 | - optionally, may be postindexed by X 133 | */ 134 | 135 | if (inst & 001000) { /* sector bit 7 set? */ 136 | ea_w = rpl + (((short) (inst << 7)) >> 7); /* yes, sign extend D */ 137 | TRACE(T_EAV, " PC relative, P=%o, new ea_w=%o\n", rpl, ea_w); 138 | eap = &gv.brp[RPBR]; 139 | 140 | } else { 141 | #if DBG 142 | if (!i) 143 | fatal ("ea64v: i not set?"); 144 | #endif 145 | eap = &gv.brp[S0BR]; 146 | ea_w = (inst & 0777); /* sector 0 */ 147 | TRACE(T_EAV, " Sector 0, new ea_w=%o\n", ea_w); 148 | if (ea_w < 0100 && x) { /* preindex by X */ 149 | TRACE(T_EAV, " Preindex, ea_w=%o, X='%o/%d\n", ea_w, getcrs16(X), getcrs16s(X)); 150 | ea_w += getcrs16(X); 151 | TRACE(T_EAV, " Preindex, new ea_w=%o\n", ea_w); 152 | x = 0; 153 | } 154 | } 155 | 156 | if (i) { 157 | if (ea_w >= gv.livereglim) { 158 | TRACE(T_EAV, " Indirect, ea_s=%o, ea_w=%o\n", ea_s, ea_w); 159 | ea_w = get16(MAKEVA(ea_s, ea_w)); 160 | } else { 161 | TRACE(T_EAV, " Indirect through live register '%o\n", ea_w); 162 | ea_w = get16trap(ea_w); 163 | } 164 | TRACE(T_EAV, " After indirect, new ea_w=%o\n", ea_w); 165 | } 166 | 167 | if (x) { 168 | TRACE(T_EAV, " Postindex, old ea_w=%o, X='%o/%d\n", ea_w, getcrs16(X), getcrs16s(X)); 169 | ea_w += getcrs16(X); 170 | TRACE(T_EAV, " Postindex, new ea_w=%o\n", ea_w); 171 | } 172 | 173 | /* if ea_w is within RP's brp cache page, set eap to match; 174 | otherwise, use PB's cache page */ 175 | 176 | if (ea_w >= gv.livereglim) { 177 | if (((ea_w ^ RPL) & 0xFC00) == 0) 178 | eap = &gv.brp[RPBR]; /* occurs 80% */ 179 | else 180 | eap = &gv.brp[PBBR]; /* occurs 20% */ 181 | return MAKEVA(ea_s, ea_w); 182 | } 183 | 184 | TRACE(T_EAV, " Live register '%o\n", ea_w); 185 | return 0x80000000 | (rph << 16) | ea_w; 186 | } 187 | -------------------------------------------------------------------------------- /util/istext.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | /* this function decides if this is a Prime text file or binary file 6 | by checking the contents of the first tape buffer. The conditions 7 | for a text file are: 8 | 9 | - it can't be inside a segdir (checked before calling here) 10 | - it has to be a SAM or DAM file - COMO files are DAM files :( 11 | - it has to have at least 1 newline in the first buffer, usually 12 | 4090+ bytes; text files with an initial line longer than this have 13 | to be converted after the restore using ptextu 14 | - lines have to start on word boundaries, with zero padding after nl 15 | (NOTE: como files have space padding, so allow that too) 16 | - only text characters, nl, ff, :001 at the beginning of a line, 17 | :221 (compression) and :211 (tab) are allowed in text files 18 | */ 19 | 20 | /* max length of extension including . and ending null byte */ 21 | 22 | #define MAXEXTENSION 10 23 | 24 | static struct { 25 | char ext[MAXEXTENSION]; 26 | int ftype; 27 | } exttype[] = { 28 | {".basic", 1}, 29 | {".cbl", 1}, 30 | {".c", 1}, 31 | {".cc", 1}, 32 | {".ci", 1}, 33 | {".cobol", 1}, 34 | {".comi", 1}, 35 | {".como", 1}, 36 | {".cpl", 1}, 37 | {".f77", 1}, 38 | {".ftn", 1}, 39 | {".ibas", 1}, 40 | {".ins", 1}, 41 | {".list", 1}, 42 | {".map", 1}, 43 | {".mod", 1}, 44 | {".pascal", 1}, 45 | {".plp", 1}, 46 | {".pl1", 1}, 47 | {".pl1g", 1}, 48 | {".pma", 1}, 49 | {".rpg", 1}, 50 | {".runi", 1}, 51 | {".runo", 1}, 52 | {".spl", 1}, 53 | {".sr", 1}, 54 | {".vrpg", 1}, 55 | 56 | {".bin", 0}, 57 | {".dl", 0}, 58 | {".save", 0}, 59 | }; 60 | #define EXTENTRIES sizeof(exttype)/sizeof(exttype[0]) 61 | 62 | int isptext(char *path, int filetype, unsigned char *buf, int len) { 63 | int i, hasnl, skipline; 64 | unsigned char ch; 65 | unsigned char extension[MAXEXTENSION]; 66 | 67 | /* scan backward to get file extension */ 68 | 69 | extension[0] = 0; 70 | for (i=strlen(path)-1; i >= 0; i--) 71 | if (path[i] == '.') { 72 | strncpy(extension, path+i, sizeof(extension)-1); 73 | break; 74 | } 75 | if (extension[0] == '.') 76 | for (i=0; i < EXTENTRIES; i++) 77 | if (strcasecmp(extension, exttype[i].ext) == 0) 78 | return exttype[i].ftype; 79 | 80 | if (filetype == 0 || (filetype == 1 && strcasecmp(extension,".como") == 0)) 81 | ; 82 | else { 83 | #ifdef DEBUG 84 | fprintf(stderr, "Filetype %d can't be a text file\n", filetype); 85 | #endif 86 | return 0; 87 | } 88 | 89 | hasnl = 0; 90 | skipline = 0; 91 | for (i=0; i= OBUFMAX) { 155 | if (fd != -1) 156 | if (write(fd, obuf, n) != n) { 157 | fprintf(stderr,"File write error text conversion, n=%d\n", n); 158 | exit(1); 159 | } 160 | n = 0; 161 | } 162 | } 163 | if (n > 0 && fd != -1 && write(fd, obuf, n) != n) { 164 | fprintf(stderr,"File write error text conversion, n=%d\n", n); 165 | exit(1); 166 | } 167 | return len; 168 | } 169 | 170 | /* scans buffer contents to see if it qualifies as a Unix text file that 171 | should be converted to Prime text. Text files can only have: 172 | - printable ASCII characters 173 | - tab 174 | - newline 175 | - carriage return 176 | - form feed 177 | */ 178 | 179 | int isutext(char *path, unsigned char *buf, int len) { 180 | int i, hasnl, skipline; 181 | unsigned char ch; 182 | unsigned char extension[MAXEXTENSION]; 183 | 184 | /* scan backward to get file extension */ 185 | 186 | extension[0] = 0; 187 | for (i=strlen(path)-1; i >= 0; i--) 188 | if (path[i] == '.') { 189 | strncpy(extension, path+i, sizeof(extension)-1); 190 | break; 191 | } 192 | if (extension[0] == '.') 193 | for (i=0; i < EXTENTRIES; i++) 194 | if (strcasecmp(extension, exttype[i].ext) == 0) 195 | return exttype[i].ftype; 196 | 197 | /* extension didn't determine type; check file contents */ 198 | 199 | hasnl = 0; 200 | for (i=0; ioddbyte = 0; 228 | state->col = 0; 229 | state->spaces = 0; 230 | 231 | It's possible that a string of spaces could be lost at the end of 232 | the file, but this is very unlikely since text files are supposed 233 | to end with a newline. 234 | */ 235 | 236 | int utextp(unsigned char *buf, int len, utextp_t *state) { 237 | int i, n, nsp; 238 | unsigned char ch; 239 | 240 | n = 0; /* next output buffer position */ 241 | for (i=0; ispaces++; 245 | else if (ch == '\t') { 246 | nsp = 8 - (state->col & 7); 247 | state->spaces += nsp; 248 | state->col += nsp; 249 | } else { 250 | while (state->spaces) { /* dump held-up spaces for non-space */ 251 | if (state->spaces < 3) { 252 | state->obuf[n++] = 0240; 253 | state->spaces--; 254 | state->oddbyte = ~state->oddbyte; 255 | } else { 256 | nsp = state->spaces; 257 | if (nsp > 255) /* can only handle 255 at once! */ 258 | nsp = 255; 259 | state->obuf[n++] = 0221; 260 | state->obuf[n++] = nsp; 261 | state->spaces = state->spaces - nsp; 262 | } 263 | } 264 | if (ch == '\r') /* ignore carriage returns (Windoze) */ 265 | continue; 266 | state->obuf[n++] = ch | 0x80; 267 | if (ch == 0212 && !state->oddbyte) 268 | state->obuf[n++] = 0; /* pad line to a word boundary */ 269 | else 270 | state->oddbyte = ~state->oddbyte; 271 | } 272 | if (n >= OBUFMAX) { 273 | if (fd != -1) 274 | if (write(fd, state->obuf, n) != n) { 275 | fprintf(stderr,"File write error text conversion, n=%d\n", n); 276 | exit(1); 277 | } 278 | n = 0; 279 | } 280 | } 281 | if (n > 0 && fd != -1 && write(fd, state->obuf, n) != n) { 282 | fprintf(stderr,"File write error text conversion, n=%d\n", n); 283 | exit(1); 284 | } 285 | return len; 286 | } 287 | #endif 288 | 289 | /* converts a fixed-length string in place from Prime to regular ascii */ 290 | 291 | void pasciiu(char *p, int len) { 292 | int i; 293 | 294 | for (i=0; i 39 | #include 40 | #include 41 | #include 42 | #include /* mkdir */ 43 | #include /* mkdir */ 44 | #include 45 | #include /* mktime */ 46 | #include /* utimes */ 47 | #include 48 | 49 | #include "istext.h" 50 | 51 | #define MAXDIRENTRIES 32 52 | 53 | #define MAXEXTENSION 10 54 | 55 | static struct { 56 | char ext[MAXEXTENSION]; 57 | int ftype; 58 | } exttype[] = { 59 | {".como", 1}, 60 | {".run", 1}, 61 | }; 62 | #define EXTENTRIES sizeof(exttype)/sizeof(exttype[0]) 63 | 64 | /* global vars */ 65 | 66 | FILE *outfile; 67 | int verbose = 0; 68 | unsigned short lrecl; 69 | struct { 70 | unsigned short ibuf[24]; 71 | } ds[MAXDIRENTRIES]; 72 | int dl = -1; /* # of entries in ds; -1=empty */ 73 | 74 | helpexit() { 75 | fprintf(stderr, "Usage: magsav [-v] [-vv] -f [file1] [file2] ...\n"); 76 | fprintf(stderr, "Saves file1-filen in Prime MAGSAV format to output file.\n"); 77 | exit(0); 78 | } 79 | 80 | /* this function takes a Unix timestamp and converts it to a Prime 81 | filesystem timestamp (a 32-bit integer). 82 | Format of a Prime FS timestamp is: 83 | 84 | left 16 bits: YYYYYYYMMMMDDDDD, year is mod 100 85 | right 16 bits: seconds since midnight divided by 4, ie, 0-21599 86 | */ 87 | 88 | unsigned int utimestampp(time_t unixtime) { 89 | int i; 90 | unsigned short pdate; 91 | unsigned short ptime; 92 | struct tm *tms; 93 | 94 | /* break down Unix timestamp */ 95 | 96 | tms = localtime(&unixtime); 97 | i = tms->tm_year; 98 | if (i > 127) 99 | i = 127; /* dont' let it overflow! */ 100 | pdate = (tms->tm_year<<9) | ((tms->tm_mon+1)<<5) | tms->tm_mday; 101 | ptime = (tms->tm_hour*3600 + tms->tm_min*60 + tms->tm_sec)/4; 102 | return (pdate<<16) | ptime; 103 | } 104 | 105 | 106 | /* write a Magsav tape record in .TAP format: 107 | - 4 bytes for .TAP format record length (bytes following this) 108 | - 3 word (6 byte) Magsav tape record header 109 | - Magsav record 110 | - 4 bytes for .TAP format ending record length 111 | */ 112 | 113 | mtwrite (int recid, unsigned short *ibuf, int nw) { 114 | 115 | int tapbytes; 116 | unsigned char tapbuf[4]; 117 | short mshdr[3]; 118 | 119 | if (recid == TAP_FILE_MARK) 120 | tapbytes = 0; 121 | else 122 | tapbytes = (nw+3)*2; 123 | tapbuf[0] = tapbytes & 0xFF; 124 | tapbuf[1] = (tapbytes>>8) & 0xFF; 125 | tapbuf[2] = (tapbytes>>16) & 0xFF; 126 | tapbuf[3] = (tapbytes>>24) & 0xFF; 127 | if (fwrite(tapbuf, 1, 4, outfile) != 4) { 128 | perror("Writing .TAP leading record length"); 129 | exit(1); 130 | } 131 | if (recid != TAP_FILE_MARK) { 132 | mshdr[0] = htons(lrecl); 133 | mshdr[1] = htons(nw+3); 134 | mshdr[2] = htons(recid); 135 | if (fwrite(mshdr, 2, 3, outfile) != 3) { 136 | perror("Writing MAGSAV record header"); 137 | exit(1); 138 | } 139 | if (fwrite(ibuf, 2, nw, outfile) != nw) { 140 | perror("Writing MAGSAV record buffer"); 141 | exit(1); 142 | } 143 | if (fwrite(tapbuf, 1, 4, outfile) != 4) { 144 | perror("Writing .TAP trailing record length"); 145 | exit(1); 146 | } 147 | } 148 | lrecl++; 149 | } 150 | 151 | 152 | savename() { 153 | mtwrite(MS_NAME, (unsigned short *)&ds, (dl+1)*24); 154 | } 155 | 156 | 157 | pushname(char *name, struct stat sb) { 158 | int i,j; 159 | char *p, ch; 160 | int filetype; 161 | unsigned char extension[8]; 162 | 163 | dl++; 164 | if (dl > MAXDIRENTRIES) { 165 | fprintf(stderr, "directory stack overflowflow!\n"); 166 | exit(1); 167 | } 168 | if ((sb.st_mode & S_IFMT) == S_IFDIR) 169 | filetype = 5; /* ACL directory type */ 170 | else { 171 | 172 | /* scan backward to get file extension */ 173 | 174 | filetype = 0; /* assume SAM file type */ 175 | extension[0] = 0; 176 | for (i=strlen(name)-1; i >= 0; i--) 177 | if (name[i] == '.') { 178 | strncpy(extension, name+i, sizeof(extension)-1); 179 | break; 180 | } 181 | if (extension[0] == '.') 182 | for (i=0; i < EXTENTRIES; i++) 183 | if (strcasecmp(extension, exttype[i].ext) == 0) 184 | filetype = exttype[i].ftype; 185 | } 186 | 187 | /* ECW: left byte=dir entry type, right byte = size in words, incl. ECW 188 | 189 | Directory entry types: 190 | 0 = old directory header 191 | 1 = directory header 192 | 2 = vacant entry 193 | 3 = file entry 194 | 4 = access category 195 | 5 = ACL 196 | 6 = directory index block 197 | */ 198 | 199 | ds[dl].ibuf[0] = htons((3<<8) | 24); 200 | p = (char *)(ds[dl].ibuf+1); /* point to name part */ 201 | j = 0; 202 | for (i=0; i<32; i++) { /* copy name, upcase, fix parity */ 203 | ch = name[j++]; 204 | if (ch == 0) 205 | ch = ' '; 206 | else if (ch == 'S') 207 | ch = '/'; 208 | else if ('a' <= ch && ch <= 'z') 209 | ch = ch - 'a' + 'A'; 210 | *p++ = ch | 0x80; 211 | } 212 | ds[dl].ibuf[17] = htons(0xFF00); /* owner/non-owner protection */ 213 | ds[dl].ibuf[18] = 0; /* ACL protection */ 214 | ds[dl].ibuf[19] = htons(filetype); 215 | *(int *)(ds[dl].ibuf+20) = htonl(utimestampp(sb.st_mtime)); 216 | ds[dl].ibuf[22] = 0; /* reserved */ 217 | ds[dl].ibuf[23] = 0; 218 | } 219 | 220 | popname() { 221 | if (dl >= 0) 222 | dl--; 223 | else { 224 | fprintf(stderr, "directory stack underflow!\n"); 225 | exit(1); 226 | } 227 | } 228 | 229 | savefile(char *path, char *name, struct stat sb) { 230 | 231 | char buf[4090]; 232 | int i, n, fd, nw; 233 | int first, text; 234 | utextp_t state; 235 | 236 | if ((fd=open(path, O_RDONLY)) == -1) { 237 | perror(NULL); 238 | return; 239 | } 240 | pushname(name, sb); 241 | savename(); 242 | popname(); 243 | 244 | state.oddbyte = 0; 245 | state.col = 0; 246 | state.spaces = 0; 247 | 248 | first = 1; 249 | while ((n=read(fd, buf, sizeof(buf))) > 0) { 250 | if (first) 251 | text = isutext(path, buf, n); 252 | if (n & 1) { 253 | fprintf(stderr,"Warning: odd-length file padded with newline or zero\n"); 254 | if (text) 255 | buf[n++] = '\n'; 256 | else 257 | buf[n++] = 0; 258 | } 259 | if (text) { 260 | } 261 | nw = (n+1)/2; 262 | mtwrite(MS_DATA, (unsigned short *)buf, nw); 263 | } 264 | close(fd); 265 | } 266 | 267 | 268 | savedir(char *path, char *name, struct stat sb) { 269 | 270 | unsigned short ibuf[9]; 271 | DIR *dp; 272 | struct dirent *de; 273 | int i; 274 | 275 | if ((dp=opendir(path)) == NULL) { 276 | perror(NULL); 277 | return; 278 | } 279 | pushname(name, sb); 280 | savename(); 281 | 282 | /* write MAGSAV directory data record */ 283 | 284 | ibuf[0] = 8; 285 | for (i=1; i<3; i++) 286 | ibuf[i] = ((' '<<8) | ' ') | 0x8080; /* owner pw */ 287 | ibuf[4] = ibuf[5] = ibuf[6] = 0; /* non-owner password */ 288 | ibuf[7] = 0; 289 | ibuf[8] = 0; 290 | mtwrite(MS_DATA, ibuf, 9); 291 | 292 | /* write all directory entries */ 293 | 294 | while ((de=readdir(dp)) != NULL) { 295 | if (strcmp(de->d_name,".") == 0 || strcmp(de->d_name,"..") == 0) 296 | continue; 297 | save(path, de->d_name); 298 | } 299 | 300 | /* all done */ 301 | 302 | closedir(dp); 303 | popname(); 304 | } 305 | 306 | 307 | save(char *parent, char *name) { 308 | 309 | char path[1024]; 310 | struct stat sb; 311 | 312 | path[0] = 0; 313 | strcpy(path, parent); 314 | strcat(path, "/"); 315 | strcat(path, name); 316 | printf("%s\n", path); 317 | if (stat(path, &sb) == -1) { 318 | perror("Stat error"); 319 | return; 320 | } 321 | if ((sb.st_mode & S_IFMT) == S_IFDIR) 322 | savedir(path, name, sb); 323 | else if ((sb.st_mode & S_IFMT) == S_IFREG) 324 | savefile(path, name, sb); 325 | else 326 | fprintf(stderr, "Ignored %s: can't save this file type\n", name); 327 | } 328 | 329 | main (int argc, char** argv) { 330 | 331 | int argx; 332 | union { 333 | unsigned short i[16]; 334 | unsigned char c[32]; 335 | } buf; 336 | int i; 337 | 338 | /* init */ 339 | 340 | outfile = NULL; 341 | 342 | /* any args? */ 343 | 344 | for (argx=1; argx REV A U-CODE) */ 172 | 2, /* 02 RESERVED */ 173 | 2, /* 03 P350 */ 174 | 2, /* 04 P450/P550 */ 175 | 2, /* 05 P750 */ 176 | 2, /* 06 P650 */ 177 | 2, /* 07 P150/P250 */ 178 | 2, /* 08 P850 */ 179 | 2, /* 09 MOLE/550 */ 180 | 2, /* 10 MOLE/650 */ 181 | 2, /* 11 P2250 */ 182 | 2, /* 12 P750Y */ 183 | 2, /* 13 P550Y */ 184 | 2, /* 14 P850Y */ 185 | 4, /* 15 P9950 */ 186 | 8, /* 16 P9650 */ 187 | 8, /* 17 P2550 */ 188 | 4, /* 18 P9955 */ 189 | 4, /* 19 P9750 */ 190 | 2, /* 20 TBD */ 191 | 8, /* 21 P2350 */ 192 | 8, /* 22 P2655 */ 193 | 8, /* 23 P9655 */ 194 | 4, /* 24 P9955-TIGGER */ 195 | 8, /* 25 P2450 */ 196 | 4, /* 26 P4050 */ 197 | 4, /* 27 P4150 */ 198 | 4, /* 28 P6350 */ 199 | 4, /* 29 P6550 */ 200 | 4, /* 30 P9955-II */ 201 | 8, /* 31 P2755 */ 202 | 8, /* 32 P2455 */ 203 | 4, /* 33 P5310 */ 204 | 4, /* 34 P9755 */ 205 | 4, /* 35 P2850 */ 206 | 4, /* 36 P2950 */ 207 | 4, /* 37 P5330 */ 208 | 4, /* 38 P4450 */ 209 | 4, /* 39 P5370 */ 210 | 4, /* 40 P6650 */ 211 | 4, /* 41 P6450 */ 212 | 4, /* 42 P6150 */ 213 | 4, /* 43 P5320 */ 214 | 4}; /* 44 P5340 */ 215 | 216 | static union { 217 | int rs[REGSETS][32]; 218 | 219 | unsigned short rs16[REGSETS][64]; 220 | 221 | /* locs '0-'177 as signed 32-bit integers */ 222 | int s32[32*REGSETS]; 223 | 224 | /* locs '0-'177 as unsigned 32-bit integers */ 225 | unsigned int u32[32*REGSETS]; 226 | 227 | /* locs '0-'377 as signed 16-bit integers */ 228 | short s16[64*REGSETS]; 229 | 230 | /* locs '0-'377 as signed 16-bit integers */ 231 | unsigned short u16[64*REGSETS]; 232 | 233 | /* symbolic register file locations */ 234 | struct { 235 | unsigned int tr0,tr1,tr2,tr3,tr4,tr5,tr6,tr7; /* '0-7 */ 236 | unsigned int rdmx1,rdmx2; /* '10-11 */ 237 | unsigned short rdum1[1],ratmpl; /* '12 */ 238 | unsigned int rsgt1,rsgt2,recc1,recc2; /* '13-16 */ 239 | unsigned short rdum2[1],reoiv,zero,one; /* '17-20 */ 240 | unsigned int pbsave,rdmx3,rdmx4,c377,rdum3[3]; /* '21-27 */ 241 | unsigned int pswpb; /* '30 */ 242 | unsigned short pswkeys,rdum4[1]; /* '31 */ 243 | unsigned short pla,pcba,plb,pcbb; /* '32-33 */ 244 | unsigned int dswrma; /* '34 */ 245 | unsigned int dswstat; /* '35 */ 246 | unsigned int dswpb,rsavptr; /* '36-37 */ 247 | unsigned short regdmx[64]; /* '40-77 */ 248 | unsigned int userregs[REGSETS-2][32]; /* '100- */ 249 | } sym; 250 | } regs; 251 | 252 | union { 253 | struct { 254 | #ifdef __BIG_ENDIAN__ 255 | unsigned short rph; 256 | unsigned short rpl; 257 | #else 258 | unsigned short rpl; 259 | unsigned short rph; 260 | #endif 261 | } s; 262 | unsigned int ul; 263 | } rpreg; 264 | 265 | #define grp RP /* turns grp assignments into dummies */ 266 | #define gcrsl crsl /* turns gcrsl assignments into dummies */ 267 | 268 | static union { 269 | short *i16; 270 | unsigned short *u16; 271 | int *i32; 272 | unsigned int *u32; 273 | long long *i64; 274 | unsigned long long *u64; 275 | } cr; 276 | 277 | #define crs cr.u16 278 | #define crsl cr.u32 279 | 280 | #define RP rpreg.ul 281 | #define RPH rpreg.s.rph 282 | #define RPL rpreg.s.rpl 283 | 284 | /************ 16-bit offset macros: *************/ 285 | 286 | /* fetch 16-bit unsigned at 16-bit offset */ 287 | //#define getcrs16(offset) crs[(offset)] 288 | static inline uint16_t getcrs16(int offset) { \ 289 | return swap16(crs[offset]); \ 290 | } 291 | 292 | /* store 16-bit unsigned at 16-bit offset */ 293 | //#define putcrs16(offset, val) crs[(offset)] = (val) 294 | static inline void putcrs16(int offset, uint16_t val) { \ 295 | crs[(offset)] = swap16(val); \ 296 | } 297 | 298 | /* get 16-bit signed at 16-bit offset */ 299 | //#define getcrs16s(offset) *(short *)(crs+(offset)) 300 | #define getcrs16s(offset) (int16_t) getcrs16((offset)) 301 | 302 | /* get 32-bit unsigned at 16-bit offset */ 303 | //#define getcrs32(offset) *(unsigned int *)(crs+offset) 304 | static inline uint32_t getcrs32(int offset) { \ 305 | return swap32(*(unsigned int *)(crs+offset)); \ 306 | } 307 | 308 | /* get 32-bit signed at 16-bit offset */ 309 | //#define getcrs32s(offset) *(int *)(crs+(offset)) 310 | #define getcrs32s(offset) (int32_t) getcrs32((offset)) 311 | 312 | /* put 32-bit unsigned at 16-bit offset */ 313 | //#define putcrs32(offset, val) *(unsigned int *)(crs+(offset)) = (val) 314 | static inline void putcrs32(int offset, uint32_t val) { \ 315 | *(unsigned int *)(crs+offset) = swap32(val); \ 316 | } 317 | 318 | /* put 32-bit signed at 16-bit offset */ 319 | //#define putcrs32s(offset, val) *(int *)(crs+(offset)) = (val) 320 | #define putcrs32s(offset, val) putcrs32((offset), (val)) 321 | 322 | /* get 32-bit effective address at 16-bit offset */ 323 | //#define getcrs32ea(offset) *(ea_t *)(crs+(offset)) 324 | #define getcrs32ea(offset) getcrs32(offset) 325 | 326 | /* put 32-bit effective address at 16-bit offset */ 327 | //#define putcrs32ea(offset, val) *(ea_t *)(crs+(offset)) = (val) 328 | #define putcrs32ea(offset, val) putcrs32((offset), (val)) 329 | 330 | /* get 64-bit signed at 16-bit offset */ 331 | //#define getcrs64s(offset) *(long long *)(crs+(offset)) 332 | static inline int64_t getcrs64s(int offset) { \ 333 | return (long long)swap64(*(long long *)(crs+(offset))); \ 334 | } 335 | 336 | /* put 64-bit signed at 16-bit offset */ 337 | //#define putcrs64s(offset, val) *(long long *)(crs+(offset)) = (val) 338 | static inline void putcrs64s(int offset, int64_t val) { \ 339 | *(long long *)(crs+offset) = swap64(val); \ 340 | } 341 | 342 | /* put 64-bit double at 16-bit offset (remove later) */ 343 | //#define putcrs64d(offset, val) *(double *)(crs+(offset)) = (val) 344 | static inline void putcrs64d(int offset, double val) { \ 345 | *(unsigned long long *)(crs+offset) = swap64(*(uint64_t *)&val); \ 346 | } 347 | 348 | /* get 16-bit unsigned at 16-bit absolute register file address */ 349 | static inline uint16_t getar16(int offset) { \ 350 | return swap16(regs.u16[offset]); \ 351 | } 352 | 353 | /* put 16-bit unsigned at 16-bit absolute register file address */ 354 | static inline void putar16(int offset, uint16_t val) { \ 355 | regs.u16[(offset)] = swap16(val); \ 356 | } 357 | 358 | /******* 32-bit offset macros: ***********/ 359 | 360 | /* fetch 16-bit unsigned at 32-bit offset (left halfword is returned) */ 361 | //#define getgr16(offset) crs[(offset)*2] 362 | static inline uint16_t getgr16(int offset) { \ 363 | return swap16(crs[offset*2]); \ 364 | } 365 | 366 | /* store 16-bit unsigned at 32-bit offset (in left halfword) */ 367 | //#define putgr16(offset, val) crs[(offset)*2] = (val) 368 | static inline void putgr16(int offset, uint16_t val) { \ 369 | crs[(offset)*2] = swap16(val); \ 370 | } 371 | 372 | /* fetch 16-bit signed at 32-bit offset (right halfword is returned) */ 373 | //#define getgr16s(offset) *(short *)(crs+(offset)*2) 374 | #define getgr16s(offset) (int16_t) getgr16((offset)) 375 | 376 | /* store 16-bit signed at 32-bit offset (in left halfword) */ 377 | //#define putgr16s(offset, val) *(short *)(crs+(offset)*2) = (val) 378 | #define putgr16s(offset, val) putgr16((offset), (val)) 379 | 380 | /* fetch 32-bit unsigned at 32-bit offset */ 381 | //#define getgr32(offset) crsl[(offset)] 382 | static inline uint32_t getgr32(int offset) { \ 383 | return swap32(crsl[offset]); 384 | } 385 | 386 | /* store 32-bit unsigned at 32-bit offset */ 387 | //#define putgr32(offset, val) crsl[(offset)] = (val) 388 | static inline void putgr32(int offset, uint32_t val) { \ 389 | crsl[offset] = swap32(val); \ 390 | } 391 | 392 | /* fetch 32-bit signed at 32-bit offset */ 393 | //#define getgr32s(offset) *(int *)(crsl+(offset)) 394 | #define getgr32s(offset) (int32_t) getgr32((offset)) 395 | 396 | /* store 32-bit signed at 32-bit offset */ 397 | //#define putgr32s(offset, val) *(int *)(crsl+(offset)) = (val) 398 | #define putgr32s(offset, val) putgr32((offset), (val)) 399 | 400 | /* fetch 64-bit signed at 32-bit offset */ 401 | //#define getgr64s(offset) *(long long *)(crsl+(offset)) 402 | static inline int64_t getgr64s(int offset) { \ 403 | return (int64_t) swap64(*(long long *)(crsl+offset)); 404 | } 405 | 406 | /* store 64-bit signed at 32-bit offset */ 407 | //#define putgr64s(offset, val) *(long long *)(crsl+(offset)) = (val) 408 | static inline void putgr64s(int offset, int64_t val) { \ 409 | *(long long *)(crsl+offset) = swap64(val); \ 410 | } 411 | 412 | /* fetch 64-bit unsigned at 32-bit offset */ 413 | //#define getgr64(offset) *(unsigned long long *)(crsl+(offset)) 414 | #define getgr64(offset) getgr64s(offset) 415 | 416 | /* get 32-bit unsigned at 32-bit absolute register file address */ 417 | static inline uint32_t getar32(int offset) { \ 418 | return swap32(regs.u32[offset]); \ 419 | } 420 | 421 | /* put 32-bit unsigned at 32-bit absolute register file address */ 422 | static inline void putar32(int offset, uint32_t val) { \ 423 | regs.u32[(offset)] = swap32(val); \ 424 | } 425 | 426 | /* fetch 32-bit unsigned at FP register 0 or 1 427 | For FP 0, offset=0; for FP 1, offset=2 428 | NOTE: instead of doing FAC0+offset, there could be another 429 | pointer to FR0, then use offset as an index */ 430 | #define getfr32(offset) getgr32(FAC0+offset) 431 | 432 | /* fetch 64-bit unsigned at FP register 0 or 1 433 | For FP 0, offset=0; for FP 1, offset=2 */ 434 | static inline uint64_t getfr64(int offset) { \ 435 | return (uint64_t) swap64(*(unsigned long long *)(crsl+FAC0+offset)); 436 | } 437 | 438 | /* put 64-bit double in FP reg 0 or 1 439 | For FP 0, offset=0; for FP 1, offset=2 */ 440 | //#define putfr64(offset, val) 441 | static inline void putfr64(int offset, unsigned long long val) { \ 442 | *(unsigned long long *)(crsl+FAC0+offset) = swap64((val)); 443 | } 444 | 445 | /* put 64-bit double in FP reg 0 or 1 446 | For FP 0, offset=0; for FP 1, offset=2 */ 447 | //#define putfr64d(offset, val) *(double *)(crsl+FAC0+offset) = (val) 448 | static inline void putfr64d(int offset, double val) { \ 449 | *(unsigned long long *)(crsl+FAC0+offset) = swap64(*(uint64_t *)&val); \ 450 | } 451 | 452 | #define PCBLEV 0 453 | #define PCBLINK 1 454 | #define PCBWAIT 2 455 | #define PCBABT 4 456 | #define PCBCPU 5 457 | #define PCBPET 8 458 | #define PCBDTAR2 10 459 | #define PCBDTAR3 12 460 | #define PCBIT 14 461 | #define PCBMASK 16 462 | #define PCBKEYS 17 463 | #define PCBREGS 18 464 | #define PCBFVEC 50 465 | #define PCBFVR0 50 466 | #define PCBFVR1 52 467 | #define PCBFVR2 54 468 | #define PCBFVR3 56 469 | #define PCBFVPF 58 470 | #define PCBCSFIRST 60 471 | #define PCBCSNEXT 61 472 | #define PCBCSLAST 62 473 | 474 | /* define mapping between memory addresses and the current register set */ 475 | 476 | static unsigned short memtocrs[] = { 477 | X, /* 0 = X */ 478 | A, /* 1 = A */ 479 | B, /* 2 = B */ 480 | Y, /* 3 = Y */ 481 | FLTH, /* 4 = FAC1/FLTH */ 482 | FLTL, /* 5 = FAC1/FLTL */ 483 | FEXP, /* 6 = FAC1/FEXP */ 484 | -1, /* 7 = PC (this is in the microcode scratch register set - TR7) */ 485 | 32, /* 10 = unnamed */ 486 | FCODE, /* 11 = FCODE */ 487 | FADDR+1,/* 12 = FADDR (word) */ 488 | 16, /* 13 = unnamed */ 489 | SBH, /* 14 = unnamed (SB seg) */ 490 | SBL, /* 15 = unnamed (SB word) */ 491 | LBH, /* 16 = unnamed (LB seg) */ 492 | LBL}; /* 17 = unnamed (LB word) */ 493 | 494 | -------------------------------------------------------------------------------- /fp.h: -------------------------------------------------------------------------------- 1 | /* fp.h, Jim Wilcoxson, June 2007 2 | Floating point conversion and helper routines for the Prime emulator. 3 | 4 | Prime DPFP format: 5 | - 48 mantissa bits stored in 2's complement format 6 | - 16 exponent bits stored in 2's complement with a bias of 128 7 | - exponent follows the mantissa in both memory and registers 8 | - some early Primes store the exponent in the 3rd halfword 9 | - the leading "1" bit in the mantissa is only suppressed for negative 10 | powers of 2 11 | - Prime treats zero mantissa as 0.0, even if exponent is non-zero 12 | - all FP operations are carried out in double precision 13 | 14 | IEEE DPFP format: 15 | - 1 sign bit 16 | - 11 exponent bits with a bias of 1023 17 | - 52 mantissa bits (sign-magnitude format) 18 | - leading bit of mantissa has a suppressed "1" (except subnormal) 19 | - if exponent is zero: 20 | - and frac = 0 => positive or negative zero, depending on sign 21 | - and frac non-zero => subnormal (aka denormal, unnormalized) 22 | - subnormals have an implied leading "0" bit (XXX: true??) 23 | 24 | References (no code was used): 25 | 26 | http://en.wikipedia.org/wiki/IEEE_754 27 | http://www.psc.edu/general/software/packages/ieee/ieee.html 28 | http://www.math.grinnell.edu/~stone/courses/fundamentals/IEEE-reals.html 29 | http://www.gnu.org/software/libc/manual/html_node/IEEE-Floating-Point.html 30 | http://en.wikipedia.org/wiki/Floating_point 31 | http://www.win.ua.ac.be/~cant/arithmos/ 32 | http://tima-cmp.imag.fr/~guyot/Cours/Oparithm/english/Op_Ar2.htm 33 | 34 | */ 35 | 36 | #include 37 | 38 | /* getdp unpacks a Prime DPFP into 48-bit sign + mantissa (left 39 | justified in 64 bits) and a 32-bit signed exponent */ 40 | 41 | inline void getdp (unsigned long long p, long long *frac64, int *exp32) { 42 | 43 | TRACE(T_FP, " getdp(%016llx)\n", p); 44 | *frac64 = p & 0xFFFFFFFFFFFF0000LL; /* unpack fraction */ 45 | *exp32 = (short) (p & 0xFFFFLL); /* unpack SIGNED exponent */ 46 | } 47 | 48 | #define RETFP(frac64, exp32) return ((frac64) & 0xFFFFFFFFFFFF0000LL) | ((exp32) & 0xFFFF) 49 | 50 | /* Conversion from Prime DPFP to IEEE DPFP 51 | Returns true if conversion succeeded, false if it failed. 52 | Conversions may fail because Prime exponents are 16 bits whereas 53 | IEEE DPFP exponents are only 11 bits. 54 | */ 55 | 56 | int prieee8(unsigned long long dp, double *d) { 57 | long long frac64, sign; 58 | int exp32; 59 | 60 | /* unpack Prime DPFP */ 61 | 62 | getdp (dp, &frac64, &exp32); 63 | TRACE(T_FP, " prieee8: unpacked frac %016llx exp %08x\n", frac64, exp32); 64 | 65 | /* if negative, change to sign-magnitude */ 66 | 67 | sign = 0; 68 | if (frac64 < 0) { 69 | TRACE(T_FP, " prieee8: changing to sign-magnitude\n"); 70 | 71 | /* special case: negative power of 2 */ 72 | 73 | if (frac64 == 0x8000000000000000LL) { 74 | TRACE(T_FP, " prieee8: frac is negative power of 2\n"); 75 | exp32 += (1023-128); 76 | if (exp32 < 0 || exp32 > 0x7fe) 77 | { 78 | TRACE(T_FP, " prieee8: exp %x out of range\n", exp32); 79 | return 0; 80 | } 81 | frac64 |= ((long long)exp32 << 52); 82 | *d = *(double *)&frac64; 83 | return 1; 84 | } else { 85 | TRACE(T_FP, " prieee8: frac is just negative\n"); 86 | sign = 0x8000000000000000LL; 87 | frac64 = -frac64; 88 | } 89 | 90 | /* special case: zero */ 91 | 92 | } else if (frac64 == 0) { 93 | TRACE(T_FP, " prieee8: frac is 0\n"); 94 | *d = 0.0; 95 | return 1; 96 | } 97 | 98 | /* normalize positive fraction until bit 2 is 1 */ 99 | 100 | while ((frac64 & 0x4000000000000000LL) == 0) { 101 | TRACE(T_FP, " prieee8: normalizing positive fraction\n"); 102 | frac64 = frac64 << 1; 103 | exp32--; 104 | } 105 | 106 | /* adjust exponent bias and check range */ 107 | 108 | exp32 += (1023-128) - 1; 109 | #if 1 110 | if (exp32 < 0 || exp32 > 0x7fe) 111 | { 112 | TRACE(T_FP, " prieee8: exponent %x out of range\n", exp32); 113 | return 0; 114 | } 115 | #else 116 | if (exp32 < 0) { 117 | *d = 0.0; 118 | return 1; 119 | } 120 | if (exp32 > 0x7fe) { 121 | exp32 = 0x7fe; 122 | frac64 = 0x7fffffffffffffffLL; 123 | } 124 | #endif 125 | 126 | /* pack into an IEEE DPFP, losing the leading 1 bit in the process */ 127 | 128 | frac64 = sign | ((long long)exp32 << 52) | ((frac64 >> 10) & 0xfffffffffffffLL); 129 | TRACE(T_FP, " prieee8: packed ieee dpfp %016llx\n", frac64); 130 | *d = *(double *)&frac64; 131 | return 1; 132 | } 133 | 134 | /* conversion from IEEE back to Prime. Prime exponents are larger, so 135 | this conversion cannot overflow/underflow, but precision may be 136 | lost */ 137 | 138 | int ieeepr8(double d, long long *p, int round) { 139 | long long frac64; 140 | int exp32, neg, okay; 141 | 142 | okay = 1; 143 | 144 | /* unpack IEEE DPFP */ 145 | 146 | retry: 147 | *(double *)&frac64 = d; 148 | neg = frac64 < 0; 149 | exp32 = (frac64 >> 52) & 0x7ff; 150 | frac64 &= 0xfffffffffffffLL; 151 | TRACE(T_FP, "d=%016llx, neg=%x, frac64=%016llx, exp32=%08x, \n", (long long)d, neg, frac64, exp32); 152 | 153 | /* special case: NaN & +-infinity */ 154 | 155 | if (exp32 == 0x7ff) { 156 | okay = 0; 157 | if (frac64 == 0) 158 | if (neg) { 159 | printf("em: +infinity in ieeepr8\n"); 160 | d = DBL_MAX; 161 | } else { 162 | printf("em: -infinity in ieeepr8\n"); 163 | d = DBL_MIN; 164 | } 165 | else { 166 | printf("em: NaN in ieeepr8\n"); 167 | d = 0.0; 168 | } 169 | goto retry; 170 | } 171 | 172 | /* add back the hidden "1" bit except for the special cases +-0.0 173 | and subnormal */ 174 | 175 | if (exp32 != 0) /* typical IEEE normalized */ 176 | { 177 | TRACE(T_FP, " ieeepr8: is normalized\n"); 178 | frac64 |= 0x10000000000000LL; 179 | } 180 | else if (frac64 == 0) /* IEEE +-0.0 (zero exp+frac) */ 181 | { 182 | TRACE(T_FP, " ieeepr8: zero exp and frac\n"); 183 | *p = 0; /* IEEE and Prime zero are the same */ 184 | return okay; 185 | } 186 | else 187 | { 188 | TRACE(T_FP, " ieeepr8: subnormal value\n"); 189 | ; /* subnormal: no hidden 1 bit */ 190 | } 191 | 192 | /* adjust exponent, change sign-magnitude to 2's complement, 193 | and shift fraction into Prime placement (high 48 bits) */ 194 | 195 | exp32 -= (1023-128) - 1; 196 | if (neg) 197 | frac64 = -frac64; 198 | frac64 <<= 10; 199 | TRACE(T_FP, " ieeepr8: de-biased prime-ised frac %016llx exp %08x\n", frac64, exp32); 200 | 201 | /* normalize Prime DPFP */ 202 | 203 | while ((frac64 ^ (frac64 << 1)) >= 0) { 204 | frac64 = frac64 << 1; 205 | exp32--; 206 | TRACE(T_FP, " ieeepr8: normalized prime frac %016llx exp %08x\n", frac64, exp32); 207 | } 208 | 209 | #if 0 210 | if (exp32 > 32767 || exp32 < -32768) { 211 | printf("em: exponent = %d in ieeepr8\n", exp32); 212 | return 0.0; 213 | } 214 | #endif 215 | 216 | /* round the fraction to 48 bits, ensuring no overflow 217 | 218 | IMPORTANT NOTE: this rounding was disabled because it screws up 219 | Prime Information at rev 21. Information uses a DPFP variable 220 | to store a pathname length, adds .5, then rounds it by adding 221 | and subtracting the special FP constant 040000 0 0 257. This 222 | process ends up adding 1 to the original variable, making the 223 | RUN command fail because the call to TSRC$$ has the pathname 224 | length 1 too big. 225 | 226 | But it needs to be enabled for FP divide, since Prime specifies 227 | that FP divide always rounds. 228 | */ 229 | 230 | if (round) 231 | if ((frac64 & 0x8000) && ((frac64 & 0x7fffffffffff0000LL) != 0x7fffffffffff0000LL)) 232 | { 233 | /* XXX: should this be a subtract for negative numbers? */ 234 | frac64 += 0x10000; 235 | TRACE(T_FP, " ieeepr8: rounded frac %016llx\n", frac64); 236 | } 237 | 238 | *p = swap64((frac64 & 0xffffffffffff0000LL) | (exp32 & 0xffff)); 239 | return okay; 240 | } 241 | 242 | 243 | /* 32-bit signed integer to Prime DPFP conversion */ 244 | 245 | double fltl (int int32) { 246 | long long frac64; 247 | int exp32, sign32; 248 | 249 | /* have to special case zero, or we end up with 250 | a "dirty zero" (zero fraction, exponent of 128) */ 251 | 252 | if (int32 == 0) 253 | return 0.0; 254 | 255 | exp32 = 128+31; 256 | sign32 = int32 & 0x80000000; 257 | if ((int32 & 0xFFFF8000) == sign32>>16) { 258 | int32 = int32<<16; 259 | exp32 -= 16; 260 | } 261 | if ((int32 & 0xFF800000) == sign32>>8) { 262 | int32 = int32<<8; 263 | exp32 -= 8; 264 | } 265 | if ((int32 & 0xF8000000) == sign32>>4) { 266 | int32 = int32<<4; 267 | exp32 -= 4; 268 | } 269 | if ((int32 & 0xE0000000) == sign32>>2) { 270 | int32 = int32<<2; 271 | exp32 -= 2; 272 | } 273 | if ((int32 & 0xC0000000) == sign32>>1) { 274 | int32 = int32<<1; 275 | exp32 -= 1; 276 | } 277 | frac64 = ((long long)int32 << 32) | exp32; 278 | return *(double *)&frac64; 279 | } 280 | 281 | 282 | /* Prime DPFP complement */ 283 | 284 | unsigned long long dfcm (unsigned long long dp, int *oflow) { 285 | long long frac64; 286 | int exp32; 287 | 288 | CLEARC; 289 | *oflow = 0; 290 | getdp(dp, &frac64, &exp32); 291 | TRACE(T_FP, " dfcm: unpacked frac %016llx exp %08x\n", frac64, exp32); 292 | if (frac64 != 0) { /* can't normalize zero */ 293 | if (frac64 == 0x8000000000000000LL) { /* overflow case? */ 294 | frac64 = 0x4000000000000000LL; /* complement power of 2 */ 295 | exp32 += 1; 296 | } else { 297 | frac64 = -frac64; /* complement fraction */ 298 | while ((frac64 ^ (frac64 << 1)) >= 0) { 299 | frac64 = frac64 << 1; /* normalize */ 300 | exp32--; 301 | } 302 | } 303 | if (exp32 > 32767 || exp32 < -32768) 304 | *oflow = 1; 305 | RETFP(frac64, exp32); 306 | } else 307 | RETFP(0, 0); /* DFCM is documented to clean up dirty zeroes */ 308 | } 309 | 310 | 311 | /* double precision floating point normalize 312 | 313 | Passed a Prime double precision variable and returns normalized 314 | value and overflow flag. */ 315 | 316 | unsigned long long norm(unsigned long long dp, int *oflow) { 317 | long long frac64; 318 | int exp32; 319 | 320 | *oflow = 0; 321 | getdp(dp, &frac64, &exp32); 322 | TRACE(T_FP, " norm: unpacked frac %016llx exp %08x\n", frac64, exp32); 323 | while ((frac64 ^ (frac64 << 1)) >= 0) { 324 | frac64 = frac64 << 1; 325 | exp32--; 326 | } 327 | if (exp32 > 32767 || exp32 < -32768) 328 | *oflow = 1; 329 | RETFP(frac64, exp32); 330 | } 331 | 332 | /* double->single floating point round (FRN) instruction. 333 | 334 | Passed a pointer to a Prime double precision variable, one of the 335 | FACC's, and updates it in place. 336 | 337 | NOTE: this routine is coded strangely because I ran into compiler 338 | bugs (gcc 4.0.1) */ 339 | 340 | unsigned long long frn(unsigned long long dp, int *oflow) { 341 | long long frac64; 342 | int exp32; 343 | int doround1, doround2; 344 | 345 | *oflow = 0; 346 | getdp(dp, &frac64, &exp32); 347 | TRACE(T_FP, " frn: unpacked frac %016llx exp %08x\n", frac64, exp32); 348 | if (frac64 == 0) 349 | { 350 | TRACE(T_FP, " frn: returning 0\n"); 351 | return 0; 352 | } 353 | else { 354 | doround1 = ((frac64 & 0x18000000000LL) != 0); 355 | doround2 = ((frac64 & 0x8000000000LL) != 0) && ((frac64 & 0x7FFFFF0000LL) != 0); 356 | TRACE(T_FP, " frn: doround1 %x doround2 %x\n", doround1, doround2); 357 | if (doround1 || doround2) { 358 | frac64 &= 0xFFFFFF0000000000LL; 359 | if (frac64 != 0x7FFFFF0000000000LL) 360 | frac64 += 0x10000000000LL; 361 | else { 362 | frac64 = 0x4000000000000000LL; 363 | exp32++; 364 | } 365 | frac64 |= (exp32 & 0xFFFF); 366 | frac64 = norm(frac64, oflow); 367 | TRACE(T_FP, " frn: rounded frac %016llx\n", frac64); 368 | return frac64; 369 | } 370 | TRACE(T_FP, " frn: dp %016llx\n", dp); 371 | return dp; 372 | } 373 | } 374 | 375 | 376 | /* SPFP comparison, for both FCS (SRV-mode) and FC (I-mode) 377 | For I-mode FC instruction, condition codes are used. 378 | For SRV-mode FCS instruction, return value is the amount 379 | RPL should be advanced. 380 | */ 381 | 382 | int fcs (unsigned long long fac, int fop) { 383 | int templ; 384 | short fopexp, facexp; 385 | 386 | CLEARCC; 387 | templ = (fac & 0xffffff0000000000LL) >> 32; /* FAC SP mantissa */ 388 | if (templ == 0) /* fix dirty zero */ 389 | facexp = 0; 390 | else 391 | facexp = fac & 0xffff; /* FAC exponent */ 392 | fopexp = fop & 0xff; 393 | fop = fop & 0xffffff00; 394 | if (fop == 0) /* fix dirty zero */ 395 | fopexp = 0; 396 | TRACE(T_FP, " fcs: FAC: %016llx %04x; op: %016llx %04x\n", templ, facexp, fop, fopexp); 397 | if ((templ & 0x80000000) == (fop & 0x80000000)) { /* compare signs */ 398 | if (facexp == fopexp) /* compare exponents */ 399 | if (templ == fop) { /* compare fractions */ 400 | TRACE(T_FP, " fcs: frac cmp returning eq / skip 1\n"); 401 | SETEQ; 402 | return 1; 403 | } else if (templ < fop) { /* compare fractions */ 404 | TRACE(T_FP, " fcs: frac cmp returning lt / skip 2\n"); 405 | SETLT; /* FAC < operand */ 406 | return 2; 407 | } else { 408 | TRACE(T_FP, " fcs: frac cmp returning gt / skip 0\n"); 409 | return 0; /* FAC > operand */ 410 | } 411 | else if (facexp < fopexp) { /* compare exponents */ 412 | TRACE(T_FP, " fcs: exp cmp returning lt / skip 2\n"); 413 | SETLT; /* FAC < operand */ 414 | return 2; 415 | } else { 416 | TRACE(T_FP, " fcs: exp cmp returning gt / skip 0\n"); 417 | return 0; 418 | } 419 | } else if (templ & 0x80000000) { 420 | TRACE(T_FP, " fcs: sign cmp returning lt / skip 2\n"); 421 | SETLT; /* FAC < operand */ 422 | return 2; 423 | } else { 424 | TRACE(T_FP, " fcs: sign cmp returning gt / skip 0\n"); 425 | return 0; /* FAC > operand */ 426 | } 427 | } 428 | 429 | 430 | /* DPFP comparison, for both DFCS (SRV-mode) and DFC (I-mode) 431 | For I-mode DFC instruction, condition codes are used. 432 | For SRV-mode DFCS instruction, return value is the amount 433 | RPL should be advanced. 434 | 435 | NOTE: This code doesn't pass Prime diagnostics for higher model 436 | CPU's, I'm guessing because comparison is implemented as subtract, 437 | and we can't do that because numbers with huge exponents (and 438 | Prime ASCII characters in the DAC) won't convert to IEEE. 439 | */ 440 | 441 | int dfcs (unsigned long long fac, long long fop) { 442 | long long templl; 443 | short fopexp, facexp; 444 | 445 | CLEARCC; 446 | templl = fac; 447 | facexp = templl & 0xffff; /* FAC exponent */ 448 | templl = templl & 0xffffffffffff0000LL; /* FAC SP mantissa */ 449 | if (templl == 0) /* fix dirty zero */ 450 | facexp = 0; 451 | fopexp = fop & 0xffff; 452 | fop = fop & 0xffffffffffff0000LL; 453 | if (fop == 0) /* fix dirty zero */ 454 | fopexp = 0; 455 | TRACE(T_FP, " dfcs: FAC: %016llx %04x; op: %016llx %04x\n", templl, facexp, fop, fopexp); 456 | if ((templl & 0x8000000000000000LL) == (fop & 0x8000000000000000LL)) { /* compare signs */ 457 | if (facexp == fopexp) /* compare exponents */ 458 | if (templl == fop) { /* compare fractions */ 459 | TRACE(T_FP, " dfcs: frac cmp returning eq / skip 1\n"); 460 | SETEQ; 461 | return 1; 462 | } else if (templl < fop) { /* compare fractions */ 463 | TRACE(T_FP, " dfcs: frac cmp returning lt / skip 2\n"); 464 | SETLT; /* FAC < operand */ 465 | return 2; 466 | } else { 467 | TRACE(T_FP, " dfcs: frac cmp returning gt / skip 0\n"); 468 | return 0; /* FAC > operand */ 469 | } 470 | else if (facexp < fopexp) { /* compare exponents */ 471 | TRACE(T_FP, " dfcs: exp cmp returning lt / skip 2\n"); 472 | SETLT; /* FAC < operand */ 473 | return 2; 474 | } else { 475 | TRACE(T_FP, " dfcs: exp cmp returning eq / skip 1\n"); 476 | return 0; 477 | } 478 | } else if (templl & 0x8000000000000000LL) { 479 | TRACE(T_FP, " dfcs: sign cmp returning lt / skip 2\n"); 480 | SETLT; /* FAC < operand */ 481 | return 2; 482 | } else { 483 | TRACE(T_FP, " dfcs: sign cmp returning gt / skip 0\n"); 484 | return 0; /* FAC > operand */ 485 | } 486 | } 487 | -------------------------------------------------------------------------------- /dispatch.h: -------------------------------------------------------------------------------- 1 | /* include file to initialize the CPU dispatch tables */ 2 | 3 | /* macros MRGEN_(VR) take as a Prime opcode number and set up the 4 | disp_(vr)mr V and R-mode dispatch tables. The Prime opcode number 5 | for 4-bit opcode (in instruction bits 3-6) 1101 is 015, and Prime 6 | writes it as 01500 in older manuals. If the X bit is used as an 7 | opcode extension (only for opcode 01500 - non-indexable 8 | instructions), the opcode becomes 03500. If bits 7-11 are 11000 9 | (for V-mode; bits 7-12 = 110000 in R-mode), the instruction is long 10 | form and has 2 extended opcode bits in bits 13-14. The Prime 11 | equivalent would be 03500 - 03503. 12 | 13 | To summarize, the mem ref opcode index is a 7-bit value, 0-127: 14 | - bit 10 = bit 2 of instruction (X) 15 | - bits 11-14 = bits 3-6 of instruction 16 | - bits 15-16 = bits 13-14 of extended opcodes 17 | 18 | Instructions like JMP (opcode 01) that might be indexed will have 19 | 2 entries in the dispatch table, 0 0001 00 and 1 0001 00, both 20 | pointing to the JMP emulation code. 21 | */ 22 | 23 | /* change V-mode MR instruction to opcode index */ 24 | 25 | #define VMRINSTIX(inst) ((((inst) >> 8) & 0x7C) | ((((inst) & 0x03E0) == 0x0300) ? (((inst) >> 2) & 3) : 0)) 26 | 27 | /* change R-mode MR instruction to opcode index 28 | (mask is 1 bit longer for R-mode long instructions) */ 29 | 30 | #define RMRINSTIX(inst) ((((inst) >> 8) & 0x7C) | ((((inst) & 0x03F0) == 0x0300) ? (((inst) >> 2) & 3) : 0)) 31 | 32 | /* change S-mode MR instruction to opcode index (no long instructions) */ 33 | 34 | #define SMRINSTIX(inst) (((inst) >> 8) & 0x7C) 35 | 36 | /* change "Prime manual" opcodes (35 03 for example) to dispatch index */ 37 | 38 | #define MRPRIMEIX(primeop) (((primeop) >> 4) | ((primeop) & 3)) 39 | 40 | /* set an entry in the R-mode memory reference opcode dispatch table */ 41 | 42 | #define MRGEN_R(opcode, name, target) \ 43 | gv.disp_rmr[MRPRIMEIX(opcode)] = &⌖ \ 44 | /* printf("R-MR opcode %05o (%s), ix=%0d\n", opcode, name, MRPRIMEIX(opcode)); */ \ 45 | if ((opcode & 01700) != 01500) { \ 46 | gv.disp_rmr[MRPRIMEIX(opcode | 02000)] = &⌖ \ 47 | /* printf("R-MR opcode %05o (%s), ix=%0d\n", opcode | 02000, name, MRPRIMEIX(opcode | 02000)); */ \ 48 | } 49 | 50 | /* set an entry in the V-mode memory reference opcode dispatch table */ 51 | 52 | #define MRGEN_V(opcode, name, target) \ 53 | gv.disp_vmr[MRPRIMEIX(opcode)] = &⌖ \ 54 | /* printf("V-MR opcode %05o (%s), ix=%0d\n", opcode, name, MRPRIMEIX(opcode)); */ \ 55 | if ((opcode & 01700) != 01500) { \ 56 | gv.disp_vmr[MRPRIMEIX(opcode | 02000)] = &⌖ \ 57 | /* printf("V-MR opcode %05o (%s), ix=%0d\n", opcode | 02000, name, MRPRIMEIX(opcode | 02000)); */ \ 58 | } 59 | 60 | /* initialize tables to "bad memory reference instruction" */ 61 | 62 | for (i=0; i < 128; i++) { 63 | gv.disp_rmr[i] = &&d_badmr; 64 | gv.disp_vmr[i] = &&d_badmr; 65 | } 66 | 67 | MRGEN_R(00100, "JMP", d_jmp); 68 | MRGEN_V(00100, "JMP", d_jmp); 69 | 70 | MRGEN_R(00101, "EAA", d_eaa); 71 | MRGEN_V(00101, "EAL", d_eal); 72 | 73 | MRGEN_R(00102, "XEC", d_xec); 74 | MRGEN_V(00102, "XEC", d_xec); 75 | 76 | MRGEN_R(00103, "ENTR", d_entr); 77 | MRGEN_V(00103, "ENTR?", d_uii); 78 | 79 | MRGEN_R(00200, "LDA/DLD", d_ldadld); 80 | MRGEN_V(00200, "LDA", d_lda); 81 | 82 | MRGEN_R(00201, "FLD", d_fld); 83 | MRGEN_V(00201, "FLD", d_fld); 84 | 85 | MRGEN_R(00202, "DFLD", d_dfld); 86 | MRGEN_V(00202, "DFLD", d_dfld); 87 | 88 | MRGEN_R(00203, "JEQ", d_jeq); 89 | MRGEN_V(00203, "LDL", d_ldl); 90 | 91 | MRGEN_R(00300, "ANA", d_ana); 92 | MRGEN_V(00300, "ANA", d_ana); 93 | 94 | MRGEN_R(00301, "STLR?", d_uii); 95 | MRGEN_V(00301, "STLR", d_stlr); 96 | 97 | MRGEN_R(00302, "ORA", d_ora); 98 | MRGEN_V(00302, "ORA", d_ora); 99 | 100 | MRGEN_R(00303, "JNE", d_jne); 101 | MRGEN_V(00303, "ANL", d_anl); 102 | 103 | MRGEN_R(00400, "STA/DST", d_stadst); 104 | MRGEN_V(00400, "STA", d_sta); 105 | 106 | MRGEN_R(00401, "FST", d_fst); 107 | MRGEN_V(00401, "FST", d_fst); 108 | 109 | MRGEN_R(00402, "DFST", d_dfst); 110 | MRGEN_V(00402, "DFST", d_dfst); 111 | 112 | MRGEN_R(00403, "JLE", d_jle); 113 | MRGEN_V(00403, "STL", d_stl); 114 | 115 | MRGEN_R(00500, "ERA", d_era); 116 | MRGEN_V(00500, "ERA", d_era); 117 | 118 | MRGEN_R(00501, "LDLR?", d_uii); 119 | MRGEN_V(00501, "LDLR", d_ldlr); 120 | 121 | MRGEN_R(00502, "QFxx", d_qfxxuii); 122 | MRGEN_V(00502, "QFxx", d_qfxxuii); 123 | 124 | MRGEN_R(00503, "JGT", d_jgt); 125 | MRGEN_V(00503, "ERL", d_erl); 126 | 127 | MRGEN_R(00600, "ADD/DAD", d_adddad); 128 | MRGEN_V(00600, "ADD", d_add); 129 | 130 | MRGEN_R(00601, "FAD", d_fad); 131 | MRGEN_V(00601, "FAD", d_fad); 132 | 133 | MRGEN_R(00602, "DFAD", d_dfad); 134 | MRGEN_V(00602, "DFAD", d_dfad); 135 | 136 | MRGEN_R(00603, "JLT", d_jlt); 137 | MRGEN_V(00603, "ADL", d_adl); 138 | 139 | MRGEN_R(00700, "SUB/DSB", d_subdsb); 140 | MRGEN_V(00700, "SUB", d_sub); 141 | 142 | MRGEN_R(00701, "FSB", d_fsb); 143 | MRGEN_V(00701, "FSB", d_fsb); 144 | 145 | MRGEN_R(00702, "DFSB", d_dfsb); 146 | MRGEN_V(00702, "DFSB", d_dfsb); 147 | 148 | MRGEN_R(00703, "JGE", d_jge); 149 | MRGEN_V(00703, "SBL", d_sbl); 150 | 151 | MRGEN_R(01000, "JST", d_jst); 152 | MRGEN_V(01000, "JST", d_jst); 153 | 154 | MRGEN_R(01002, "CREP", d_crep); 155 | MRGEN_V(01002, "PCL", d_pcl); 156 | 157 | MRGEN_R(01100, "CAS", d_cas); 158 | MRGEN_V(01100, "CAS", d_cas); 159 | 160 | MRGEN_R(01101, "FCS", d_fcs); 161 | MRGEN_V(01101, "FCS", d_fcs); 162 | 163 | MRGEN_R(01102, "DFCS", d_dfcs); 164 | MRGEN_V(01102, "DFCS", d_dfcs); 165 | 166 | MRGEN_R(01103, "CLS?", d_uii); 167 | MRGEN_V(01103, "CLS", d_cls); 168 | 169 | MRGEN_R(01200, "IRS", d_irs); 170 | MRGEN_V(01200, "IRS", d_irs); 171 | 172 | MRGEN_R(01202, "EAXB?", d_uii); 173 | MRGEN_V(01202, "EAXB", d_eaxb); 174 | 175 | MRGEN_R(01300, "IMA", d_ima); 176 | MRGEN_V(01300, "IMA", d_ima); 177 | 178 | MRGEN_R(01302, "EALB?", d_uii); 179 | MRGEN_V(01302, "EALB", d_ealb); 180 | 181 | MRGEN_R(01400, "JSY?", d_uii); 182 | MRGEN_V(01400, "JSY", d_jsy); 183 | 184 | MRGEN_R(01401, "EIO?", d_uii); 185 | MRGEN_V(01401, "EIO", d_eio); 186 | 187 | MRGEN_R(01402, "JSXB?", d_uii); 188 | MRGEN_V(01402, "JSXB", d_jsxb); 189 | 190 | MRGEN_R(01500, "STX", d_stx); 191 | MRGEN_V(01500, "STX", d_stx); 192 | 193 | MRGEN_R(01501, "FLX", d_flx); 194 | MRGEN_V(01501, "FLX", d_flx); 195 | 196 | MRGEN_R(01502, "JDX", d_jdx); 197 | MRGEN_V(01502, "DFLX", d_dflx); 198 | 199 | MRGEN_R(01503, "JIX", d_jix); 200 | MRGEN_V(01503, "QFLX", d_qflx); 201 | 202 | MRGEN_R(01600, "MPY", d_mpy_r); 203 | MRGEN_V(01600, "MPY", d_mpy); 204 | 205 | MRGEN_R(01601, "FMP", d_fmp); 206 | MRGEN_V(01601, "FMP", d_fmp); 207 | 208 | MRGEN_R(01602, "DFMP", d_dfmp); 209 | MRGEN_V(01602, "DFMP", d_dfmp); 210 | 211 | MRGEN_R(01603, "MPL?", d_uii); 212 | MRGEN_V(01603, "MPL", d_mpl); 213 | 214 | MRGEN_R(01700, "DIV", d_div); 215 | MRGEN_V(01700, "DIV", d_div); 216 | 217 | MRGEN_R(01701, "FDV", d_fdv); 218 | MRGEN_V(01701, "FDV", d_fdv); 219 | 220 | MRGEN_R(01702, "DFDV", d_dfdv); 221 | MRGEN_V(01702, "DFDV", d_dfdv); 222 | 223 | MRGEN_R(01703, "DVL?", d_uii); 224 | MRGEN_V(01703, "DVL", d_dvl); 225 | 226 | MRGEN_R(03500, "LDX", d_ldx); 227 | MRGEN_V(03500, "LDX", d_ldx); 228 | 229 | MRGEN_R(03501, "LDY?", d_uii); 230 | MRGEN_V(03501, "LDY", d_ldy); 231 | 232 | MRGEN_R(03502, "STY?", d_uii); 233 | MRGEN_V(03502, "STY", d_sty); 234 | 235 | MRGEN_R(03503, "JSX", d_jsx); 236 | MRGEN_V(03503, "JSX", d_jsx); 237 | 238 | 239 | #define GENIX(inst) ((inst>>4) & 06000) | (inst & 01777) 240 | 241 | #define DIGEN(opcode, name, target) \ 242 | disp_gen[GENIX(opcode)] = &⌖ \ 243 | //printf("Opcode %06o (%s), ix=%0o\n", opcode, name, GENIX(opcode)) 244 | 245 | /* initialize entire table to jump to bad generic label */ 246 | 247 | for (i=0; i < 010000; i++) { 248 | disp_gen[i] = &&d_badgen; 249 | } 250 | 251 | /* initialize class 0 generics (first 2 bits are zero) */ 252 | 253 | DIGEN(000000, "HLT", d_hlt); 254 | DIGEN(000201, "IAB", d_iab); 255 | DIGEN(001314, "CGT", d_cgt); 256 | DIGEN(000115, "PIDA", d_pida); 257 | DIGEN(000305, "PIDL", d_pidl); 258 | DIGEN(000015, "PIMA", d_pima); 259 | DIGEN(000301, "PIML", d_piml); 260 | DIGEN(001302, "LDC 0", d_ldc0); 261 | DIGEN(001312, "LDC 1", d_ldc1); 262 | DIGEN(001322, "STC 0", d_stc0); 263 | DIGEN(001332, "STC 1", d_stc1); 264 | DIGEN(001300, "EAFA 0", d_eafa0); 265 | DIGEN(001310, "EAFA 1", d_eafa1); 266 | DIGEN(001301, "ALFA 0", d_alfa0); 267 | DIGEN(001311, "ALFA 1", d_alfa1); 268 | DIGEN(001303, "LFLI 0", d_lfli0); 269 | DIGEN(001313, "LFLI 1", d_lfli1); 270 | DIGEN(001320, "STFA 0", d_stfa0); 271 | DIGEN(001330, "STFA 1", d_stfa1); 272 | DIGEN(001321, "TLFL 0", d_tlfl0); 273 | DIGEN(001331, "TLFL 1", d_tlfl1); 274 | DIGEN(001323, "TFLL 0", d_tfll0); 275 | DIGEN(001333, "TFLL 1", d_tfll1); 276 | DIGEN(000611, "PRTN", d_prtn); 277 | DIGEN(001005, "TKA", d_tka); 278 | DIGEN(001015, "TAK", d_tak); 279 | DIGEN(000001, "NOP 1", d_nop); 280 | DIGEN(000715, "RSAV", d_rsav); 281 | DIGEN(000717, "RRST", d_rrst); 282 | DIGEN(000400, "ENBM", d_enb); 283 | DIGEN(000401, "ENBL", d_enb); 284 | DIGEN(000402, "ENBP", d_enb); 285 | DIGEN(001000, "INHM", d_inh); 286 | DIGEN(001001, "INHL", d_inh); 287 | DIGEN(001002, "INHP", d_inh); 288 | DIGEN(001200, "STAC", d_stac); 289 | DIGEN(001204, "STLC", d_stlc); 290 | DIGEN(000605, "ARGT", d_argt); 291 | DIGEN(000705, "CALF", d_calf); 292 | DIGEN(001114, "ZMV", d_zmv); 293 | DIGEN(001115, "ZMVD", d_zmvd); 294 | DIGEN(001116, "ZFIL", d_zfil); 295 | DIGEN(001117, "ZCM", d_zcm); 296 | DIGEN(001110, "ZTRN", d_ztrn); 297 | DIGEN(001111, "ZED", d_zed); 298 | DIGEN(001112, "XED", d_xed); 299 | DIGEN(001100, "XAD", d_xuii); 300 | DIGEN(001101, "XMV", d_xuii); 301 | DIGEN(001102, "XCM", d_xuii); 302 | DIGEN(001104, "XMP", d_xuii); 303 | DIGEN(001107, "XDV", d_xuii); 304 | DIGEN(001145, "XBTD", d_xuii); 305 | DIGEN(001146, "XDTB", d_xuii); 306 | DIGEN(000510, "STTM", d_sttm); 307 | DIGEN(000511, "RTS", d_rts); 308 | DIGEN(000315, "WAIT", d_wait); 309 | DIGEN(001210, "NFYE", d_nfy); 310 | DIGEN(001211, "NFYB", d_nfy); 311 | DIGEN(001214, "INEN", d_nfy); 312 | DIGEN(001215, "INBN", d_nfy); 313 | DIGEN(001216, "INEC", d_nfy); 314 | DIGEN(001217, "INBC", d_nfy); 315 | DIGEN(001315, "STEX", d_stex); 316 | DIGEN(000044, "LIOT", d_liot); 317 | DIGEN(000064, "PTLB", d_ptlb); 318 | DIGEN(000615, "ITLB", d_itlb); 319 | DIGEN(000711, "LPSW", d_lpsw); 320 | DIGEN(000024, "STPM", d_stpm); 321 | DIGEN(001700, "DBG0", d_dbgill); 322 | DIGEN(001701, "DBG1", d_dbgill); 323 | DIGEN(001702, "PBUG", d_pbug); 324 | DIGEN(000601, "IRTN", d_irtn); 325 | DIGEN(000603, "IRTC", d_irtc); 326 | DIGEN(000411, "CAI", d_cai); 327 | DIGEN(000005, "SGL", d_sgl); 328 | DIGEN(000011, "E16S", d_e16s); 329 | DIGEN(000013, "E32S", d_e32s); 330 | DIGEN(001013, "E32R", d_e32r); 331 | DIGEN(001011, "E64R", d_e64r); 332 | DIGEN(000010, "E64V", d_e64v); 333 | DIGEN(001010, "E32I", d_e32i); 334 | DIGEN(000505, "SVC", d_svc); 335 | DIGEN(000111, "CEA", d_cea); 336 | DIGEN(000205, "PIM", d_pim); /* R-mode */ 337 | DIGEN(000211, "PID", d_pid); /* R-mode */ 338 | DIGEN(000007, "DBL", d_dbl); /* R-mode */ 339 | DIGEN(000041, "SCA", d_sca); /* R-mode */ 340 | DIGEN(000043, "INKr", d_inkr); /* R-mode */ 341 | DIGEN(000405, "OTKr", d_otkr); /* R-mode */ 342 | DIGEN(000415, "ESIM", d_esim); 343 | DIGEN(000417, "EVIM", d_evim); 344 | DIGEN(000101, "NRM", d_nrm); /* R-mode */ 345 | DIGEN(000105, "RTN", d_rtn); /* R-mode */ 346 | DIGEN(000003, "SYNC", d_sync); 347 | DIGEN(000503, "EMCM", d_emcm); 348 | DIGEN(000501, "LMCM", d_lmcm); 349 | DIGEN(000021, "RMC", d_rmc); 350 | DIGEN(000311, "VIRY", d_viry); 351 | DIGEN(001113, "XVFY", d_xvfy); 352 | DIGEN(001304, "MDEI", d_mdxx); 353 | DIGEN(001305, "MDII", d_mdxx); 354 | DIGEN(001306, "MDRS", d_mdxx); 355 | DIGEN(001307, "MDWC", d_mdxx); 356 | DIGEN(001324, "MDIW", d_mdxx); 357 | 358 | DIGEN(000217, "EPMJ", d_uii); 359 | DIGEN(000215, "LPMJ", d_uii); 360 | DIGEN(000237, "EPMX", d_uii); 361 | DIGEN(000235, "LPMX", d_uii); 362 | DIGEN(000703, "EVMJ", d_uii); 363 | DIGEN(000701, "ERMJ", d_uii); 364 | DIGEN(000723, "EVMX", d_uii); 365 | DIGEN(000721, "ERMX", d_uii); 366 | DIGEN(000515, "OSI", d_uii); 367 | 368 | DIGEN(000617, "LPID", d_lpid); 369 | 370 | /* initialize entire class 1 generics (shift group) to "badshift", 371 | then initialize each individual shift instruction */ 372 | 373 | for (i = 02000; i < 04000; i++) { 374 | disp_gen[i] = &&d_badshift; 375 | } 376 | 377 | for (i = 02000; i < 02100; i++) { 378 | disp_gen[i] = &&d_lrl; 379 | } 380 | 381 | for (i = 02100; i < 02200; i++) { 382 | disp_gen[i] = &&d_lrs; 383 | } 384 | 385 | for (i = 02200; i < 02300; i++) { 386 | disp_gen[i] = &&d_lrr; 387 | } 388 | 389 | for (i = 02300; i < 02400; i++) { 390 | disp_gen[i] = &&d_300shift; 391 | } 392 | 393 | for (i = 02400; i < 02500; i++) { 394 | disp_gen[i] = &&d_arl; 395 | } 396 | 397 | for (i = 02500; i < 02600; i++) { 398 | disp_gen[i] = &&d_ars; 399 | } 400 | 401 | for (i = 02600; i < 02700; i++) { 402 | disp_gen[i] = &&d_arr; 403 | } 404 | 405 | for (i = 03000; i < 03100; i++) { 406 | disp_gen[i] = &&d_lll; 407 | } 408 | 409 | for (i = 03100; i < 03200; i++) { 410 | disp_gen[i] = &&d_lls; 411 | } 412 | 413 | for (i = 03200; i < 03300; i++) { 414 | disp_gen[i] = &&d_llr; 415 | } 416 | 417 | for (i = 03400; i < 03500; i++) { 418 | disp_gen[i] = &&d_all; 419 | } 420 | 421 | for (i = 03500; i < 03600; i++) { 422 | disp_gen[i] = &&d_als; 423 | } 424 | 425 | for (i = 03600; i < 03700; i++) { 426 | disp_gen[i] = &&d_alr; 427 | } 428 | 429 | /* initialize class 2 generics (skip group) */ 430 | 431 | #if 0 432 | for (i = 04000; i < 06000; i++) { 433 | disp_gen[i] = &&d_gen2; 434 | } 435 | #endif 436 | 437 | DIGEN(0101000, "NOP-SKP", d_nopskp); 438 | DIGEN(0100000, "SKP", d_skp); 439 | DIGEN(0101400, "SMI/SLT", d_smi); 440 | DIGEN(0100400, "SPL/SGE", d_spl); 441 | DIGEN(0101100, "SLN", d_sln); 442 | DIGEN(0100100, "SLZ", d_slz); 443 | DIGEN(0101040, "SNZ/SNE", d_snz); 444 | DIGEN(0100040, "SZE/SEQ", d_sze); 445 | DIGEN(0101220, "SLE", d_sle); 446 | DIGEN(0100220, "SGT", d_sgt); 447 | DIGEN(0101001, "SSC", d_ssc); 448 | DIGEN(0100001, "SRC", d_src); 449 | DIGEN(0100260, "SAR 1", d_sar); 450 | DIGEN(0100261, "SAR 2", d_sar); 451 | DIGEN(0100262, "SAR 3", d_sar); 452 | DIGEN(0100263, "SAR 4", d_sar); 453 | DIGEN(0100264, "SAR 5", d_sar); 454 | DIGEN(0100265, "SAR 6", d_sar); 455 | DIGEN(0100266, "SAR 7", d_sar); 456 | DIGEN(0100267, "SAR 8", d_sar); 457 | DIGEN(0100270, "SAR 9", d_sar); 458 | DIGEN(0100271, "SAR 10", d_sar); 459 | DIGEN(0100272, "SAR 11", d_sar); 460 | DIGEN(0100273, "SAR 12", d_sar); 461 | DIGEN(0100274, "SAR 13", d_sar); 462 | DIGEN(0100275, "SAR 14", d_sar); 463 | DIGEN(0100276, "SAR 15", d_sar); 464 | DIGEN(0100277, "SAR 16", d_sar); 465 | DIGEN(0101260, "SAS 1", d_sas); 466 | DIGEN(0101261, "SAS 2", d_sas); 467 | DIGEN(0101262, "SAS 3", d_sas); 468 | DIGEN(0101263, "SAS 4", d_sas); 469 | DIGEN(0101264, "SAS 5", d_sas); 470 | DIGEN(0101265, "SAS 6", d_sas); 471 | DIGEN(0101266, "SAS 7", d_sas); 472 | DIGEN(0101267, "SAS 8", d_sas); 473 | DIGEN(0101270, "SAS 9", d_sas); 474 | DIGEN(0101271, "SAS 10", d_sas); 475 | DIGEN(0101272, "SAS 11", d_sas); 476 | DIGEN(0101273, "SAS 12", d_sas); 477 | DIGEN(0101274, "SAS 13", d_sas); 478 | DIGEN(0101275, "SAS 14", d_sas); 479 | DIGEN(0101276, "SAS 15", d_sas); 480 | DIGEN(0101277, "SAS 16", d_sas); 481 | DIGEN(0100240, "SNR 1", d_snr); 482 | DIGEN(0100241, "SNR 2", d_snr); 483 | DIGEN(0100242, "SNR 3", d_snr); 484 | DIGEN(0100243, "SNR 4", d_snr); 485 | DIGEN(0100244, "SNR 5", d_snr); 486 | DIGEN(0100245, "SNR 6", d_snr); 487 | DIGEN(0100246, "SNR 7", d_snr); 488 | DIGEN(0100247, "SNR 8", d_snr); 489 | DIGEN(0100250, "SNR 9", d_snr); 490 | DIGEN(0100251, "SNR 10", d_snr); 491 | DIGEN(0100252, "SNR 11", d_snr); 492 | DIGEN(0100253, "SNR 12", d_snr); 493 | DIGEN(0100254, "SNR 13", d_snr); 494 | DIGEN(0100255, "SNR 14", d_snr); 495 | DIGEN(0100256, "SNR 15", d_snr); 496 | DIGEN(0100257, "SNR 16", d_snr); 497 | DIGEN(0101240, "SNS 1", d_sns); 498 | DIGEN(0101241, "SNS 2", d_sns); 499 | DIGEN(0101242, "SNS 3", d_sns); 500 | DIGEN(0101243, "SNS 4", d_sns); 501 | DIGEN(0101244, "SNS 5", d_sns); 502 | DIGEN(0101245, "SNS 6", d_sns); 503 | DIGEN(0101246, "SNS 7", d_sns); 504 | DIGEN(0101247, "SNS 8", d_sns); 505 | DIGEN(0101250, "SNS 9", d_sns); 506 | DIGEN(0101251, "SNS 10", d_sns); 507 | DIGEN(0101252, "SNS 11", d_sns); 508 | DIGEN(0101253, "SNS 12", d_sns); 509 | DIGEN(0101254, "SNS 13", d_sns); 510 | DIGEN(0101255, "SNS 14", d_sns); 511 | DIGEN(0101256, "SNS 15", d_sns); 512 | DIGEN(0101257, "SNS 16", d_sns); 513 | DIGEN(0100200, "SMCR", d_smcr); 514 | DIGEN(0101200, "SMCS", d_smcs); 515 | DIGEN(0101020, "SS1", d_ssx); 516 | DIGEN(0100020, "SR1", d_srx); 517 | DIGEN(0101010, "SS2", d_ssx); 518 | DIGEN(0100010, "SR2", d_srx); 519 | DIGEN(0101004, "SS3", d_ssx); 520 | DIGEN(0100004, "SR3", d_srx); 521 | DIGEN(0101002, "SS4", d_ssx); 522 | DIGEN(0100002, "SR4", d_srx); 523 | DIGEN(0101036, "SSS", d_ssx); 524 | DIGEN(0100036, "SSR", d_srx); 525 | 526 | /* initialize class 3 generics */ 527 | 528 | DIGEN(0141604, "BCLT", d_bclt); 529 | DIGEN(0141600, "BCLE", d_bcle); 530 | DIGEN(0141602, "BCEQ", d_bceq); 531 | DIGEN(0141603, "BCNE", d_bcne); 532 | DIGEN(0141605, "BCGE", d_bcge); 533 | DIGEN(0141601, "BCGT", d_bcgt); 534 | DIGEN(0141705, "BCR", d_bcr); 535 | DIGEN(0141704, "BCS", d_bcs); 536 | DIGEN(0141707, "BLR", d_blr); /* also BMLT */ 537 | DIGEN(0141706, "BLS", d_bls); 538 | DIGEN(0140614, "BLT", d_blt); 539 | DIGEN(0140610, "BLE", d_ble); 540 | DIGEN(0140612, "BEQ", d_beq); 541 | DIGEN(0140613, "BNE", d_bne); 542 | DIGEN(0140615, "BGE", d_bge); 543 | DIGEN(0140611, "BGT", d_bgt); 544 | DIGEN(0140700, "BLLE", d_blle); 545 | DIGEN(0140702, "BLEQ", d_bleq); 546 | DIGEN(0140703, "BLNE", d_blne); 547 | DIGEN(0140701, "BLGT", d_blgt); 548 | DIGEN(0141614, "BFLT", d_bflt); 549 | DIGEN(0141610, "BFLE", d_bfle); 550 | DIGEN(0141612, "BFEQ", d_bfeq); 551 | DIGEN(0141613, "BFNE", d_bfne); 552 | DIGEN(0141615, "BFGE", d_bfge); 553 | DIGEN(0141611, "BFGT", d_bfgt); 554 | DIGEN(0141334, "BIX", d_bix); 555 | DIGEN(0141324, "BIY", d_biy); 556 | DIGEN(0140724, "BDY", d_bdy); 557 | DIGEN(0140734, "BDX", d_bdx); 558 | DIGEN(0141206, "A1A", d_a1a); /* aka AOA */ 559 | DIGEN(0140304, "A2A", d_a2a); 560 | DIGEN(0141216, "ACA", d_aca); 561 | DIGEN(0140110, "S1A", d_s1a); 562 | DIGEN(0140310, "S2A", d_s2a); 563 | DIGEN(0141050, "CAL", d_cal); 564 | DIGEN(0141044, "CAR", d_car); 565 | DIGEN(0140040, "CRA", d_cra); 566 | DIGEN(0140014, "CRB300", d_crb300); 567 | DIGEN(0140015, "CRB", d_crb); 568 | DIGEN(0140016, "FDBL", d_fdbl); 569 | DIGEN(0140010, "CRL", d_crl); 570 | DIGEN(0140214, "CAZ", d_caz); 571 | DIGEN(0140114, "IRX", d_irx); 572 | DIGEN(0140210, "DRX", d_drx); 573 | DIGEN(0141240, "ICR", d_icr); 574 | DIGEN(0141140, "ICL", d_icl); 575 | DIGEN(0141340, "ICA", d_ica); 576 | DIGEN(0140417, "LT", d_lt); 577 | DIGEN(0140416, "LF", d_lf); 578 | DIGEN(0140314, "TAB", d_tab); 579 | DIGEN(0140504, "TAX", d_tax); 580 | DIGEN(0140505, "TAY", d_tay); 581 | DIGEN(0140604, "TBA", d_tba); 582 | DIGEN(0141034, "TXA", d_txa); 583 | DIGEN(0141124, "TYA", d_tya); 584 | DIGEN(0140104, "XCA", d_xca); 585 | DIGEN(0140204, "XCB", d_xcb); 586 | DIGEN(0140407, "TCA", d_tca); 587 | DIGEN(0141210, "TCL", d_tcl); 588 | DIGEN(0140600, "SCB", d_scb); 589 | DIGEN(0140200, "RCB", d_rcb); 590 | DIGEN(0140024, "CHS", d_chs); 591 | DIGEN(0140500, "SSM", d_ssm); 592 | DIGEN(0140100, "SSP", d_ssp); 593 | DIGEN(0140401, "CMA", d_cma); 594 | DIGEN(0140320, "CSA", d_csa); 595 | DIGEN(0141500, "LCLT", d_lclt); 596 | DIGEN(0141501, "LCLE", d_lcle); 597 | DIGEN(0141503, "LCEQ", d_lceq); 598 | DIGEN(0141502, "LCNE", d_lcne); 599 | DIGEN(0141504, "LCGE", d_lcge); 600 | DIGEN(0141505, "LCGT", d_lcgt); 601 | DIGEN(0140410, "LLT", d_llt); 602 | DIGEN(0140411, "LLE", d_lle); 603 | DIGEN(0140412, "LNE", d_lne); 604 | DIGEN(0140413, "LEQ", d_leq); 605 | DIGEN(0140414, "LGE", d_lge); 606 | DIGEN(0140415, "LGT", d_lgt); 607 | DIGEN(0141511, "LLLE", d_llle); 608 | DIGEN(0141513, "LLEQ", d_lleq); 609 | DIGEN(0141512, "LLNE", d_llne); 610 | DIGEN(0141515, "LLGT", d_llgt); 611 | DIGEN(0141110, "LFLT", d_lflt); 612 | DIGEN(0141111, "LFLE", d_lfle); 613 | DIGEN(0141113, "LFEQ", d_lfeq); 614 | DIGEN(0141112, "LFNE", d_lfne); 615 | DIGEN(0141114, "LFGE", d_lfge); 616 | DIGEN(0141115, "LFGT", d_lfgt); 617 | DIGEN(0140550, "FLOT", d_flot); 618 | DIGEN(0140534, "FRN", d_frn); 619 | DIGEN(0140574, "DFCM", d_dfcm); 620 | DIGEN(0141000, "ADLL", d_adll); 621 | DIGEN(0140530, "FCMv", d_fcmv); 622 | DIGEN(0140510, "FSZE", d_fsze); 623 | DIGEN(0140511, "FSNZ", d_fsnz); 624 | DIGEN(0140512, "FSMI", d_fsmi); 625 | DIGEN(0140513, "FSPL", d_fspl); 626 | DIGEN(0140514, "FSLE", d_fsle); 627 | DIGEN(0140515, "FSGT", d_fsgt); 628 | DIGEN(0140554, "INT", d_int); 629 | DIGEN(0140531, "INTA", d_inta); 630 | DIGEN(0140532, "FLTA", d_flta); 631 | DIGEN(0140533, "INTL", d_intl); 632 | DIGEN(0140535, "FLTL", d_fltl); 633 | DIGEN(0141711, "BMLE", d_bmle); 634 | /* DIGEN(0141606, "BMGE", d_bmge); */ /* replaced by BLS */ 635 | DIGEN(0141710, "BMGT", d_bmgt); 636 | DIGEN(0141404, "CRE", d_cre); 637 | DIGEN(0141410, "CRLE", d_crle); 638 | DIGEN(0141414, "ILE", d_ile); 639 | DIGEN(0140570, "QFCM", d_quii); 640 | DIGEN(0140571, "DRNM", d_quii); 641 | DIGEN(0140572, "QINQ", d_quii); 642 | DIGEN(0140573, "QIQR", d_quii); 643 | DIGEN(0141714, "RTQ", d_rtq); 644 | DIGEN(0141715, "RBQ", d_rbq); 645 | DIGEN(0141716, "ABQ", d_abq); 646 | DIGEN(0141717, "ATQ", d_atq); 647 | DIGEN(0141757, "TSTQ", d_tstq); 648 | DIGEN(0141700, "DIAGILL", d_diagill); 649 | -------------------------------------------------------------------------------- /util/magrst.c: -------------------------------------------------------------------------------- 1 | /* magrst.c, Jim Wilcoxson, March 19, 2005 2 | Reads both old and new (drb) format Magsav tape files on Unix. 3 | 4 | Still to do: 5 | - nested segdirs haven't been tested 6 | - .TAP files aren't handled directly - need to untap first 7 | - file types are lost, so Unix Magsav has to guess the file type 8 | - filename translation (slashes, etc.) is not well thought out 9 | - make a hidden symlink for max entries in a segdir (emulation of segdir) 10 | - acls and category acls aren't handled 11 | 12 | - roam, rbf, and cam entries aren't handled (who cares!) 13 | - print a warning/error if reels are out of order 14 | - no partial restores 15 | - not tested on multi-reel backups 16 | - does a zero-length file have a data record? 17 | - needs error check for path and buf overflows 18 | - should have a force-overwrite option, and not overwrite otherwise 19 | - an option to save the boot program to disk? 20 | - an option to just display an index with gory details? 21 | - an option to not lowercase filenames? 22 | - ptimestampu should check to see if current timezone observes DST 23 | - isptext maybe should ensure :001 is at the beginning of the line 24 | - a single non-text character prevents text conversion; use a percentage? 25 | - when weirdness happens, set "skipping" more often instead of bombing 26 | - test that skipping=1 actually works 27 | - have a list of suffixes that are most likely text (.cpl, .list, etc) 28 | and a list that are most likely binary (.bin, .run, etc.). If tasting 29 | the file shows a different type, prompt user (or write both types and 30 | let them choose later, in the emulator) 31 | */ 32 | 33 | #include 34 | #include 35 | #include 36 | #include /* mkdir */ 37 | #include /* mkdir */ 38 | #include 39 | #include /* mktime */ 40 | #include /* utimes */ 41 | 42 | /* Magsav and "new" Magsav (aka drb) record ids. 43 | NOTE: magsav recid's are positive on the tape! */ 44 | 45 | #define MS_DATA -1 46 | #define MS_NAME -2 47 | #define MS_START_LOG_TAPE -4 48 | #define MS_END_LOG_TAPE -5 49 | 50 | #define DRB_START_LOG_TAPE 1 51 | #define DRB_START_OBJ 2 52 | #define DRB_ACL_DATA 3 53 | #define DRB_ACAT_DATA 4 54 | #define DRB_FILE_DATA 5 55 | #define DRB_ROAM_DATA 6 56 | #define DRB_END_OBJ 7 57 | #define DRB_END_LOG_TAPE 8 58 | 59 | #define DRB_DIR_OBJ 1 60 | #define DRB_FILE_OBJ 2 61 | #define DRB_SEGDIR_OBJ 3 62 | #define DRB_ACAT_OBJ 4 63 | #define DRB_ROAM_OBJ 5 64 | #define DRB_ROAM_SEGDIR_OBJ 6 65 | #define DRB_SEG_SUBFILE_OBJ 7 66 | #define DRB_SEG_SUBDIR_OBJ 8 67 | 68 | 69 | /* writes a buffer of Prime text, converting it to Unix text. 70 | The 2-character space compression sequences may cross tape 71 | buffers; "state" is used to track this: 72 | 73 | state=0 means no compression pending 74 | state=1 means the next buffer character is the compression count 75 | 76 | Before calling convtext for a new file, state must be initialized 77 | to zero in the caller, then left alone after that. 78 | */ 79 | 80 | int convtext(int fd, unsigned char *buf, int len, int *state) { 81 | int i, n; 82 | unsigned char ch; 83 | 84 | /* NOTE: one interation through the text conversion loop could add up 85 | to 255 spaces because of text compression, so some slop is added 86 | to the size of the output buffer in the declaration */ 87 | 88 | #define OBUFMAX 4096 89 | unsigned char obuf[OBUFMAX+256]; 90 | 91 | n = 0; /* next output buffer postion */ 92 | for (i=0; i= OBUFMAX) { 106 | if (fd > 0 && write(fd, obuf, n) != n) { 107 | fprintf(stderr,"File write error text conversion, n=%d\n", n); 108 | exit(1); 109 | } 110 | n = 0; 111 | } 112 | } 113 | if (fd > 0 && n > 0 && write(fd, obuf, n) != n) { 114 | fprintf(stderr,"File write error text conversion, n=%d\n", n); 115 | exit(1); 116 | } 117 | return len; 118 | } 119 | 120 | 121 | /* this function takes a Prime filesystem timestamp (a 32-bit integer) 122 | and returns a Unix timestamp. Since Primos doesn't store timezone 123 | information in the timestamp, the current timezone is used. 124 | Format of a Prime FS timestamp is: 125 | 126 | left 16 bits: YYYYYYYMMMMDDDDD, year is mod 100 127 | right 16 bits: seconds since midnight divided by 4, ie, 0-21599 128 | */ 129 | 130 | time_t ptimestampu(unsigned int ptime) { 131 | int i; 132 | time_t unixtime; 133 | struct tm tms; 134 | 135 | i = ptime >> 25; /* year mod 100 */ 136 | if (i < 75) /* assume 2000 if year >= 75 */ 137 | i += 100; 138 | tms.tm_year = i; /* mktime wants years since 1900 */ 139 | tms.tm_mon = (ptime >> 21) & 0xf; 140 | tms.tm_mday = (ptime >> 16) & 0x1f; 141 | 142 | /* convert secs since midnight/4 to hours, minutes, and seconds */ 143 | 144 | i = (ptime & 0xffff) * 4; 145 | tms.tm_hour = i/3600; 146 | tms.tm_min = (i%3600)/60; 147 | tms.tm_sec = i%60; 148 | tms.tm_isdst = 1; /* use current timezone's DST flag? */ 149 | 150 | unixtime = mktime(&tms); 151 | if (unixtime == -1) { 152 | fprintf(stderr,"Unable to convert Prime timestamp:\n"); 153 | fprintf(stderr," year=%d, mon=%d, day=%d, hour=%d, min=%d, sec=%d\n", tms.tm_year, tms.tm_mon, tms.tm_mday, tms.tm_hour, tms.tm_min, tms.tm_sec); 154 | } 155 | return unixtime; 156 | } 157 | 158 | 159 | /* read a short (16-bit) integer in big-endian format into native format */ 160 | 161 | unsigned short readshort () { 162 | 163 | return getchar()<<8 | getchar(); 164 | } 165 | 166 | /* read a long (32-bit) integer in big-endian format into native format */ 167 | 168 | int readlong () { 169 | int n,ch; 170 | 171 | return getchar()<<24 | getchar()<<16 | getchar()<<8 | getchar(); 172 | } 173 | 174 | 175 | main (int argc, char** argv) { 176 | int verbose; /* verbose level */ 177 | int nowrite; /* true if indexing only */ 178 | int binary; /* true = don't translate text files */ 179 | int text; /* true = only restore text files */ 180 | int drb; /* true if this is a drb save */ 181 | int firstrec; /* true if first record */ 182 | int fp; /* current record's file position */ 183 | int logrecno; /* Magsav logical record number */ 184 | int explogrecno; /* expected logical record number (Magsav) */ 185 | int expblockno; /* expected block number (drb) */ 186 | int nwords; 187 | int i,ch; 188 | int recid; /* record id */ 189 | int wordsleft; /* number of words left in current record */ 190 | int tapeformat; /* pre-19, 19 w/o ACLS, 19 w/ACLS, etc. */ 191 | int objtype; /* drb object type */ 192 | int lrecversion; /* drb logical record version */ 193 | int filetype = -1; /* Primos file type being restored */ 194 | unsigned int dtm,dta,dtc,lsrdtm; /* date modified, accessed, created, parent dtm */ 195 | int fd; /* Unix fd for writing output files */ 196 | int nwritten; 197 | int ecwskip; /* words to skip at end of entry */ 198 | int skipping; /* true if skipping the current object */ 199 | int textfile; /* true if converting a text file */ 200 | int textstate; /* tracks text file compression state */ 201 | int maxentries; /* maximum entries in a segdir */ 202 | int segentry; /* this file's entry in a segdir */ 203 | int insegdir; /* true if inside a segdir */ 204 | unsigned char path[4096]; /* object pathname */ 205 | struct { /* directory stack info */ 206 | char path[4096]; /* pathname */ 207 | unsigned int dta; /* access time */ 208 | unsigned int dtm; /* modification time */ 209 | } dirstack[64]; 210 | int dirlevel; /* last entry in dirstack, -1 if empty */ 211 | 212 | unsigned char *p; 213 | unsigned char buf[16*2048]; /* tape buffer (limit of 16K words on Prime) */ 214 | struct utimbuf ut; 215 | int reel; /* reel number (old magsav) */ 216 | short bootskiprecno, bootskipreclen, bootskiprecid; 217 | drb = 0; /* assume it's an old magsav tape initially */ 218 | skipping = 1; /* might allow us to correctly start w/reel 2 */ 219 | fd = -1; /* make sure it doesn't look like a file is open */ 220 | wordsleft = 0; /* words left in this logical record */ 221 | 222 | verbose = 0; 223 | nowrite = 0; 224 | binary = 0; 225 | text = 0; 226 | 227 | /* any args? */ 228 | 229 | for (i=1; i 0) { 259 | if (verbose >= 2) 260 | fprintf(stderr, "WARNING: %d words left unread at position %d\n", wordsleft, ftell(stdin)); 261 | if (fread(buf, wordsleft*2, 1, stdin) != 1) { 262 | fprintf(stderr, "fread read error at position %d\n", fp); 263 | exit(1); 264 | } 265 | } 266 | wordsleft = 0; 267 | 268 | /* now we're at some kind of record header, in theory */ 269 | 270 | fp = ftell(stdin); /* might give -1 on some OS's */ 271 | 272 | /* read the tape header; for a newer-format (drb) tape, the first 273 | 4 bytes are "LSR " */ 274 | 275 | if (fread(buf, 4, 1, stdin) != 1) 276 | if (feof(stdin)) { 277 | if (verbose >= 1) fprintf(stderr,"End of file at position %d\n", fp); 278 | exit(0); 279 | } else { 280 | fprintf(stderr, "Error reading header at position %d\n", fp); 281 | exit(1); 282 | } 283 | buf[4]=0; 284 | 285 | /* some drb tapes have ANSI labels; skip them */ 286 | 287 | if (strcmp(buf,"VOL1") == 0) { 288 | drb = 1; 289 | expblockno++; 290 | if (verbose >= 1) 291 | fprintf(stderr,"Skipping %s label record at position %d\n", buf, fp); 292 | fread(buf,76,1,stdin); 293 | 294 | /* try to find start of VOL2 record. This is the way we bypass 295 | the boot program, which immediately follows VOL1 if a drb tape has 296 | a boot program. Assumes the string VOL2 doesn't occur in the boot 297 | program. */ 298 | 299 | strcpy(buf,"VOL2"); 300 | i=0; 301 | while(i < 4) { 302 | ch = getchar(); 303 | if (ch == buf[i]) 304 | i++; 305 | else 306 | i = 0; 307 | } 308 | } 309 | 310 | if (strncmp(buf,"VOL",3) == 0 || strncmp(buf,"UVL",3) == 0 || strncmp(buf,"HDR",3) == 0 || strncmp(buf,"UHL",3) == 0 || strncmp(buf,"EOF",3) == 0 || strncmp(buf,"EOV",3) == 0 || strncmp(buf,"UTL",3) == 0 ) { 311 | drb = 1; 312 | expblockno++; 313 | if (verbose >= 1) 314 | fprintf(stderr,"Skipping %s label record at position %d\n", buf, fp); 315 | fread(buf,76,1,stdin); 316 | continue; 317 | } 318 | 319 | /* "LSR " header might be in either Prime ASCII or standard ASCII */ 320 | 321 | if (strcmp(buf,"\314\323\322\240") == 0 || strcmp(buf,"LSR ") == 0) { 322 | 323 | drb = 1; 324 | if (verbose >= 2) fprintf(stderr, "drb: POS %d, ", fp); 325 | 326 | i = readshort(); 327 | if (i != 1) { 328 | fprintf(stderr, "Logical tape version is %d, but expected 1\n", i); 329 | exit(1); 330 | } 331 | 332 | i = readshort(); 333 | if (verbose >= 2) fprintf(stderr, "save %d, ", i); 334 | 335 | i = readlong(); 336 | if (verbose >= 2) lsrdtm = ptimestampu(i); 337 | 338 | i = readlong(); 339 | if (verbose >= 2) fprintf(stderr, "block #%d, ", i); 340 | if (i != expblockno) 341 | fprintf(stderr," ************ EXPECTED BLOCK %d, READ BLOCK %d\n", expblockno, i); 342 | expblockno = i+1; 343 | 344 | i = readshort(); 345 | if (verbose >= 2) fprintf(stderr, "size %d, ", i); 346 | wordsleft = i-10; 347 | 348 | i = readshort(); 349 | if (verbose >= 2) fprintf(stderr, "lrecs %d, ", i); 350 | 351 | if (verbose >= 2) fprintf(stderr, "%s", ctime((time_t *)&lsrdtm)); 352 | 353 | /* read the logical record header following LSR, then fall through */ 354 | 355 | if (fread(buf, 4, 1, stdin) != 1) { 356 | fprintf(stderr, "Error reading header following LSR at position %d\n", fp); 357 | exit(1); 358 | } 359 | } 360 | 361 | /* every drb logical record has a 4-byte header: 362 | byte 1: record id 363 | byte 2: version 364 | bytes 3-4: record length, including the 4-byte header 365 | old magsav logical records have a 3-word header: 366 | word 1: logical record number in logical tape, starting with 1 367 | word 2: record length, including the 3-word header 368 | word 3: record id 369 | */ 370 | 371 | /* if this is an old magsav record, get the 3-word header. 372 | Otherwise it's a drb record with a 2-word header. Either way, the 373 | record size is always in word 2 */ 374 | 375 | nwords = buf[2]<<8 | buf[3]; 376 | if (nwords > 16*1024) { 377 | fprintf(stderr, "Record size is %d words at position %d\n", nwords, fp); 378 | exit(1); 379 | } 380 | 381 | if (drb) { 382 | recid = buf[0]; 383 | lrecversion = buf[1]; 384 | if (lrecversion != 1 && lrecversion != 128) { 385 | fprintf(stderr, "Logical record version is %d, but expected 1 at position %d\n", lrecversion, fp); 386 | exit(1); 387 | } 388 | if (verbose >= 2) 389 | fprintf(stderr,"drb: recid=%d, lrecversion=%d, nwords=%d\n", recid, lrecversion, nwords); 390 | wordsleft = nwords-2; 391 | } else { 392 | logrecno = buf[0]<<8 | buf[1]; 393 | recid = -readshort(); /* NOTE: make magsav recid's negative! */ 394 | bootskipdone: 395 | wordsleft = nwords-3; 396 | if (verbose >= 2) 397 | fprintf(stderr,"magrst: recno %d, %d words, recid %d\n", logrecno, nwords, recid); 398 | if (logrecno != explogrecno) { 399 | fprintf(stderr,"********** magrst: expected logrec %d, saw %d\n", explogrecno, logrecno); 400 | explogrecno = logrecno; 401 | } 402 | explogrecno++; 403 | } 404 | 405 | /* if there is a file open and this isn't a continuation of it, close 406 | the file now, set the file timestamp */ 407 | 408 | if (fd >= 0 && recid != MS_DATA && recid != DRB_FILE_DATA) { 409 | if (fd > 0) 410 | close(fd); 411 | fd = -1; 412 | 413 | /* now go back up the directory stack and set dtm/dta in reverse 414 | order */ 415 | 416 | for (i = dirlevel; i>= 0; i--) { 417 | ut.actime = ptimestampu(dirstack[i].dta); 418 | ut.modtime = ptimestampu(dirstack[i].dtm); 419 | if (ut.modtime >= 0) { 420 | if (verbose >= 2) 421 | fprintf(stderr,"Setting timestamp on %s to %d\n", dirstack[i].path, dirstack[i].dtm); 422 | if (ut.actime < 0) 423 | ut.actime = ut.modtime; 424 | if (utime(dirstack[i].path, &ut) == -1) { 425 | fprintf(stderr,"Error setting timestamp for %s:", dirstack[i].path); 426 | perror(NULL); 427 | } 428 | } 429 | } 430 | } 431 | 432 | if (recid == MS_START_LOG_TAPE) { 433 | fprintf(stderr,"\nStart of logical tape at position %d\n", fp); 434 | 435 | /* word 4: tape format */ 436 | 437 | tapeformat = readshort(); wordsleft--; 438 | if (tapeformat == 0x8000) 439 | fprintf(stderr,"Tape format: pre rev 19\n"); 440 | else if (tapeformat == 0xC000) 441 | fprintf(stderr,"Tape format: rev 19 w/o ACLs\n"); 442 | else if (tapeformat == 0xE000) 443 | fprintf(stderr,"Tape format: rev 19 with ACLs\n"); 444 | else if (tapeformat == 0xD000) 445 | fprintf(stderr,"Tape format: rev 20 w/o ACLs\n"); 446 | else if (tapeformat == 0xF000) 447 | fprintf(stderr,"Tape format: rev 20 with ACLs\n"); 448 | else if (tapeformat == 0xA000) 449 | fprintf(stderr,"Tape format: rev 22 w/o ACLs\n"); 450 | else if (tapeformat == 0xB000) 451 | fprintf(stderr,"Tape format: rev 22 with ACLs\n"); 452 | else 453 | fprintf(stderr,"Tape format: 0x%x\n", tapeformat); 454 | 455 | /* words 5-7: date as MMDDYY */ 456 | 457 | if (fread(buf, 6, 1, stdin) != 1) { 458 | fprintf(stderr, "error reading date at position %d\n", fp); 459 | exit(1); 460 | } 461 | buf[6] = 0; 462 | pasciiu(buf,6); 463 | wordsleft -= 3; 464 | fprintf(stderr, "Date: %6s\n", buf); 465 | 466 | /* word 8: user version */ 467 | 468 | i = readshort(); wordsleft--; 469 | fprintf(stderr, "User version: %d\n", i); 470 | 471 | /* word 9: reel number */ 472 | 473 | reel = readshort(); wordsleft--; /* reel number */ 474 | fprintf(stderr, "Reel: %d\n", reel); 475 | 476 | /* words 10-12: tape name */ 477 | 478 | if (fread(buf, 6, 1, stdin) != 1) { 479 | fprintf(stderr, "error reading tape name at position %d\n", fp); 480 | exit(1); 481 | } 482 | buf[6] = 0; 483 | wordsleft -= 3; 484 | pasciiu(buf,6); 485 | fprintf(stderr, "Tape name: %6s\n", buf); 486 | 487 | /* the "start logical tape" record length doesn't include the size 488 | of the boot program, if present. Magsav adds the boot program 489 | to the first record on reel 1 of a backup. AFAIK, it doesn't 490 | write the boot record on reel 2 of a continued backup. At rev 491 | 19 and 20, it appears to add the boot record to every logical 492 | tape header, even if it isn't the first one on the tape. 493 | 494 | If the boot program is present, the first record will be 1024 495 | bytes for rev 19 and earlier tapes. Later revs of magsav 496 | store much longer boots - for example, rev 20.2 stores over 497 | 4K bytes in the first tape record. Without record markers, it's 498 | not possible to tell how long the physical tape record is, so 499 | we need to add some code to heuristically determine the 500 | length of the boot program by looking for logical record 1's 501 | header in the data stream. 502 | */ 503 | 504 | if (reel == 1) { 505 | if (verbose >= 1) 506 | fprintf(stderr, "Skipping boot at BOT, nwords=%d...\n", nwords); 507 | 508 | /* the first record is at least 1024 bytes (512 words). The 509 | start logical tape header size (nwords) doesn't include the 510 | boot, so skip 512-nwords first */ 511 | 512 | #if 0 513 | if (fread(buf, 512-nwords, 2, stdin) != 512-nwords) { 514 | perror("Error skipping boot"); 515 | exit(1); 516 | } 517 | #endif 518 | wordsleft = 4096; 519 | 520 | /* look for logical record 1, record length n (usually 27), 521 | and record id MS_NAME (remember, we negate old magsav 522 | recid's to distinguish them from drb */ 523 | 524 | while (1) { 525 | bootskiprecno = readshort(); wordsleft--; 526 | if (bootskiprecno != 1) 527 | continue; 528 | skipcont: 529 | bootskipreclen = readshort(); wordsleft--; 530 | if (bootskipreclen < 1) /* not part of header */ 531 | continue; 532 | 533 | /* rec lengths must be 3 words or greater, but if it's 534 | a 1, it might be the recno, so backup */ 535 | 536 | if (bootskipreclen == 1) { 537 | bootskiprecno = bootskipreclen; 538 | goto skipcont; 539 | } 540 | #if 1 541 | /* might want to add this later as an additional check; 542 | for example, a reclen of 4 would cause this loop to 543 | exit, but it's probably not logical record 1 because 544 | MS_NAME records have more data than this */ 545 | 546 | if (bootskipreclen < 27 || bootskipreclen > 100) 547 | continue; 548 | #endif 549 | bootskiprecid = readshort(); wordsleft--; 550 | if (-bootskiprecid == MS_NAME) { 551 | explogrecno = 1; 552 | logrecno = 1; 553 | recid = MS_NAME; 554 | nwords = bootskipreclen; 555 | goto bootskipdone; 556 | } 557 | 558 | /* this word might be the start of logical record 1 */ 559 | 560 | if (bootskiprecid == 1) { 561 | bootskiprecno = bootskiprecid; 562 | goto skipcont; 563 | } 564 | } 565 | fprintf(stderr,"Unable to find logical reccord 1\n"); 566 | exit(1); 567 | } 568 | 569 | } else if (recid == MS_NAME || recid == DRB_START_OBJ) { 570 | 571 | skipping = 0; 572 | insegdir = 0; 573 | if (recid == DRB_START_OBJ) { 574 | 575 | /* drb stores all possible attributes in the same record layout, 576 | even though most attributes only apply to certain object types */ 577 | 578 | objtype = readshort(); 579 | if (verbose >= 2) 580 | fprintf(stderr,"Start object type %d at position %d\n", objtype, fp); 581 | i = readlong(); /* dtm */ 582 | if (objtype == DRB_DIR_OBJ || objtype == DRB_FILE_OBJ || objtype == DRB_SEGDIR_OBJ) 583 | dtm = i; 584 | if (objtype == DRB_SEG_SUBFILE_OBJ || objtype == DRB_SEG_SUBDIR_OBJ) 585 | insegdir = 1; 586 | i = readlong(); /* dtc */ 587 | i = readlong(); /* dta */ 588 | if (objtype == DRB_DIR_OBJ || objtype == DRB_FILE_OBJ || objtype == DRB_SEGDIR_OBJ) 589 | dta = i; 590 | i = readshort(); /* file protection */ 591 | i = readlong(); /* segdir entry class */ 592 | i = readshort(); /* file object file type: 1=SAM, 2=DAM, 3=CAM */ 593 | filetype = 0; /* assume it's a regular SAM file for now */ 594 | i = readshort(); /* CAM extent info */ 595 | i = readlong(); /* max quota (dir) */ 596 | if (fread(buf, 12, 1, stdin) != 1) { /* owner & non-owner pass */ 597 | fprintf(stderr, "error reading owner/non-owner at %d\n", fp); 598 | exit(1); 599 | } 600 | i = readshort(); /* dir type, 1=PW, 2=ACL (dir) */ 601 | if (objtype == DRB_DIR_OBJ) /* use filesystem & old magsav types */ 602 | if (i == 1) 603 | filetype = 4; 604 | else if (i == 2) 605 | filetype = 5; 606 | else { 607 | fprintf(stderr, "Skipping drb dir type %d at position %d\n", i, fp); 608 | skipping = 1; 609 | } 610 | i = readshort(); /* file bits (rwlock, etc.) */ 611 | i = readshort(); /* roam segdir type (roam) */ 612 | i = readshort(); /* regular segdir type, 1=SAM, 2=DAM (segdir) */ 613 | if (objtype == DRB_SEGDIR_OBJ) 614 | if (i == 1) 615 | filetype = 2; 616 | else if (i == 2) 617 | filetype = 3; 618 | else { 619 | fprintf(stderr, "Skipping drb segdir type %d at position %d\n", i, fp); 620 | skipping = 1; 621 | } 622 | 623 | maxentries = readlong(); /* max entries (segdir) */ 624 | i = readshort(); /* dummy */ 625 | i = readshort(); /* object level */ 626 | i = readshort(); /* 0=regular, 1=roam */ 627 | i = readshort(); /* seg level: 1=subfile, 2=file under seg subdir */ 628 | i = readshort(); /* object name length in bytes */ 629 | if (i < 1 || i > 32) { 630 | fprintf(stderr, "object length = %d at %d\n", i, fp); 631 | exit(1); 632 | } 633 | if (fread(buf, 32, 1, stdin) != 1) { /* object name */ 634 | fprintf(stderr, "error reading object name at %d\n", fp); 635 | exit(1); 636 | } 637 | buf[i] = 0; 638 | i = readshort(); /* object pathname length (bytes) */ 639 | wordsleft -= 48; 640 | if (fread(path, (i+1)/2*2, 1, stdin) != 1) { /* object name */ 641 | fprintf(stderr, "error reading object name at %d\n", fp); 642 | exit(1); 643 | } 644 | wordsleft -= (i+1)/2; 645 | path[i] = 0; 646 | 647 | } else if (recid == MS_NAME) { 648 | p = path; /* pointer to accumulate pathname */ 649 | while (wordsleft > 0) { 650 | 651 | #if 0 652 | if (wordsleft % 24 != 0) { 653 | fprintf(stderr, "name parse error, position %d, wordsleft=%d\n", fp, wordsleft); 654 | exit(1); 655 | } 656 | #endif 657 | 658 | /* for a segdir, entries look like this: 659 | 660 | word 1: type of subfile (parent or this file?) 661 | word 2: zero 662 | word 3: segdir subfile entry number 663 | word 4: protection (?) 664 | words 5-19: unused 665 | word 20: subfile type (parent or this file?) 666 | words 21-24: unused 667 | 668 | NOTE: Segment directories can be nested. Need a test tape 669 | to implement the restore for these. 670 | 671 | For non-segdirs, entries look like this: 672 | 673 | word 1: entry control word (right byte = length in words) 674 | words 2-17: filename 675 | word 18: owner/non-owner protection bits 676 | word 19: acl protection; bit 1 set if non-default ACL protection 677 | word 20: file type: 678 | left byte: 679 | :100000 = special file (BOOT/DSKRAT) 680 | :040000 = clear if file has been changed since last backup 681 | :020000 = set if modified by Primos II (timestamp is inaccurate) 682 | :010000 = set for special BOOT, MFD, BADSPT, and DSKRAT files 683 | bits 5-6 are the file's read/write lock: 684 | 00 = use system rwlock (dflt) 685 | 01 = N readers or 1 writer (excl) 686 | 10 = N readers and 1 writer (updt) 687 | 11 = N readers and N writers (none) 688 | NOTE: UFD's don't have rwlocks; segdirs do. 689 | Segment subfiles have the same rwlock as the (top-level?) segdir 690 | right byte: 691 | 0 = SAM (sequential) file 692 | 1 = DAM (direct access) file 693 | 2 = SEGSAM (sequential segment directory) 694 | 3 = SEGDAM (direct seqment directory) 695 | 4 = password directory 696 | 5 = ACL directory 697 | 6 = category ACL 698 | 7 = CAM (contiguous) file 699 | */ 700 | 701 | /* ecw */ 702 | 703 | i = readshort(); wordsleft--; 704 | if (i & 0xFF < 24) { 705 | fprintf(stderr, "ecw = %d/%d (size != 24) at position %d\n", i>>8, i&0xff, fp); 706 | exit(1); 707 | } 708 | ecwskip = (i & 0xFF) - 22; 709 | ecwskip = 2; /* only skip words 23 & 24? */ 710 | 711 | /* words 2-17: filename (regular entry) */ 712 | 713 | if (fread(p, 32, 1, stdin) != 1) { 714 | fprintf(stderr, "error reading file name at position %d\n", fp); 715 | exit(1); 716 | } 717 | wordsleft -= 16; 718 | 719 | /* for segment subfiles (word 2 is zero), add the entry number */ 720 | 721 | if (*p == 0 && *(p+1) == 0) { 722 | segentry = *(p+2)<<8 | *(p+3); 723 | sprintf(p, "%d/", segentry); 724 | p = path + strlen(path); 725 | insegdir = 1; 726 | } else { 727 | for (i=0; i<32; i++) { /* find the end of the filename */ 728 | if (*p == 0240) { /* space w/parity */ 729 | break; 730 | } 731 | p++; 732 | } 733 | *p++ = '>'; 734 | *p = 0; 735 | } 736 | 737 | /* word 18: owner/non-owner protection bits */ 738 | 739 | i = readshort(); wordsleft--; 740 | 741 | /* word 19: acl protection; bit 1 set if non-default ACL */ 742 | 743 | i = readshort(); wordsleft--; 744 | 745 | filetype = readshort(); wordsleft--; 746 | filetype &= 0xff; 747 | 748 | /* word 21-22: date/time modified */ 749 | 750 | dta = 0; 751 | dtm = readlong(); wordsleft -= 2; 752 | 753 | /* words 23 to (ecw length) are dummy */ 754 | 755 | while (ecwskip > 0) { 756 | i = readshort(); 757 | wordsleft--; 758 | ecwskip--; 759 | } 760 | } 761 | *(--p) = 0; 762 | } 763 | 764 | /* pathname postprocessing: strip parity, change > to /, change / to S */ 765 | 766 | for (p=path; *p; p++) { 767 | *p = *p & 0x7f; 768 | if ('A' <= *p && *p <= 'Z') /* lowercase the name for Unix */ 769 | *p = *p+('a'-'A'); 770 | if (*p == '/') 771 | *p = 'S'; 772 | if (*p == '>') 773 | *p = '/'; 774 | } 775 | 776 | /* if path starts with BLAH, strip the leading < */ 777 | 778 | if (path[0] == '<') 779 | strcpy(path,path+1); 780 | 781 | fprintf(stderr,"%s\n", path); 782 | if (fd > 0) { 783 | fprintf(stderr,"fd should be zero or -1??\n"); 784 | exit(1); 785 | } 786 | 787 | /* create the parent directories */ 788 | 789 | if (nowrite == 0) { 790 | dirlevel = -1; 791 | for (p=path; *p != 0; p++) 792 | if (*p == '/') { 793 | *p = 0; 794 | if (mkdir(path, 0755) == -1 && errno != EEXIST) { 795 | fprintf(stderr,"Creating directory %s\n", path); 796 | perror(" Error is"); 797 | exit(1); 798 | } 799 | dirlevel++; 800 | strcpy(dirstack[dirlevel].path, path); 801 | *p = '/'; 802 | } 803 | 804 | dirlevel++; 805 | strcpy(dirstack[dirlevel].path, path); 806 | dirstack[dirlevel].dtm = dtm; 807 | dirstack[dirlevel].dta = dta; 808 | //fprintf(stderr,"Saved dtm for %s as %d\n", dirstack[dirlevel].path, dirstack[dirlevel].dtm); 809 | } 810 | 811 | } else if (recid == MS_DATA && (filetype == 4 || filetype == 5)) { 812 | 813 | /* Magsav data record following a password or ACL directory: 814 | word 1: always 8 815 | words 2-4: owner password 816 | words 5-7: non-owner password 817 | word 8: always zero 818 | word 9: zero for pre-quota saves (and last word) 819 | words 9-10: "QT$$" for quota saves 820 | word 11: 0 if quota directory, 1 if non-quota 821 | words 12-19: q$read return array; word 14 is max quota 822 | */ 823 | 824 | i = readshort(); wordsleft--; 825 | if (i != 8) { 826 | fprintf(stderr,"Expected 8 but got %d for ufd at position %d\n", i, fp); 827 | exit(1); 828 | } 829 | 830 | } else if (recid == MS_DATA && (filetype == 2 || filetype == 3)) { 831 | 832 | /* Magsav data record following a segment directory: 833 | word 1: maximum entries in the segdir */ 834 | 835 | maxentries = readshort(); wordsleft--; 836 | #if 0 837 | fprintf(stderr,"Segdir %s, maxentries=%d\n", path, maxentries); 838 | #endif 839 | 840 | /* below occurs when reel 2 starts with file data and is restored alone */ 841 | 842 | } else if ((recid == MS_DATA || recid == DRB_FILE_DATA) && filetype == -1) { 843 | fprintf(stderr,"Skipping file data at position %d\n", fp); 844 | 845 | } else if ((recid == MS_DATA || recid == DRB_FILE_DATA) && (filetype == 0 || filetype == 1)) { /* SAM or DAM data */ 846 | 847 | if (drb) { /* read file data size in bytes */ 848 | i = readlong(); wordsleft -= 2; 849 | if (verbose >= 2) 850 | fprintf(stderr, "File data size = %d bytes\n", i); 851 | } 852 | 853 | if (fread(buf, wordsleft*2, 1, stdin) != 1) { 854 | fprintf(stderr, "error reading file data at position %d\n", fp); 855 | exit(1); 856 | } 857 | if (nowrite == 0) { 858 | if (fd < 0) { /* first data record; open output file */ 859 | textstate = 0; 860 | if (!binary && !insegdir && isptext(path,filetype,buf,wordsleft*2)) 861 | textfile = 1; 862 | else 863 | textfile = 0; 864 | 865 | /* open the file for writing (should check for overwrite) */ 866 | 867 | fd = creat(path, 0644); 868 | if (fd == -1) { 869 | fprintf(stderr,"Opening file %s\n", path); 870 | perror(" Error is"); 871 | } 872 | if (!textfile && text) { /* don't restore binary files */ 873 | close(fd); 874 | fd = 0; 875 | } 876 | } 877 | if (textfile) 878 | nwritten = convtext(fd, buf, 2*wordsleft, &textstate); 879 | else if (fd > 0) { 880 | nwritten = write(fd, buf, 2*wordsleft); 881 | if (nwritten != 2*wordsleft) { 882 | fprintf(stderr,"Writing file %s\n", path); 883 | perror(" Error is"); 884 | } 885 | } 886 | } 887 | wordsleft = 0; 888 | 889 | } else if (recid == DRB_FILE_DATA) { 890 | i = readlong(); wordsleft -= 2; 891 | fprintf(stderr,"Unrecognized drb filetype = %d ignored\n", filetype); 892 | 893 | } else if (recid == DRB_END_OBJ) { 894 | i = readshort(); wordsleft--; 895 | if (i == 0x8000) 896 | fprintf(stderr, "WARNING: File may be incomplete (open?), status = 0x%04x\n", i); 897 | else if (i & 0xF800) { 898 | fprintf(stderr, "WARNING: File save status = 0x%04x\n", i); 899 | } 900 | 901 | } else if (recid == MS_END_LOG_TAPE || recid == DRB_END_LOG_TAPE) { 902 | fprintf(stderr,"End of logical tape at position %d\n", fp); 903 | #if 0 904 | exit(0); 905 | #endif 906 | } 907 | } 908 | } 909 | --------------------------------------------------------------------------------