Initial commit
authorPhilipp Hachtmann <hachti@hachti.de>
Sun, 27 Sep 2015 13:49:42 +0000 (15:49 +0200)
committerPhilipp Hachtmann <hachti@hachti.de>
Sun, 27 Sep 2015 13:49:42 +0000 (15:49 +0200)
This initial commit adds speed8 and the hpgl plotting program.

Signed-off-by: Philipp Hachtmann <hachti@hachti.de>
24 files changed:
.gitignore [new file with mode: 0644]
sw/plot_hpgl/pc/Makefile [new file with mode: 0644]
sw/plot_hpgl/pc/hpgl.c [new file with mode: 0644]
sw/plot_hpgl/pc/hpgl.h [new file with mode: 0644]
sw/plot_hpgl/pc/optimize.c [new file with mode: 0644]
sw/plot_hpgl/pc/optimize.h [new file with mode: 0644]
sw/plot_hpgl/pdp8/PLOT.FT [new file with mode: 0644]
sw/speed8/pc/.gitignore [new file with mode: 0644]
sw/speed8/pc/Makefile [new file with mode: 0644]
sw/speed8/pc/link.c [new file with mode: 0644]
sw/speed8/pc/link.h [new file with mode: 0644]
sw/speed8/pc/log.c [new file with mode: 0644]
sw/speed8/pc/log.h [new file with mode: 0644]
sw/speed8/pc/memory.c [new file with mode: 0644]
sw/speed8/pc/memory.h [new file with mode: 0644]
sw/speed8/pc/rk05.c [new file with mode: 0644]
sw/speed8/pc/rk05.h [new file with mode: 0644]
sw/speed8/pc/rktool.c [new file with mode: 0644]
sw/speed8/pc/speed8.h [new file with mode: 0644]
sw/speed8/pdp8/.gitignore [new file with mode: 0644]
sw/speed8/pdp8/Makefile [new file with mode: 0644]
sw/speed8/pdp8/README [new file with mode: 0644]
sw/speed8/pdp8/SPEED8.PA [new file with mode: 0644]
sw/speed8/pdp8/speed8.pal [new file with mode: 0644]

diff --git a/.gitignore b/.gitignore
new file mode 100644 (file)
index 0000000..d7108a5
--- /dev/null
@@ -0,0 +1,4 @@
+*~
+*.o
+*.d
+tmp
diff --git a/sw/plot_hpgl/pc/Makefile b/sw/plot_hpgl/pc/Makefile
new file mode 100644 (file)
index 0000000..de6e487
--- /dev/null
@@ -0,0 +1,24 @@
+
+all: default
+
+default: hpgl
+
+CFLAGS=-Wall -g
+LDFLAGS=-g -lm
+
+hpgl:  hpgl.o optimize.o
+       gcc ${LDFLAGS} -o $@ $^
+
+hpgl.o: hpgl.c hpgl.h optimize.h
+       gcc ${CFLAGS} -c -o $@ $<
+
+optimize.o: optimize.c optimize.h
+       gcc ${CFLAGS} -c -o $@ $<
+
+clean: 
+       @echo CLEAN
+       @rm -f *.o *~ hpgl
+
+.PHONY: default all clean
+
+
diff --git a/sw/plot_hpgl/pc/hpgl.c b/sw/plot_hpgl/pc/hpgl.c
new file mode 100644 (file)
index 0000000..70bb2f8
--- /dev/null
@@ -0,0 +1,273 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <ctype.h>
+
+/* #define DEBUG */
+
+#include "hpgl.h"
+#include "optimize.h"
+
+#define BUFFER_SIZE (32*1024*1024)
+
+static char data[BUFFER_SIZE];
+static int data_size;
+static int data_left;
+
+/*********************************++
+ *
+ * Read input data into a buffer
+ *
+ */
+static void read_file(int fd){
+  int res=0;
+  data_size=0;
+  while ((res=read(fd,data+data_size,BUFFER_SIZE-1-data_size))) data_size+=res;
+  data_left=data_size;
+  DBG("Read %i bytes.\n",data_left);
+}
+
+static int comp(char ** pptr, char * b){
+  char * ptr= *pptr;
+  if (strncmp(ptr,b,strlen(b))==0){
+    (*pptr)+=strlen(b);
+    data_left-=strlen(b);
+    return 1;
+  } else return 0;
+}
+
+static struct point * get_point(char ** pptr){
+  char * ptr2;
+  struct point *result = (struct point *)malloc(sizeof(struct point));
+  result->valid=1;
+  result->x=strtold(*pptr,&ptr2);
+  if (ptr2==*pptr) result->valid=0;
+  else { 
+    *pptr=ptr2;
+    *pptr=index(*pptr,',')+1;
+    result->y=strtold(*pptr,&ptr2);
+    if (ptr2==*pptr) result->valid=0;
+    else *pptr=ptr2;
+  }
+  result->next=NULL;
+  result->previous_in_path=NULL;
+
+  if (result->valid)
+    (*pptr)++; /* Step over comma between point specs! */
+  return result;
+}
+
+static void output (struct path * first_path){
+  struct point * last_point=NULL;
+  struct path * current_path;
+
+  for (current_path=first_path; current_path != NULL; 
+       current_path=current_path->next){
+
+    struct point * current_point;
+
+    /* Initial move to start of plot */
+    current_point=current_path->plot_start_point;
+    if (!equal(current_point,last_point)) {
+      printf("PU, %.2f,%.2f\n",current_point->x,current_point->y);
+    } 
+
+    if (current_path->plot_start_point
+       == current_path->first_point){ /* Forward */
+      
+      for (current_point=current_point->next; current_point != NULL; 
+          current_point=current_point->next) {
+       if (!equal(current_point,last_point)) {
+         printf("PD, %.2f,%.2f\n",current_point->x,current_point->y);
+       }
+       last_point=current_point;
+      }
+    } else { /* Going backwards */
+      for (current_point=current_point->previous_in_path; current_point != NULL; 
+          current_point=current_point->previous_in_path) {
+       if (!equal(current_point,last_point)) {
+         printf("PD, %.2f,%.2f\n",current_point->x,current_point->y);
+       }
+       last_point=current_point;
+      }
+    }
+  }
+}
+
+/*******************************************************************************
+ *                                                                             *
+ * main()                                                                      *
+ *                                                                             *
+ *******************************************************************************
+ */
+int main(int argc, char ** args){
+  
+  int fd=0; /* File descriptor for data input, defaults to standard input */
+  
+  char * ptr=data; /* Input buffer */
+  enum plot_mode_t plot_mode=absolute; /* Coordinate modus */
+  enum pen_state_t pen_state=up;
+
+  struct point pen_pos={0,0,1};
+
+  double character_angle=0;
+  double character_size=40;
+  
+  struct path * first_path=NULL;
+  struct path * current_path=NULL;
+
+  int in_path=0;
+  
+  enum hpgl_cmd_t command; /* Current command being processed */
+
+  DBG("Start\n");
+  
+  /* Open input file to read if specified. */
+  if (argc>1){
+    fd=open(args[1],O_RDONLY);
+    if (fd < 0){
+      ERR("Could not open file: \"%s\"\n",args[1]);
+      exit (-2);
+    }
+  }
+  /*  fd=open("haus.hpl",O_RDONLY); */
+  read_file(fd);
+  
+
+
+  /* Main-Loop */
+  while(data_left){  
+    while(data_left-3 && isspace(ptr[0])) ptr++;
+    
+    /* Determine command */
+    command=0;
+    if (comp(&ptr, "SC")) command=CMD_SC;
+    if (comp(&ptr, "PU")) command=CMD_PU;
+    if (comp(&ptr, "PD")) command=CMD_PD;
+    if (comp(&ptr, "LB")) command=CMD_LB;
+    if (comp(&ptr, "DI")) command=CMD_DI;
+    if (comp(&ptr, "SP")) command=CMD_SP;
+    if (comp(&ptr, "PA")) command=CMD_PA;
+    if (comp(&ptr, "PR")) command=CMD_PR;
+    if (comp(&ptr, "IN")) command=CMD_IN;
+    if (command==0) {data_left-- ; ptr++;}
+    else DBG("Command: %i %c%c\n",command,ptr[-2],ptr[-1]);
+    
+    int process_points=0;
+    switch(command){
+      
+    case CMD_NO_COMMAND:
+      break;
+    case CMD_PU: /* Pen up - not much to do here. */
+      pen_state=up;
+      in_path=0;
+      process_points=1;
+      break;
+
+    case CMD_PD:
+      pen_state=down;
+      if (!in_path){ /* Create and store a new path object with first point @pen_pos. */
+
+       struct point * new_point=(struct point *)malloc(sizeof(struct point));
+       new_point->x=pen_pos.x;
+       new_point->y=pen_pos.y;
+       new_point->next=NULL;
+       new_point->previous_in_path=NULL;
+       new_point->valid=1;
+
+       struct path * new_path=(struct path *)malloc(sizeof(struct path));
+       new_path->next=NULL; /* This is the last in the chain. */
+       new_path->first_point=new_point;
+       new_path->last_point=new_point;
+       new_path->plot_start_point=new_path->first_point;
+
+       if (first_path==NULL){ /* This is the first path */
+         first_path=new_path;
+       } else {
+         current_path->next=new_path; /* Append to linked list */
+       }
+       current_path=new_path; /* Update pointer to current path. */
+       in_path=1; /* We're in a path now, fine! */
+      }
+      process_points=1;
+      break;
+
+    case CMD_SC:
+      break;
+
+    case CMD_IN:
+      plot_mode=absolute;
+      pen_pos.x=0;
+      pen_pos.y=0;
+      pen_pos.valid=1;
+      character_angle=0;
+      character_size=40;
+      pen_state=up;
+      break;
+
+    case CMD_LB:
+      break;
+    case CMD_DI: /* Character direction */
+      break;
+    case CMD_SI: /* Character size */
+      break;
+    case CMD_SP: /* Select pen */
+      break;
+    case CMD_PA:
+      process_points=1;
+      plot_mode=absolute;
+      break;
+    case CMD_PR:
+      process_points=1;
+      plot_mode=relative;
+      break;
+    }
+
+    if (process_points) { /* Process point arguments */
+      struct point * new_point;
+      do{
+       new_point=get_point(&ptr);
+
+       if (new_point->valid) { /* The following only works if we got a good point! */
+
+         /* Scale: Plot in cm!! */
+         new_point->x = new_point->x / 500;
+         new_point->y = new_point->y / 500;
+
+         DBG("Found point: %6.2f,%6.2f\n",new_point->x,new_point->y);
+
+         /* Convert relative coordinates to absolute coordinates if needed */
+         if (plot_mode==relative){
+           DBG("Relative -> absolute coordinate calculation!\n");
+           new_point->x += pen_pos.x;
+           new_point->y += pen_pos.y;
+         }
+         
+         if (pen_state==down) { /* We're in a path, so add the point to current path */
+           current_path->last_point->next=new_point; /* Add to the end of list */
+           new_point->previous_in_path=current_path->last_point; /* Backwards reference */
+           current_path->last_point=new_point;               /* Fix the point */
+         } 
+         pen_pos.x=new_point->x;
+         pen_pos.y=new_point->y;
+         
+       }
+      } while (new_point->valid);
+    }
+
+  } /* while(data_left) */
+  
+  struct path * output_paths;
+
+  round_path(first_path);
+  
+  /* output_paths = optimize(first_path); */
+  output_paths=optimize(split_paths(first_path));
+  output(output_paths);
+  DBG("Programmende\n");
+  return 0;
+}
diff --git a/sw/plot_hpgl/pc/hpgl.h b/sw/plot_hpgl/pc/hpgl.h
new file mode 100644 (file)
index 0000000..595f52c
--- /dev/null
@@ -0,0 +1,58 @@
+#ifndef HPGL_H
+#define HPGL_H
+#include <stdio.h>
+
+struct point {
+  double x; 
+  double y; 
+  int valid; 
+  struct point * next;
+  struct point * previous_in_path;
+};
+
+struct path {
+  struct point * first_point;
+  struct point * last_point;
+  struct point * plot_start_point;
+  struct point * plot_end_point;
+  struct  path * next;
+
+  /* Internal special stuff */
+  double dist;
+  struct path *back;
+  
+  //int done;
+};
+
+enum hpgl_cmd_t {
+  CMD_NO_COMMAND,
+  CMD_PU,
+  CMD_PD,
+  CMD_SC,
+  CMD_IN,
+  CMD_LB,
+  CMD_DI,
+  CMD_SI,
+  CMD_SP,
+  CMD_PA,
+  CMD_PR };
+
+  enum plot_mode_t {absolute, relative};
+  enum  pen_state_t {up,down};
+
+
+#ifdef DEBUG
+
+#define POS {fprintf(stderr,"(%s, %i) %20s(): ",__FILE__, __LINE__,__FUNCTION__);}
+#define DBG(...) {POS; fprintf(stderr, "*** DEBUG: " __VA_ARGS__);}
+#define ERR(...) {POS; fprintf(stderr, "*** ERROR: " __VA_ARGS__); exit(-1); }
+
+#else
+#define POS {}
+#define DBG(...) {}
+#define ERR(...) {}
+#endif
+
+
+
+#endif
diff --git a/sw/plot_hpgl/pc/optimize.c b/sw/plot_hpgl/pc/optimize.c
new file mode 100644 (file)
index 0000000..5a434d0
--- /dev/null
@@ -0,0 +1,217 @@
+#include <stdlib.h>
+#include <string.h>
+#include <math.h>
+
+
+//#define DEBUG
+#include "hpgl.h"
+
+/*
+ * Get plotting distance between points.
+ * Distances of X and Y are calculated. The longer one
+ * is returned. This applies to plotters that can do
+ * diagonal steps. In that case the shorter distance is "hidden"
+ * and doesn't consume time. If the plotter is unable to do diagonal
+ * steps the sum of both distances must be returned.
+ */
+double plot_distance(struct point *a, struct point *b){
+  //  double dist_x = (a->x>b->x?a->x-b->x:b->x-a->x);
+  //double dist_y = (a->y>b->y?a->y-b->y:b->y-a->y);
+  //return (dist_x > dist_y?dist_x:dist_y);  
+  double dist_x = fabs(a->x - b->x);
+  double dist_y = fabs(a->y - b->y);
+  return (dist_x > dist_y ? dist_x:dist_y);  
+}
+
+static double path_distance(struct point *point, struct path *path){
+  double forward_dist=plot_distance(point, path->first_point);
+  double reverse_dist=plot_distance(point, path->last_point);
+
+  if (reverse_dist < forward_dist){
+    path->plot_start_point=path->last_point;
+    path->plot_end_point=path->first_point;
+    return reverse_dist;
+  } else {
+    path->plot_start_point=path->first_point;
+    path->plot_end_point=path->last_point;
+    return forward_dist;
+  }
+}
+
+double OLDpath_distance(struct point *origin, struct path *candidate){
+  double forward_dist=plot_distance(origin, candidate->first_point);
+  double reverse_dist=plot_distance(origin, candidate->last_point);
+  if (reverse_dist < forward_dist){
+    candidate->plot_start_point=candidate->last_point;
+    candidate->plot_end_point=candidate->first_point;
+    return reverse_dist;
+  } else {
+    candidate->plot_start_point=candidate->first_point;
+    candidate->plot_end_point=candidate->last_point;
+    return forward_dist;
+  }
+}
+
+int equal(struct point *a, struct point *b){
+  if ((a==NULL)||(b==NULL)) return 0;
+  if ((a->x == b->x) &&( a->y == b->y)) return 1;
+  else return 0;
+}
+
+static void link_reverse(struct path *path){
+  struct path *cur;
+  if (!path) return;
+  for (cur = path; cur->next; cur = cur->next)
+    cur->next->back = cur;
+}
+
+static void remove_path(struct path **target, struct path *path){
+
+  if (path == *target){ /* First element! */
+    *target = path->next;
+    return;
+  }
+  path->back->next = path->next;
+  if (path->next)
+    path->next->back = path->back;
+}
+
+struct path *optimize(struct path *input_list){
+  struct point __point;
+  __point.x=0;
+  __point.y=0;
+
+  struct point *point = &__point;
+  struct path *result = NULL;
+  struct path *result_last;
+  struct path *cur, *best;
+  int i;
+
+  link_reverse(input_list);
+  //  DBG("link_reverse done\n");
+
+  i=0;
+  for (cur = input_list; cur; cur = cur->next)
+    DBG("%4i Input Path (%5.2f,%5.2f) -> (%5.2f,%5.2f)\n",
+          i++,
+          cur->plot_start_point->x,
+          cur->plot_start_point->y,
+          cur->plot_end_point->x,
+          cur->plot_end_point->y
+          );
+
+  while(input_list) {
+    best = NULL;
+    for (cur = input_list; cur; cur = cur->next) {
+      cur->dist = path_distance(point, cur);
+      if ((!best)||(cur->dist < best->dist)) {
+       best = cur;
+       /*
+       DBG("cur->dist = %5.2f, best->dist = %5.2f\n",
+              cur->dist, best->dist);
+       DBG("Cur Path (%5.2f,%5.2f) -> (%5.2f,%5.2f)\n",
+          cur->plot_start_point->x,
+          cur->plot_start_point->y,
+          cur->plot_end_point->x,
+          cur->plot_end_point->y
+          );
+       */
+
+      }
+    }
+    point = best->plot_end_point;
+
+    DBG("xbest Path (%5.2f,%5.2f) -> (%5.2f,%5.2f)\n",
+          best->plot_start_point->x,
+          best->plot_start_point->y,
+          best->plot_end_point->x,
+          best->plot_end_point->y
+          );
+
+    /* Take best result */
+    remove_path(&input_list, best);
+
+    if (!result) {
+      result = best;
+    } else {
+      result_last->next = best;
+    }
+    result_last = best;
+    result_last->next = NULL;
+  }
+
+#ifdef DEBUG
+  i = 0;
+  for (cur = result; cur; cur = cur->next)
+    DBG("%4i Path (%5.2f,%5.2f) -> (%5.2f,%5.2f)\n",
+          i++,
+          cur->plot_start_point->x,
+          cur->plot_start_point->y,
+          cur->plot_end_point->x,
+          cur->plot_end_point->y
+          );
+#endif
+
+  return result;
+}
+
+
+struct path *single_stroke_path(struct point *a, struct point *b){
+  struct path *result=(struct path *)malloc(sizeof(struct path));
+
+  struct point *aa=(struct point *)malloc(sizeof(struct point));
+  struct point *bb=(struct point *)malloc(sizeof(struct point));
+  memcpy(aa,a,sizeof(struct point));
+  memcpy(bb,b,sizeof(struct point));
+  aa->next=bb;
+  bb->next=NULL;
+  aa->previous_in_path=NULL;
+  bb->previous_in_path=aa;
+  
+  result->next=NULL;
+  result->first_point=aa;
+  result->plot_start_point=aa;
+  result->last_point=bb;
+  result->plot_end_point=bb;
+  return result;
+}
+
+
+struct path *split_paths(struct path *source){
+  struct path *result=NULL;
+  struct path *last_result=NULL;
+  
+  struct path *current_path;
+  for (current_path=source; current_path!=NULL; current_path=current_path->next){
+    
+    struct point *current_point;
+
+    for (current_point=current_path->first_point; current_point->next!=NULL; 
+        current_point=current_point->next){
+      struct path*tmp=single_stroke_path(current_point,current_point->next);
+      if (result==NULL){
+       result=tmp;
+       last_result=tmp;
+      } else { 
+       last_result->next=tmp;
+       last_result=last_result->next;
+      }
+
+    }
+  }
+  return result;
+}
+
+
+void round_path(struct path *source){
+  struct path *current_path;
+  for (current_path=source; current_path!=NULL; current_path=current_path->next){
+        struct point *current_point;
+       
+    for (current_point=current_path->first_point; current_point!=NULL; 
+        current_point=current_point->next){
+      current_point->x=round(current_point->x*100.0)/100.0;
+      current_point->y=round(current_point->y*100.0)/100.0;
+    }
+  }
+}
diff --git a/sw/plot_hpgl/pc/optimize.h b/sw/plot_hpgl/pc/optimize.h
new file mode 100644 (file)
index 0000000..5a8bc72
--- /dev/null
@@ -0,0 +1,11 @@
+
+#ifndef OPTIMIZE_H
+#define OPTIMIZE_H
+
+extern struct path *optimize(struct path *first_path);
+struct path *split_paths(struct path *source);
+struct path *join_paths(struct path *source);
+extern void round_path(struct path *input);
+extern int equal(struct point *a, struct point *b);
+
+#endif
diff --git a/sw/plot_hpgl/pdp8/PLOT.FT b/sw/plot_hpgl/pdp8/PLOT.FT
new file mode 100644 (file)
index 0000000..ffa75b4
--- /dev/null
@@ -0,0 +1,45 @@
+C PHILIPP'S FILE PLOTTER 2010!\r
+\r
+0100  DIMENSION NAME(3)\r
+\r
+C     565 0.01 INCH, DATA IN CM\r
+      CALL PLOTS(0.0254,0)\r
+\r
+C     CALL PLOTS(0.004,0)\r
+\r
+0150  XMAX=0\r
+0200  WRITE (4,300)\r
+0300  FORMAT (' FILE TO PLOT:')\r
+0400  READ (4,600) NAME\r
+0500  CALL USR(5,NAME,2,ERR)\r
+0501  IF (NAME(1) .EQ. ' ') GO TO 200 \r
+0600  FORMAT (3A6)\r
+\r
+\r
+\r
+1200  CALL CHKEOF(EOF)\r
+1300  READ (5,1000) PEN,X,Y\r
+1000  FORMAT (A3,2F13.5)\r
+1500  IF (EOF.NE.0) GO TO 7000\r
+\r
+C ROTATE\r
+C      TMP=Y\r
+C      Y=-X\r
+C      X=-TMP\r
+\r
+      TMP=Y\r
+      Y=-X\r
+      X=TMP\r
+\r
+2000  IF (PEN.EQ.'PU,') P=3\r
+2100  IF (PEN.EQ.'PD,') P=2\r
+      IF (X.GE.XMAX) XMAX=X\r
+      CALL XYPLOT(X,Y,P)\r
+\r
+1700  GO TO 1200\r
+\r
+7000  CALL XYPLOT(XMAX+1,0,-3);\r
+      CALL PLEXIT\r
+\r
+9999  GO TO 150\r
+\r
diff --git a/sw/speed8/pc/.gitignore b/sw/speed8/pc/.gitignore
new file mode 100644 (file)
index 0000000..c64fb19
--- /dev/null
@@ -0,0 +1 @@
+rktool
\ No newline at end of file
diff --git a/sw/speed8/pc/Makefile b/sw/speed8/pc/Makefile
new file mode 100644 (file)
index 0000000..688398a
--- /dev/null
@@ -0,0 +1,21 @@
+
+
+RKTOOL_OBJS=rk05.o  link.o memory.o log.o rktool.o
+
+CFLAGS=-Wall
+LDFLAGS=-lm
+
+rktool: $(RKTOOL_OBJS)
+       @echo LD $@
+       @$(CC) $(LDFLAGS) -o $@ $^
+
+%.o:%.c
+       @echo CC $@
+       @$(CC) $(CFLAGS) -c -o $@ $^
+
+
+clean:
+       @echo CLEAN
+       @rm -f *.o rktool *~
+
+.PHONY: clean
diff --git a/sw/speed8/pc/link.c b/sw/speed8/pc/link.c
new file mode 100644 (file)
index 0000000..c04eb78
--- /dev/null
@@ -0,0 +1,125 @@
+
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <fcntl.h>
+#include <string.h>
+#include <errno.h>
+#include <sys/select.h>
+#include <termios.h>
+
+#include "link.h"
+#include "log.h"
+
+static int fd=-1;
+static struct termios org_settings;
+static struct termios work_settings;
+
+int link_open(char * port){
+  int res;
+  fd=open(port,O_RDWR);
+  if (fd<0) return -1;
+
+  res=tcgetattr(fd,&org_settings);
+  if (res!=0) return -1;
+
+  res=tcgetattr(fd,&work_settings);
+  if (res!=0) return -1;
+
+  cfmakeraw(&work_settings);
+  res=tcsetattr(fd, TCSANOW, &work_settings);
+
+  if (res!=0) return -1;
+
+  return 0;
+}
+
+extern void link_close(void){
+  tcdrain(fd);
+  tcsetattr(fd, TCSANOW, &org_settings);
+  close(fd);
+}
+
+extern int link_flush(char * port){
+  int ifd=open(port, O_RDWR);
+  if (ifd<0) return -1;
+  
+  fd_set fds;
+  struct timeval timeout={
+    .tv_sec=0,
+    .tv_usec=50000,
+  };
+  FD_ZERO(&fds);
+  FD_SET(ifd,&fds);
+  
+  char tmpbuf[100];
+  while (timeout.tv_usec){
+    //debug("select\n");
+    select(ifd+1,&fds,NULL,NULL,&timeout);
+    //debug("select done\n");
+    if (timeout.tv_sec+timeout.tv_usec)
+      read(ifd,&tmpbuf, sizeof(tmpbuf));
+  }
+  close(ifd);
+  return 0;
+}
+
+int link_write(uint16_t * data, int len){
+  char * wbuf;
+  int i;
+  int remain;
+  if (fd<0) {
+    errno=EINVAL;
+    return -1;
+  }
+  wbuf=(char *)malloc(len*2);
+  if (!wbuf){
+    return -1;
+  }
+  for (i=0; i<len; i++){
+    wbuf[2*i]=(data[i]>>6)&077;
+    wbuf[2*i+1]=(data[i])&077;
+  }
+  remain=len*2;
+  
+  while(remain){
+    int res=write(fd,wbuf+len*2-remain, remain);
+    if (res<0){
+      free(wbuf);
+      return -1;
+    }
+    remain-=res;
+  }
+  free(wbuf);
+  return len;
+}
+
+int link_read(uint16_t * buffer, int len){
+  int i;
+  char * rbuf;
+  int remain;
+  rbuf=(char *)malloc(len*2);
+  if (!rbuf) return -1;
+  if (!buffer){
+    errno=EINVAL;
+    return -1;
+  }
+  remain=len*2;
+  while(remain){
+    int res=read(fd,rbuf+len*2-remain, remain);
+    if (res<0){
+      free(rbuf);
+      return -1;
+    }
+    remain-=res;
+  }
+  
+  for (i=0; i<len; i++){
+    buffer[i]  = rbuf[2*i]&077;
+    buffer[i] |= rbuf[2*i+1]<<6;
+    buffer[i] &= 07777;
+  }
+  free(rbuf);
+  return len;
+}
diff --git a/sw/speed8/pc/link.h b/sw/speed8/pc/link.h
new file mode 100644 (file)
index 0000000..78335c5
--- /dev/null
@@ -0,0 +1,14 @@
+#ifndef LINK_H
+#define LINK_H
+
+#include <stdint.h>
+
+extern int link_open(char * port);
+extern void link_close(void);
+extern int link_flush(char * port);
+
+extern int link_read(uint16_t * buffer, int len);
+extern int link_write(uint16_t * buffer, int len);
+
+
+#endif
diff --git a/sw/speed8/pc/log.c b/sw/speed8/pc/log.c
new file mode 100644 (file)
index 0000000..b1ec19c
--- /dev/null
@@ -0,0 +1,35 @@
+
+#include <stdarg.h>
+#include <stdio.h>
+#include "log.h"
+
+int loglevel=LL_DEFAULT;
+
+int debug(char * format,...){
+  va_list va;
+  va_start(va,format);
+  if (loglevel>=LL_DEBUG) return vfprintf(stderr, format, va);
+  else return 0;
+}
+
+int info(char * format,...){
+  va_list va;
+  va_start(va,format);
+  if (loglevel>=LL_INFO) return vfprintf(stderr, format, va);
+  else return 0;
+}
+
+int warn(char * format,...){
+  va_list va;
+  va_start(va,format);
+  if (loglevel>=LL_WARN) return vfprintf(stderr, format, va);
+  else return 0;
+}
+
+int err(char * format,...){
+  va_list va;
+  va_start(va,format);
+  if (loglevel>=LL_ERROR) return vfprintf(stderr, format, va);
+  else return 0;
+}
+
diff --git a/sw/speed8/pc/log.h b/sw/speed8/pc/log.h
new file mode 100644 (file)
index 0000000..7d642b1
--- /dev/null
@@ -0,0 +1,30 @@
+
+#ifndef LOG_H
+#define LOG_H
+
+#define LL_QUIET 0
+#define LL_ERROR 1
+#define LL_WARN  2
+#define LL_INFO  3
+#define LL_DEBUG 4
+
+#define LL_DEFAULT LL_INFO
+
+extern int loglevel;
+
+extern int debug(char * format,...);
+extern int info(char * format,...);
+extern int warn(char * format,...);
+extern int err(char * format,...);
+
+#define warning(...) warn(__VA_ARGS__)
+#define error(...) err(__VA_ARGS__)
+#define dbg(...) debug(__VA_ARGS__)
+
+
+#define DEBUG if (loglevel>=LL_DEBUG)
+#define INFO  if (loglevel>=LL_INFO)
+#define WARN  if (loglevel>=LL_WARN)
+#define ERROR if (loglevel>=LL_ERROR)
+
+#endif
diff --git a/sw/speed8/pc/memory.c b/sw/speed8/pc/memory.c
new file mode 100644 (file)
index 0000000..ee4e65a
--- /dev/null
@@ -0,0 +1,33 @@
+
+#include <stdint.h>
+
+#include "speed8.h"
+#include "link.h"
+#include "memory.h"
+
+
+int get_buffer(uint16_t * buffer){
+  uint16_t cb[2];
+  int res;
+  cb[0]=ATTENTION;
+  cb[1]=CMD_GET_BUFFER;
+  
+  res=link_write(cb,2);
+  if (res<0) return res;
+  res=link_read(buffer, 4096);
+  if (res<0) return res;
+  return 0;
+}
+
+int put_buffer(uint16_t * buffer){
+  uint16_t cb[2];
+  int res;
+  cb[0]=ATTENTION;
+  cb[1]=CMD_PUT_BUFFER;
+  res=link_write(cb,2);
+  if (res<0) return res;
+  res=link_write(buffer, 4096);
+  if (res<0) return res;
+  return 0;
+}
+
diff --git a/sw/speed8/pc/memory.h b/sw/speed8/pc/memory.h
new file mode 100644 (file)
index 0000000..8a14514
--- /dev/null
@@ -0,0 +1,14 @@
+#ifndef MEMORY_H
+#define MEMORY_H
+
+
+extern int get_buffer(uint16_t * buffer);
+extern int get_buffer(uint16_t * buffer);
+
+
+
+
+
+
+
+#endif
diff --git a/sw/speed8/pc/rk05.c b/sw/speed8/pc/rk05.c
new file mode 100644 (file)
index 0000000..588254d
--- /dev/null
@@ -0,0 +1,304 @@
+#include <stdint.h>
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <math.h>
+
+#include "rk05.h"
+#include "speed8.h"
+#include "link.h"
+#include "log.h"
+
+/* Commands for the RK8E controller */
+#define RK_CM_READ      00000
+#define RK_CM_SEEK_ONLY 03000
+#define RK_CM_WRITE     04000
+#define RK_CM_TRANS256  00000
+#define RK_CM_TRANS128  00100
+
+static char * bars(int width, int max, int current){
+  static char buffer[100];
+  if (width>80) width=80;
+  int i;
+  float width_f=width;
+  float max_f=max;
+  float current_f=current;
+  float step=width_f/max_f;
+  float dots=step*current_f;
+  int actual=round(dots);
+  buffer[0]='[';
+  buffer[width+1]=']';
+  for (i=0; i<width; i++){
+    if (i<actual) buffer[i+1]='*';
+    else buffer[i+1]=' ';
+  }
+  int perc=round((current_f/max_f)*100);
+  sprintf(buffer+width+2," %3i%%",perc);
+  return buffer;
+}
+
+int rk05_write_sector(uint8_t drive, uint16_t disk_address, uint16_t mem_address){
+  uint16_t cb[5];
+  debug("rk05_write_sector(): Drive %i, DA: %05o, CA: %04o\n",drive,disk_address, mem_address);
+  if ((disk_address>=203*2*16*256)||(mem_address>4095)||(drive>3)) {
+    debug("rk05_write_sector(): Invalid parameters!\n");
+    errno=EINVAL;
+    return -1;
+  }
+  cb[0]=ATTENTION;
+  cb[1]=CMD_RK_GO;
+  cb[2]=mem_address; // Current address
+  cb[3]=RK_CM_WRITE|(drive&3)<<1 
+    | ((disk_address>>12)&1) 
+    | RK_CM_TRANS256
+    | 010;   // Field!!
+  cb[4]=disk_address&07777;
+  return link_write(cb,5);
+}
+
+int rk05_read_sector(uint8_t drive, uint16_t disk_address, uint16_t mem_address){
+  debug("rk05_read_sector(): Drive %i, DA: %05o, CA: %04o\n",drive,disk_address, mem_address);
+  uint16_t cb[5];
+  if ((disk_address>=RK05_TRACKS*16)||(mem_address>4095)||(drive>3)) {
+    debug("rk05_read_sector(): Invalid parameters!\n");
+    errno=EINVAL;
+    return -1;
+  }
+  cb[0]=ATTENTION;
+  cb[1]=CMD_RK_GO;
+  cb[2]=mem_address; // Current address
+  cb[3]=RK_CM_READ|(drive&3)<<1 
+    | ((disk_address>>12)&1) 
+    | 010;   // Field!!
+  cb[4]=disk_address&07777;
+  link_write(cb,5);
+  return 0;
+}
+
+int rk05_write_track(uint8_t drive, uint16_t track){
+  uint16_t cb[4];
+  uint16_t sector=track*16+0;
+  debug("rk05_write_track(): Drive %i, Track: %i\n",drive,track);
+  if ((drive>3)||(track>=RK05_TRACKS)){
+    debug("rk05_write_track(): Invalid parameters!\n");
+    errno=EINVAL;
+    return -1;
+  }
+  cb[0]=ATTENTION;
+  cb[1]=CMD_RK_W_TRACK;
+  cb[2]=((drive&3)<<1)
+    |((sector>>12)&1);
+  cb[3]=(sector)&07777;
+  return link_write(cb,4);
+}
+
+int rk05_read_track(uint8_t drive, uint16_t track){
+  uint16_t cb[4];
+  uint16_t sector=track*16+0;
+  debug("rk05_read_track(): Drive %i, Track: %i\n",drive,track);
+  if ((drive>3)||(track>=RK05_TRACKS)){
+    debug("rk05_read_track(): Invalid parameters!\n");
+    errno=EINVAL;
+    return -1;
+  }
+  cb[0]=ATTENTION;
+  cb[1]=CMD_RK_R_TRACK;
+  cb[2]=((drive&3)<<1)
+    |((sector>>12)&1);
+  cb[3]=sector&07777;
+  return link_write(cb,4);
+}
+
+int rk05_status(uint16_t * status){
+  uint16_t cb[2];
+  int res;
+  debug("rk05_status()\n");
+  cb[0]=ATTENTION;
+  cb[1]=CMD_RK_STATUS;
+  res=link_write(cb,2);
+  if (res<0) return res;
+  res=link_read(cb,1);
+  if (res<0) return res;
+  if (status) *status=cb[0];
+  debug("rk05_status(): %04o\n",cb[0]);
+  return 0;
+}
+
+int rk05_recalibrate(uint8_t drive){
+  uint16_t cb[2];
+  debug("rk05_recalibrate(): Drive: %i\n",drive);
+  if (drive>3){
+    debug("rk05_recalibrate(): Invalid drive!\n");
+    errno=EINVAL;
+    return -1;
+  }
+  cb[0]=ATTENTION;
+  cb[1]=CMD_RK_RECAL0 + 0100*drive;
+  return link_write(cb,2);
+}
+
+
+int order1[16]={0, 2, 4, 6, 8, 10, 12, 14, 1,3,5,7,9,11,13,15};
+int order2[16]={0, 3, 6, 9, 12, 15, 1, 4, 7,10,13,2,5,8,11,14};
+int order3[16]={0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15};
+int order4[16]={0,4,8,12,1,5,9,13,2,6,10,14,3,7,11,15};
+
+int rk05_read_disk(uint8_t disk, uint16_t * target){
+  int track,sector;
+  int res;
+  uint16_t status;
+  if (!target){
+    errno=EINVAL;
+    return -1;
+  }
+  res=rk05_recalibrate(disk);
+  if (res<0) return res;
+  res=rk05_status(&status);
+  if (res<0) return res;
+  if (status != RK05_STATUS_OK){
+    err("Status error while recalibrating!\n");
+    ERROR rk05_perror(status);
+    errno=EIO;
+    return -1;
+  }
+
+  for (track=0; track<RK05_TRACKS; track++){
+    info("\rReading drive %i: CYL: %3i, SIDE: %i  %s",
+        disk, track/2, track%2,bars(70,RK05_TRACKS-1,track));
+    res=rk05_read_track(disk, track);
+    if (res<0) return res;
+    res=rk05_status(&status);
+    if (res<0) return res;
+    
+    if (status != RK05_STATUS_OK){
+      int retries;
+      info("\nAn error occured!\n");
+      INFO rk05_perror(status);
+      warning("Reading of track %i failed. Will try individual sectors now.\n", track);
+      res=rk05_recalibrate(disk);
+      if (res<0) return res;
+      res=rk05_status(&status);
+      if (res<0) return res;
+      for (sector=0; sector<16; sector++){
+       retries=16;
+       while(retries){
+         debug("Reading sector %i with %i tries left\n",sector,retries);
+         res=rk05_read_sector(disk,track*16+order2[sector],order2[sector]*0400);
+         if (res<0) return res;
+         res=rk05_status(&status);
+         if (res<0) return res;
+         if (status != RK05_STATUS_OK){
+           retries--;
+           warning("Error on sector %i, will retry %i times.\n",sector, retries);
+           INFO rk05_perror(status);
+           res=rk05_recalibrate(disk);
+           if (res<0) return res;
+           res=rk05_status(&status);
+           if (res<0) return res;
+         } else {
+           retries++;
+           break;
+         }
+       }
+      }
+      if(retries==0){
+       err("Failed to read track %i. Giving up.\n",track);
+       errno=EIO;
+       return -1;
+      } else {
+       warning("Now it succeeded.\n");
+      }
+    }
+    res=get_buffer(target+(4096*track));
+    if(res<0) return res;
+  }
+  info("\n");
+  debug("rk05_read_disk():Success\n");
+  return 0;
+}
+
+int rk05_write_disk(uint8_t disk, uint16_t * target){
+  int track,sector;
+  int res;
+  uint16_t status;
+  if (!target){
+    errno=EINVAL;
+    return -1;
+  }
+  res=rk05_recalibrate(disk);
+  if (res<0) return res;
+  res=rk05_status(&status);
+  if (res<0) return res;
+  if (status != RK05_STATUS_OK){
+    err("Status error while recalibrating!\n");
+    ERROR rk05_perror(status);
+    errno=EIO;
+    return -1;
+  }
+  for (track=0; track<RK05_TRACKS; track++){
+    info("\rWriting drive %i: CYL: %3i, SIDE: %i  %s", disk, track/2, track%2, bars(70,RK05_TRACKS-1,track));
+    res=put_buffer(target+(4096*track));
+    if (res<0) return res;
+    res=rk05_write_track(disk,track);
+    if (res<0) return res;
+    res=rk05_status(&status);
+    if (res<0) return res;
+    
+    if (status != RK05_STATUS_OK){
+      int retries;
+      info ("\nAn error occured!\n");
+      INFO rk05_perror(status);
+      warning("Writing of track %i failed. Will try individual sectors now.\n", track);
+      res=rk05_recalibrate(disk);
+      if (res<0) return res;
+      res=rk05_status(&status);
+      if (res<0) return res;
+      for (sector=0; sector<16; sector++){
+       retries=16;
+       while(retries){
+         debug("Writing sector %i with %i tries left\n",order2[sector],retries);
+         res=rk05_write_sector(disk,track*16+order2[sector],order2[sector]*0400);
+         if (res<0) return res;
+         res=rk05_status(&status);
+         if (res<0) return res;
+         if (status != RK05_STATUS_OK){
+           retries--;
+           warning("Error on sector %i, will retry %i times.\n",order2[sector], retries);
+           INFO rk05_perror(status);
+           res=rk05_recalibrate(disk);
+           if (res<0) return res;
+           res=rk05_status(&status);
+           if (res<0) return res;
+         } else {
+           retries++;
+           break;
+         }
+       }
+      }
+      if(retries==0){
+       err("Failed to write track %i. Giving up.\n",track);
+       errno=EIO;
+       return -1;
+      } else {
+       warning("Now it succeeded.\n");
+      }
+    }
+  }
+  info("\n");
+  debug("rk05_write_disk():Success\n");
+  return 0;
+}
+
+void rk05_perror(uint16_t status){
+  fprintf(stderr, "Status: %s\n",(status&04000?"Done":"Busy"));
+  if (status & 02000) fprintf(stderr,"Head in motion.\n");
+  if (status & 00400) fprintf(stderr,"Seek fail\n");
+  if (status & 00200) fprintf(stderr,"File not ready\n");
+  if (status & 00100) fprintf(stderr,"Control busy\n");
+  if (status & 00040) fprintf(stderr,"Timing error");
+  if (status & 00020) printf("Write lock error\n");
+  if (status & 00010) fprintf(stderr,"Parity error\n");
+  if (status & 00004) fprintf(stderr,"Data request late\n");
+  if (status & 00002) fprintf(stderr,"Drive status error\n");
+  if (status & 00001) fprintf(stderr,"Cylinder address error\n");
+}
diff --git a/sw/speed8/pc/rk05.h b/sw/speed8/pc/rk05.h
new file mode 100644 (file)
index 0000000..d311457
--- /dev/null
@@ -0,0 +1,28 @@
+
+
+#ifndef RK05_H
+#define RK05_H
+#include <stdint.h>
+
+//#define RK05_CYLINDERS 203
+#define RK05_CYLINDERS 203
+#define RK05_DISKSIZE (RK05_CYLINDERS*2*16*256)
+#define RK05_TRACKS (RK05_CYLINDERS*2)
+
+#define RK05_STATUS_OK (04000)
+
+extern int rk05_write_sector(uint8_t drive, uint16_t disk_address, uint16_t mem_address);
+extern int rk05_read_sector(uint8_t drive, uint16_t disk_address, uint16_t mem_address);
+extern int rk05_status(uint16_t * status);
+extern int rk05_write_track(uint8_t drive, uint16_t track);
+extern int rk05_read_track(uint8_t drive, uint16_t track);
+extern int rk05_read_disk(uint8_t disk, uint16_t * target);
+extern int rk05_write_disk(uint8_t disk, uint16_t * target);
+
+
+extern void rk05_perror(uint16_t status);
+
+
+
+
+#endif
diff --git a/sw/speed8/pc/rktool.c b/sw/speed8/pc/rktool.c
new file mode 100644 (file)
index 0000000..0835d37
--- /dev/null
@@ -0,0 +1,247 @@
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <fcntl.h>
+#include <string.h>
+#include <errno.h>
+#include <getopt.h>
+
+#include "speed8.h"
+#include "link.h"
+#include "rk05.h"
+#include "memory.h"
+#include "log.h"
+
+#define PORT "/dev/ttyUSB0"
+
+int fd;
+
+static const struct option longopts[]={
+  {"help",0,NULL,'h'},
+  {"read",0,NULL,'r'},
+  {"write",0,NULL,'w'},
+  {"verbose",0,NULL,'v'},
+  {"port",1,NULL,'p'},
+  {"drive",1,NULL,'d'},
+  {"quiet",0,NULL,'q'},
+  {"loglevel",1,NULL,'l'},
+  {"verify",0,NULL,'V'},
+  {NULL}
+};
+
+static const char optstring[]="hrwvp:d:l:qV";
+
+static void help(void){
+  fprintf(stderr, "\
+\n\
+    Usage: rktool {-r|-w} [-v] [-V] [-h] [-d<unit_number>] [-l<loglevel>] <imagefile>\n\
+\n\
+    This tool will help you to backup restore RK05 media on a PDP8 running speed8.\n\
+\n\
+    Options:\n\
+\n\
+    -r, --read       Read disk (default if nothing specified)\n\
+    -w, --write      Write disk\n\
+    -d, --drive=     Select drive number 0-3 (defaults to 0)\n\
+    -p, --port=      Serial port to use (defaults to /dev/ttyUSB0)\n\
+    -V, --verify     Verify after read or write\n\
+    -v, --verbose    Be verbose\n\
+    -q, --quiet      Be quiet, opposite of -v\n\
+    -l, --loglevel=  Set loglevel for debugging purposes\n\
+    -h, --help       Display this help and exit\n\
+\n\
+");
+  exit(100);
+}
+
+static  char defaultport[]=PORT;
+
+int main(int argc, char ** argv){
+  int res;
+  int e;
+  
+  int drive=0;
+  #define READ 0
+  #define WRITE 1
+  int action=READ;
+  char * port=defaultport;
+  char * file=NULL;
+  int verify=0;
+  char opt;
+
+  opterr=1;
+  opt=getopt_long(argc,argv,optstring,longopts,NULL);
+
+  while(opt>0){
+    switch(opt){
+    case 'v':
+      loglevel++;
+      break;
+    case 'h': 
+      help();
+      break;
+    case 'r':
+      action=READ;
+      break;
+    case 'w':
+      action=WRITE;
+      break;
+    case 'p':
+      if (!optarg) {
+       error("-p needs an argument!\n");
+       exit(1);
+      }
+      port=optarg;
+      break;
+    case 'd':
+      if (!optarg) {
+       error("-d needs an argument!\n");
+       exit(1);
+      }
+      drive=strtol(optarg, NULL, 0);
+      break;
+    case 'q':
+      loglevel=LL_QUIET;
+      break;
+    case 'l':
+      if (!optarg) {
+       error("-l needs an argument!\n");
+       exit(1);
+      }
+      loglevel=strtol(optarg,NULL,0);
+      break;
+    case 'V':
+      verify=1;
+      break;
+    case '?':
+      help();
+      break;
+    }
+    opt=getopt_long(argc,argv,optstring,longopts,NULL);
+  }
+
+  file=argv[optind];
+
+  if (!file) {
+    fprintf(stderr,"Error: No file specified!\n");
+    help();
+  }
+
+  debug("Opening port \"%s\".\n",port);
+  debug("Image file: %s\n", file);
+  debug("Flushing first\n");
+
+  res=link_flush(port);
+
+  if (res <0){
+    ERROR perror("While initializing link");
+    return -1;
+  }
+  debug("Opening for real now\n");
+  res=link_open(port);
+  if (res <0){
+    ERROR perror("While initializing link");
+    return -1;
+  }
+
+  if (action==WRITE){
+    uint16_t origin[RK05_DISKSIZE];
+    uint16_t readback[RK05_DISKSIZE];
+
+    int fd=open(file,O_RDONLY);
+    if (fd<0){
+      ERROR perror("While opening file");
+      exit(2);
+    }
+    res=read(fd,(char*)origin,sizeof(origin));
+    WARN if (res<RK05_DISKSIZE*2) 
+      warn("Read only %i bytes! Hope, that's ok.\n",res);
+    close(fd);
+
+    res=rk05_write_disk(drive,origin);
+    if(res<0){
+      ERROR perror("While writing disk image");
+      exit(3);
+    }
+    
+    if (verify){
+      info("Reading back image.\n");
+      res=rk05_read_disk(drive,readback);
+      if(res<0){
+       ERROR perror("While reading disk image");
+       exit(3);
+      }
+      int i;
+      e=0;
+      int cylinder,side,sector,word;
+      for (cylinder=0; cylinder<RK05_CYLINDERS;cylinder++)
+       for (side=0; side<1; side++)
+         for (sector=0; sector<16; sector++)
+           for (word=0; word<256; word++){
+             i=cylinder*2*16*256+side*16*256+sector*256+word;
+             if (readback[i]!=origin[i]){
+               error("Verify mismatch! Cylinder %i, Side %i, Sector %i, Word %i\n",
+                     cylinder,side,sector,word);
+         e++;
+       }
+      }
+      if (e) {
+       error("Found %i verify errors!\n",e);
+       exit(3);
+      }
+    } // verify
+  } // WRITE
+
+  if (action==READ){
+
+    uint16_t target[RK05_DISKSIZE];
+    uint16_t target2[RK05_DISKSIZE];
+
+    info("Reading drive %i\n",drive);
+    res=0;
+    res=rk05_read_disk(drive,target);
+    if(res<0){
+      ERROR perror("While reading disk image");
+      exit(3);
+    }
+    if (verify){
+      e=0;
+      info("Reading again\n");
+      res=rk05_read_disk(drive,target2);
+      if(res<0){
+       ERROR perror("While reading disk image");
+       exit(3);
+      }
+      int i;
+      for (i=0; i<RK05_DISKSIZE ;i++){
+       debug("verify: %i\n",i);
+       if (target[i]!=target2[i]){
+         error("Verify mismatch! Cylinder %i, Side %i, Sector %i, Word %i\n",
+               i>>13,(i>>12)&1,(i>>8)&15,i&256);
+         e++;
+       }
+      }
+    }
+    int fd=open(file,O_WRONLY|O_CREAT|O_TRUNC,0640);
+    if (fd<0){
+      ERROR perror("While opening file");
+      exit(2);
+    }
+    debug("File \"%s\" opened.\n",file);
+    res=write(fd,target,sizeof(target));
+    if (res!=sizeof(target)){
+      ERROR perror ("While writing file to disk");
+      exit(1);
+    }
+    
+  }
+  
+  link_close();
+  link_flush(file);
+  if (e) {
+    error("Found %i verify errors!\n",e);
+    exit(5);
+  }
+  return 0;
+}
diff --git a/sw/speed8/pc/speed8.h b/sw/speed8/pc/speed8.h
new file mode 100644 (file)
index 0000000..5dc7ed5
--- /dev/null
@@ -0,0 +1,30 @@
+#ifndef SPEED8_H
+#define SPEED8_H
+
+
+#define ATTENTION 036
+
+#define CMD_GET_BUFFER 00001
+#define CMD_PUT_BUFFER 00002
+
+#define CMD_RK_GO      00003
+#define CMD_RK_STATUS  00004
+#define CMD_RK_R_TRACK 00005
+#define CMD_RK_W_TRACK 00006
+#define CMD_RK_RECAL0  00007
+#define CMD_RK_RECAL1  00107
+#define CMD_RK_RECAL2  00207
+#define CMD_RK_RECAL3  00307
+
+
+
+
+extern int get_buffer(uint16_t * buffer);
+extern int put_buffer(uint16_t * buffer);
+
+
+
+
+
+
+#endif
diff --git a/sw/speed8/pdp8/.gitignore b/sw/speed8/pdp8/.gitignore
new file mode 100644 (file)
index 0000000..5ba71cc
--- /dev/null
@@ -0,0 +1,3 @@
+*.lst
+*.rim
+*.BN
\ No newline at end of file
diff --git a/sw/speed8/pdp8/Makefile b/sw/speed8/pdp8/Makefile
new file mode 100644 (file)
index 0000000..bcd9ef4
--- /dev/null
@@ -0,0 +1,19 @@
+all: speed8.bin speed8.rim
+
+%.bin:%.pal
+       @echo PAL $@
+       @palbart $^
+
+%.rim:%.pal
+       @echo PAL $@
+       @palbart -r $^
+
+clean:
+       @echo CLEAN
+       @rm -f *.lst *.bin *.rim *.err *~
+
+boot:  speed8.bin
+       @echo BOOT
+       @rb speed8.bin
+
+.PHONY: all clean boot
diff --git a/sw/speed8/pdp8/README b/sw/speed8/pdp8/README
new file mode 100644 (file)
index 0000000..fda7480
--- /dev/null
@@ -0,0 +1,6 @@
+
+Speed 8 PDP8 part.
+
+speed8.pal: The source code
+SPEED8.PA: Binary source code, created from speed8.pal
+
diff --git a/sw/speed8/pdp8/SPEED8.PA b/sw/speed8/pdp8/SPEED8.PA
new file mode 100644 (file)
index 0000000..4ad1464
--- /dev/null
@@ -0,0 +1,256 @@
+*30
+       6743
+       5031
+
+*34
+       0200
+       1036
+       1046
+       1001
+       1020
+       0400
+       0412
+       0600
+       0646
+       0730
+       7600
+*200
+       4435
+       1274
+       7440
+       5200
+       4435
+       3310
+       1310
+       1275
+       7440
+       5214
+       4437
+       5200
+       7200
+       1310
+       1276
+       7440
+       5223
+       4440
+       5200
+       7200
+       1310
+       1277
+       7440
+       5232
+       4441
+       5200
+       7200
+       1310
+       1300
+       7440
+       5241
+       4442
+       5200
+       7200
+       1310
+       1301
+       7440
+       5247
+       4443
+       7200
+       1310
+       1302
+       7440
+       5255
+       4444
+       7200
+       1310
+       0273
+       1303
+       7440
+       5271
+       1310
+       7012
+       7012
+       7010
+       0272
+       4445
+       5200
+       0006
+       7477
+       7742
+       7777
+       7776
+       7775
+       7774
+       7773
+       7772
+       7771
+       7671
+       7571
+       7471
+       7777
+*401
+       7301
+       6742
+       4435
+       6744
+       4435
+       6746
+       4435
+       6743
+       5600
+       0000
+       6741
+       5213
+       6745
+       4436
+       5612
+*601
+       7200
+       1322
+       3321
+       7301
+       6742
+       7200
+       3316
+       6744
+       4435
+       3323
+       1323
+       1324
+       6746
+       4435
+       3320
+       1320
+       6743
+       7200
+       2320
+       1316
+       1315
+       3316
+       1320
+       6741
+       5230
+       7301
+       6742
+       1323
+       1325
+       6746
+       1316
+       6744
+       1320
+       6743
+       2321
+       5222
+       5600
+       0000
+       7200
+       1322
+       3321
+       7301
+       6742
+       7200
+       3316
+       6744
+       4435
+       3323
+       1323
+       1326
+       6746
+       4435
+       3320
+       1320
+       6743
+       7200
+       2320
+       1316
+       1315
+       3316
+       1320
+       6741
+       5276
+       7301
+       6742
+       1323
+       1327
+       6746
+       7200
+       1316
+       6744
+       1320
+       6743
+       2321
+       5270
+       5646
+       0400
+       0000
+       0020
+       0000
+       0000
+       7761
+       0000
+       0010
+       1010
+       4010
+       5010
+       0000
+       3347
+       7301
+       6742
+       1347
+       6746
+       7326
+       6742
+       6741
+       5340
+       1346
+       1347
+       6746
+       5730
+       0200
+       0000
+*1000
+       0000
+       0000
+       7200
+       3200
+       6211
+       7200
+       1600
+       6417
+       5207
+       7002
+       6417
+       5212
+       2200
+       5205
+       6201
+       5601
+       0000
+       7200
+       3200
+       6211
+       6407
+       5224
+       7002
+       6407
+       5227
+       3600
+       2200
+       5224
+       6201
+       5620
+       0000
+       7200
+       6407
+       5240
+       7002
+       6407
+       5243
+       5636
+       0000
+       6417
+       5247
+       7002
+       6417
+       5252
+       7002
+       5646
diff --git a/sw/speed8/pdp8/speed8.pal b/sw/speed8/pdp8/speed8.pal
new file mode 100644 (file)
index 0000000..617c195
--- /dev/null
@@ -0,0 +1,392 @@
+
+/ The RK fast access program
+/ Needs kturbo and tturbo instructions provided by omni-usb
+
+/ Communication is word-wise via the subroutines 
+/ RWRD and SWRD and their aliases RWORD and SWORD
+
+/ All intelligence is located on the PC side :-)
+
+       IDEV=400
+       ODEV=410
+
+       KTURBO=6007 IDEV
+       TTURBO=6007 ODEV
+
+       /  Commands sent from the PC
+       ATT=36
+       CMGET=1
+       CMPUT=2
+
+       /RK05 commands
+       CRKGO=3              / Execute command
+       CRKST=4             / Return drive status
+       CRKRT=5         / Read track
+       CRKWT=6         / Write track
+       CRKR0=007
+       CRKR1=107
+       CRKR2=207
+       CRKR3=307
+
+       /GO BACK TO RIMLOADER
+       CBOOT=04566
+
+/ RK05 commands are followed by the disk address and transfer length
+/ <ATT> <CRKGO> <CA_REG> <CM_REG> <DA_REG>
+
+*7700   JMP I XSTART
+
+*10
+AUTO0, 0
+AUTO1, 0
+AUTO2, 0
+AUTO3, 0
+AUTO4, 0
+AUTO5, 0
+AUTO6, 0
+AUTO7, 0
+
+*30
+RKBOOT,        DLAG
+       JMP .
+       
+*34
+XSTART, START
+XRWRD, RWRD
+XSWRD, SWRD
+XSBUF, SBUF
+XRBUF, RBUF
+XRKGO,  RKGO
+XRKST, RKST
+XRKRT,  RKRT
+XRKWT,  RKWT
+XRECAL,        RECAL
+XEXIT, EXIT
+
+        DSKP=6741
+        DCLR=6742
+        DLAG=6743
+        DLCA=6744
+        DRST=6745
+        DLDC=6746
+       
+       RWORD=JMS I XRWRD
+       SWORD=JMS I XSWRD
+
+*200
+START,
+       RWORD
+       TAD KATT
+       SZA
+       JMP START
+       RWORD
+       DCA CMD
+       TAD CMD
+       TAD KCMGET
+       SZA
+       JMP NO1
+       JMS I XSBUF
+       JMP START
+       
+NO1,
+       CLA
+       TAD CMD
+       TAD KCMPUT
+       SZA
+       JMP NO2
+       JMS I XRBUF
+       JMP START
+       
+NO2,
+       CLA
+       TAD CMD
+       TAD KCRKGO
+       SZA
+       JMP NO3
+       JMS I XRKGO
+       JMP START
+NO3,
+       CLA
+       TAD CMD
+       TAD KCRKST
+       SZA
+       JMP NO4
+       JMS I XRKST
+       JMP START
+NO4,
+       CLA
+       TAD CMD
+       TAD KCRKRT
+       SZA
+       JMP NO5
+       JMS I XRKRT
+NO5,
+
+       CLA
+       TAD CMD
+       TAD KCRKWT
+       SZA
+       JMP NO6
+       JMS I XRKWT
+NO6,
+               
+       CLA
+       TAD CMD
+       AND K7477       / MASK OUT DRIVE SELECT
+       TAD KCRKR0
+       SZA
+       JMP NO7
+       TAD CMD
+       RTR
+       RTR
+       RAR
+       AND K6
+       JMS I XRECAL
+                       
+NO7,   
+
+       JMP START
+
+K6,    6
+K7477, 7477
+KATT,  -ATT
+KCMGET, -CMGET
+KCMPUT, -CMPUT
+KCRKGO,        -CRKGO
+KCRKST, -CRKST
+KCRKRT, -CRKRT
+KCRKWT, -CRKWT
+KCRKR0, -CRKR0
+KCRKR1, -CRKR1
+KCRKR2, -CRKR2
+KCRKR3, -CRKR3
+XBOOT,  7777
+CMD, 0
+
+/***********************************************
+PAGE
+/***********************************************
+
+RKGO,  0
+
+       CLA CLL IAC     / Standard read from manual
+        DCLR
+
+       RWORD           / MEMORY ADDRESS
+       DLCA
+
+       RWORD           / COMMAND REGISTER
+       DLDC
+
+       RWORD           / DISK ADDRESS
+       DLAG
+
+       JMP I RKGO
+
+RKST,  0
+       DSKP    
+       JMP .-1
+       DRST
+       SWORD
+       JMP I RKST
+/***********************************************
+PAGE
+
+RKRT,  0
+       CLA
+       TAD     K17N
+       DCA     CNT
+       CLA CLL IAC     / Standard read from manual
+        DCLR
+       
+       CLA
+       DCA     TPTR
+       DLCA
+
+       RWORD
+       DCA CMDP
+       TAD CMDP
+       TAD CMDR1
+       DLDC
+       
+       RWORD
+       DCA ADR
+       TAD ADR
+       DLAG
+       
+
+RTTL,  CLA
+       ISZ ADR
+       TAD     TPTR
+       TAD     K400
+       DCA     TPTR
+       TAD     ADR
+
+       DSKP
+       JMP .-1
+
+       CLA CLL IAC     / Standard read from manual
+        DCLR
+
+       TAD CMDP
+       TAD CMDR2
+       DLDC
+
+       TAD     TPTR
+       DLCA
+       TAD ADR
+       DLAG
+       ISZ CNT
+       JMP RTTL
+       JMP I RKRT
+
+/**********************************************
+
+RKWT,  0
+       CLA
+       TAD K17N
+       DCA     CNT
+       CLA CLL IAC     / Clear control
+        DCLR
+       
+       CLA     
+       DCA     TPTR
+       DLCA
+       
+       RWORD
+       DCA CMDP
+       TAD CMDP
+       TAD CMDW1
+       DLDC
+       
+       RWORD
+       DCA ADR
+       TAD ADR
+       DLAG
+
+WTTL,  CLA
+       ISZ ADR
+       TAD     TPTR
+       TAD     K400
+       DCA     TPTR
+       TAD     ADR
+
+       DSKP
+       JMP .-1
+
+       CLA CLL IAC     / Standard read from manual
+        DCLR
+
+       TAD CMDP
+       TAD CMDW2
+       DLDC
+       CLA
+
+       TAD     TPTR
+       DLCA
+
+       TAD ADR
+       DLAG
+       ISZ CNT
+       JMP WTTL
+       JMP I RKWT
+
+K400,  400
+TPTR,  0
+K20N,  20
+ADR,   0
+CNT,   0
+K17N,  -17
+CMDP,  0
+CMDR1, 0010
+CMDR2, 1010
+CMDW1,         4010
+CMDW2,         5010
+/**********************************************
+
+RECAL, 0
+       DCA     DRIVE   / SAVE DRIVE NUMBER*2 found in AC
+        CLA CLL IAC     / Recal drive, clear errors and set to cyl 0
+        DCLR            / From manual
+        TAD DRIVE
+        DLDC
+        CLA CLL CML RTL
+        DCLR
+        DSKP
+        JMP .-1
+        TAD K0200
+        TAD DRIVE
+        DLDC
+        JMP I RECAL
+
+K0200, 200
+DRIVE, 0     
+
+/***********************************************
+/ Memory dump/restore routines
+
+PAGE
+
+PTR,   0
+
+SBUF,  0
+       CLA
+       DCA PTR
+       CDF 10
+SLOP,  CLA
+       TAD I PTR
+       TTURBO
+       JMP .-1
+       BSW
+       TTURBO
+       JMP .-1
+       ISZ PTR
+       JMP SLOP
+        CDF 0
+       JMP I SBUF
+
+
+RBUF,  0
+       CLA
+       DCA PTR
+       CDF 10
+RLOP,  KTURBO
+       JMP .-1
+       BSW
+       KTURBO
+       JMP .-1
+       DCA I PTR
+       ISZ PTR
+       JMP RLOP
+        CDF 0
+       JMP I RBUF
+
+RWRD,  0
+       CLA
+       KTURBO
+       JMP .-1
+       BSW
+       KTURBO
+       JMP .-1
+       JMP I RWRD
+
+SWRD,  0
+       TTURBO
+       JMP .-1
+       BSW
+       TTURBO
+       JMP .-1
+       BSW
+       JMP I SWRD
+
+
+/***********************************************
+/ What hapens on exit to 7600
+/ Relies on the fact that OS/8 won't allow overwriting here.
+
+*7600
+EXIT,  HLT
+       JMP 30
+
+
+$