1 /* ibm1130_plot.c: IBM 1130 1627 plotter emulation
3 Based on the SIMH simulator package written by Robert M Supnik
9 2006.1.2 - Rewritten as plotter routine by Carl V Claunch
11 * (C) Copyright 2004, Brian Knittel.
12 * You may freely use this program, but: it offered strictly on an AS-IS, AT YOUR OWN
13 * RISK basis, there is no warranty of fitness for any purpose, and the rest of the
14 * usual yada-yada. Please keep this notice and the copyright in any distributions
17 * This is not a supported product, but I welcome bug reports and fixes.
18 * Mail to simh@ibm1130.org
21 #include "ibm1130_defs.h"
23 #ifndef ENABLE_PLOT_SUPPORT
26 "PLOT", NULL
, NULL
, NULL
,
31 void xio_1627_plotter (int32 addr
, int32 func
, int32 modify
)
33 /* silently eat any plotter commands */
40 /***************************************************************************************
41 * 1627 model 1 plotter (based on Calcomp 535 which was sold as IBM 1627)
43 * - 11" wide carriage, addressible in .01" steps
44 * - continous sheet paper up to 120' in length
45 * - sheet moveable in .01" steps, either direction
46 * - switchable pen, in various colors and line widths
48 * Simulator implementation will create a JPEG image corresponding to a
49 * landscape mode sheet of paper, the width of the carriage at 11".
50 * A diagram of more than 8" of paper travel will span printed pages
53 * When an 'att plot' command is issued a file is created based on the
54 * default or currently set values of paper length, starting
55 * position of the pen in both X and Y axes, pen color and pen width.
56 * Based on the number of logical pages of paper, the command will create
57 * the proper size canvas internally and create the output JPEG file.
59 * When a 'det plot' command is issued, the plotter image will be converted
60 * into the file that was specified during the attach process. The
61 * image is not viewable until this point, unless an examine plot is
62 * issued which will dump the current state of the paper into the file.
64 * The 'set plot' command can set pen width, paper length, pen color,
65 * current carriage X and Y coordinates. Paper length can be set
66 * to alter the default of 800 (8"); changes are ignored until
67 * the next 'attach' command. The current carriage x and y positions
68 * can be set at any time and will go into effect immediately, just
69 * as the pen color and pen width can be altered on the fly.
71 * NOTE: requires gd library and definition of ENABLE_PLOT_SUPPORT in makefile or Visual C configuration
72 * gd is not included in the main simh and ibm1130.org distributions at the present time.
73 ***************************************************************************************/
75 #define PLOT1627_DSW_OP_COMPLETE 0x8000
76 #define PLOT1627_DSW_BUSY 0x0200
77 #define PLOT1627_DSW_NOT_READY 0x0100
79 #define IS_ONLINE(u) (((u)->flags & (UNIT_ATT|UNIT_DIS)) == UNIT_ATT)
80 #define IS_DEBUG ((plot_unit->flags & UNIT_DEBUG) == UNIT_DEBUG)
81 #define IS_PENDOWN ((plot_unit->flags & UNIT_PEN) == UNIT_PEN)
83 static t_stat
plot_svc (UNIT
*uptr
); /* activity routine */
84 static t_stat
plot_reset (DEVICE
*dptr
); /* reset of 1130 */
85 static t_stat
plot_attach (UNIT
*uptr
, char *cptr
); /* attach, loads plotter */
86 static t_stat
plot_detach (UNIT
*uptr
); /* detach and save image */
87 static t_stat
plot_examine (UNIT
*uptr
); /* update file with current canvas */
88 static t_stat
plot_set_length (UNIT
*uptr
, int32 val
, char * ptr
, void *desc
); /* set paper length */
89 static t_stat
plot_set_pos (UNIT
*uptr
, int32 val
, char * ptr
, void *desc
); /* reset current X/Y position */
90 static t_stat
plot_show_vals(FILE *fp
, UNIT
*uptr
, int32 val
, void *descrip
); /* print x, y and length */
91 static t_stat
plot_show_nl(FILE *fp
, UNIT
*uptr
, int32 val
, void *descrip
); /* overcome wacky simh behavior */
92 static void update_pen(void); /* will ensure pen action is correct when changes made */
93 static t_stat
plot_validate_change (UNIT
*uptr
, int32 val
, char * ptr
, void *desc
); /* when set command issued */
94 static void process_cmd(void); /* does actual drawing for plotter */
96 extern int32 sim_switches
; /* switches set on simh command */
97 static int16 plot_dsw
= 0; /* device status word */
98 static int16 plot_cmd
= 0; /* the command to process */
99 static int32 plot_wait
= 1000; /* plotter movement wait */
100 static int32 plot_xpos
= 0; /* current X position */
101 static int32 plot_xmax
= 799; /* end of paper */
102 static int32 plot_ypos
= 0; /* current Y position */
103 static int32 plot_ymax
= 1099; /* right edge of carriage */
105 #define PEN_DOWN 0x80000000
106 #define PEN_UP 0x00000000
107 static int32 plot_pen
= PEN_UP
; /* current pen position */
109 static int black_pen
; /* holds color black */
110 static int blue_pen
; /* holds color blue */
111 static int red_pen
; /* holds color red */
112 static int green_pen
; /* holds color green */
113 static int yellow_pen
; /* holds yellow color */
114 static int purple_pen
; /* holds color purple */
115 static int ltgrey_pen
; /* holds light grey */
116 static int grey_pen
; /* holds grey */
117 static int white_background
; /* holds white of paper roll */
118 static int plot_pwidth
; /* set and display variable */
119 static int plot_pcolor
; /* set and display variable */
120 static int need_update
= 0; /* flag to force and update_pen() */
121 static gdImagePtr image
; /* pointer to our canvas */
123 #define UNIT_V_COLOR (UNIT_V_UF + 0) /* color of selected pen - 3 bits */
124 #define UNIT_V_WIDTH (UNIT_V_UF + 3) /* width of pen - two bits */
125 #define UNIT_V_NOOP (UNIT_V_UF + 5) /* dummy for set/show commands */
126 #define UNIT_V_DEBUG (UNIT_V_UF + 6) /* for -d switch on attach command */
127 #define UNIT_V_PEN (UNIT_V_UF + 7) /* track pen state */
129 #define UNIT_WIDTH (3u << UNIT_V_WIDTH) /* two bits */
130 #define UNIT_COLOR (7u << UNIT_V_COLOR) /* three bits */
131 #define UNIT_NOOP (1u << UNIT_V_NOOP) /* dummy for set/show */
132 #define UNIT_DEBUG (1u << UNIT_V_DEBUG) /* shows debug mode on */
133 #define UNIT_PEN (1u << UNIT_V_PEN) /* the pen state bit */
135 #define PEN_BLACK (0u << UNIT_V_COLOR)
136 #define PEN_RED (1u << UNIT_V_COLOR)
137 #define PEN_BLUE (2u << UNIT_V_COLOR)
138 #define PEN_GREEN (3u << UNIT_V_COLOR)
139 #define PEN_YELLOW (4u << UNIT_V_COLOR)
140 #define PEN_PURPLE (5u << UNIT_V_COLOR)
141 #define PEN_LTGREY (6u << UNIT_V_COLOR)
142 #define PEN_GREY (7u << UNIT_V_COLOR)
144 #define SET_COLOR(op) {plot_unit[0].flags &= ~UNIT_COLOR; plot_unit[0].flags |= (op);}
145 #define GET_COLOR (plot_unit[0].flags & UNIT_COLOR)
150 #define GREEN 0,255,0
151 #define YELLOW 200,200,0
152 #define PURPLE 150,0,150
153 #define LTGREY 200,200,200
154 #define GREY 120,120,120
155 #define WHITE 255,255,255
157 #define PEN_SINGLE (0u << UNIT_V_WIDTH)
158 #define PEN_DOUBLE (1u << UNIT_V_WIDTH)
159 #define PEN_TRIPLE (2u << UNIT_V_WIDTH)
160 #define PEN_QUAD (3u << UNIT_V_WIDTH)
162 #define GET_WIDTH() (plot_unit[0].flags & UNIT_WIDTH)
163 #define SET_WIDTH(cd) {plot_unit[0].flags &= ~UNIT_WIDTH; un.flags |= (cd);}
166 { UDATA (&plot_svc
, UNIT_ATTABLE
, 0) },
170 { HRDATA (DSW
, plot_dsw
, 16) }, /* device status word */
171 { DRDATA (WTIME
, plot_wait
, 24), PV_LEFT
}, /* plotter movement wait */
172 { DRDATA (Xpos
, plot_xpos
, 32), PV_LEFT
}, /* Current X Position*/
173 { DRDATA (Ypos
, plot_ypos
, 32), PV_LEFT
}, /* Current Y Position*/
174 { FLDATA (PenDown
, plot_pen
, 0)}, /* Current pen position - 1 = down */
175 { DRDATA (PaperSize
, plot_xmax
, 32), PV_LEFT
}, /* Length of paper in inches */
179 { UNIT_COLOR
, PEN_BLACK
, "black", "BLACK", &plot_validate_change
},
180 { UNIT_COLOR
, PEN_RED
, "red", "RED", &plot_validate_change
},
181 { UNIT_COLOR
, PEN_BLUE
, "blue", "BLUE", &plot_validate_change
},
182 { UNIT_COLOR
, PEN_GREEN
, "green", "GREEN", &plot_validate_change
},
183 { UNIT_COLOR
, PEN_YELLOW
, "yellow", "YELLOW", &plot_validate_change
},
184 { UNIT_COLOR
, PEN_PURPLE
, "purple", "PURPLE", &plot_validate_change
},
185 { UNIT_COLOR
, PEN_LTGREY
, "ltgrey", "LTGREY", &plot_validate_change
},
186 { UNIT_COLOR
, PEN_GREY
, "grey", "GREY", &plot_validate_change
},
187 { UNIT_WIDTH
, PEN_SINGLE
, "1.0", "1.0", &plot_validate_change
},
188 { UNIT_WIDTH
, PEN_DOUBLE
, "2.0", "2.0", &plot_validate_change
},
189 { UNIT_WIDTH
, PEN_TRIPLE
, "3.0", "3.0", &plot_validate_change
},
190 { UNIT_WIDTH
, PEN_QUAD
, "4.0", "4.0", &plot_validate_change
},
191 { UNIT_PEN
, UNIT_PEN
, "pendown", "PENDOWN", &plot_validate_change
},
192 { UNIT_PEN
, 0, "penup", "PENUP", &plot_validate_change
},
193 /* below is dummy entry to trigger the show routine and print extended values */
194 { UNIT_NOOP
, 0, "", NULL
, NULL
, &plot_show_vals
},
195 /* extended entries must allow parm for both unit and dev, but
196 * then they will print the value twice for a 'show plot' command
197 * therefore they are set to not display unless explicity requested
198 * and the special dummy NOOP entry will cause the print of these values */
199 { MTAB_XTD
| MTAB_VAL
| MTAB_VUN
| MTAB_VDV
| MTAB_NMO
, 2,
200 "length", "LENGTH", &plot_set_length
, &plot_show_nl
, &plot_reg
[5]},
201 { MTAB_XTD
| MTAB_VAL
| MTAB_VDV
| MTAB_VUN
| MTAB_NMO
, 0,
202 "Xpos", "XPOS", &plot_set_pos
, &plot_show_nl
, &plot_reg
[2]},
203 { MTAB_XTD
| MTAB_VAL
| MTAB_VDV
| MTAB_VUN
| MTAB_NMO
, 1,
204 "Ypos", "YPOS", &plot_set_pos
, &plot_show_nl
, &plot_reg
[3]},
208 "PLOT", plot_unit
, plot_reg
, plot_mod
,
209 1, 16, 16, 1, 16, 16,
210 NULL
, NULL
, plot_reset
,
211 NULL
, plot_attach
, plot_detach
};
213 /* xio_1627_plotter - XIO command interpreter for the 1627 plotter model 1 */
215 void xio_1627_plotter (iocc_addr
, iocc_func
, iocc_mod
)
219 if (! IS_ONLINE(plot_unit
) ) {
220 SETBIT(plot_dsw
, PLOT1627_DSW_NOT_READY
); /* set not ready */
221 if (IS_DEBUG
) printf("Plotter has no paper, ignored\n");
222 return; /* and ignore */
226 case XIO_READ
: /* read XIO */
227 xio_error("Read XIO not supported by 1627 plotter");
230 case XIO_WRITE
: /* write: do one plotter operation */
231 if ((plot_dsw
& PLOT1627_DSW_NOT_READY
)) {
232 if (IS_DEBUG
) printf("Wrote to non-ready Plotter\n");
235 plot_cmd
= (uint16
) ( M
[iocc_addr
& mem_mask
] >> 10 ); /* pick up command */
236 process_cmd(); /* interpret command */
237 sim_activate(plot_unit
, plot_wait
); /* schedule interrupt */
238 SETBIT(plot_dsw
, PLOT1627_DSW_BUSY
); /* mark it busy */
241 case XIO_SENSE_DEV
: /* sense device status */
242 ACC
= plot_dsw
; /* get current status */
243 if (iocc_mod
& 0x01) { /* reset interrupts */
244 CLRBIT(plot_dsw
, PLOT1627_DSW_OP_COMPLETE
);
245 CLRBIT(ILSW
[3], ILSW_3_1627_PLOTTER
);
249 case XIO_CONTROL
: /* control XIO */
250 xio_error("Control XIO not supported by 1627 plotter");
254 sprintf(msg
, "Invalid 1627 Plotter XIO function %x", iocc_func
);
260 // plot_svc - 1627 plotter operation complete
262 static t_stat
plot_svc (UNIT
*uptr
)
264 CLRBIT(plot_dsw
, PLOT1627_DSW_BUSY
); /* clear reader busy flag */
266 SETBIT(plot_dsw
, PLOT1627_DSW_OP_COMPLETE
); /* indicate read complete */
268 SETBIT(ILSW
[3], ILSW_3_1627_PLOTTER
); /* initiate interrupt */
274 /* plot_reset - reset emulated plotter */
276 static t_stat
plot_reset (DEVICE
*dptr
)
281 sim_cancel(plot_unit
);
283 CLRBIT(plot_dsw
, PLOT1627_DSW_BUSY
| PLOT1627_DSW_OP_COMPLETE
);
285 if (IS_DEBUG
) printf("reset routine for Plotter\n");
287 CLRBIT(ILSW
[3], ILSW_3_1627_PLOTTER
);
294 /* plot_attach - attach file to simulated plotter */
296 static t_stat
plot_attach (UNIT
*uptr
, char *cptr
)
300 CLRBIT(uptr
->flags
, UNIT_DEBUG
);
301 if (sim_switches
& SWMASK('D')) SETBIT(uptr
->flags
, UNIT_DEBUG
);
303 /* get the output file by using regular attach routine */
304 result
= attach_unit(uptr
, cptr
);
306 if (result
!= SCPE_OK
) {
307 if (IS_DEBUG
) printf("problem attaching file\n");
311 SETBIT(plot_dsw
, PLOT1627_DSW_NOT_READY
); /* assume failure */
313 /* set up our canvas at the desired size */
314 image
= gdImageCreate(plot_ymax
+1,plot_xmax
+1); /* create our canvas */
316 if (IS_DEBUG
) printf("problem creating image canvas\n");
320 /* set up the basic colors after image created */
321 white_background
= gdImageColorAllocate(image
,WHITE
); /* white is background */
322 black_pen
= gdImageColorAllocate(image
,BLACK
); /* load up black color */
323 blue_pen
= gdImageColorAllocate(image
,BLUE
); /* load up blue color */
324 red_pen
= gdImageColorAllocate(image
,RED
); /* load up red color */
325 green_pen
= gdImageColorAllocate(image
,GREEN
); /* load up green color */
326 yellow_pen
= gdImageColorAllocate(image
,YELLOW
); /* load up yellow color */
327 purple_pen
= gdImageColorAllocate(image
,PURPLE
); /* load up purple color */
328 ltgrey_pen
= gdImageColorAllocate(image
,LTGREY
); /* load up light grey color */
329 grey_pen
= gdImageColorAllocate(image
,GREY
); /* load up grey color */
331 if ( (white_background
== -1) || (black_pen
== -1) ||
332 (red_pen
== -1) || (blue_pen
== -1) || (green_pen
== -1) ||
333 (purple_pen
== -1) || (ltgrey_pen
== -1) || (grey_pen
== -1) ) {
334 if (IS_DEBUG
) printf("problem allocating pen colors\n");
338 CLRBIT(plot_dsw
, PLOT1627_DSW_NOT_READY
); /* we're in business */
340 update_pen(); /* routine to ensure pen is okay */
345 /* pen updating routine, called at attach and whenever we reset the values */
347 void update_pen (void)
352 if (!IS_ONLINE(plot_unit
)) return; /* only do this if attached */
354 /* pick up latest color as active pen */
358 plot_pcolor
= black_pen
;
362 plot_pcolor
= red_pen
;
366 plot_pcolor
= blue_pen
;
370 plot_pcolor
= green_pen
;
374 plot_pcolor
= yellow_pen
;
378 plot_pcolor
= purple_pen
;
382 plot_pcolor
= ltgrey_pen
;
386 plot_pcolor
= grey_pen
;
390 if (IS_DEBUG
) printf("invalid pen color state\n");
391 plot_pcolor
= black_pen
;
395 /* set up anti-aliasing for the line */
396 gdImageSetAntiAliased(image
, plot_pcolor
);
398 /* pick up latest width for pen */
403 gdImageSetThickness(image
, 1);
408 gdImageSetThickness(image
, 3);
413 gdImageSetThickness(image
, 2);
418 gdImageSetThickness(image
, 4);
422 if (IS_DEBUG
) printf("invalid pen width\n");
424 gdImageSetThickness(image
, 1);
428 /* now ensure the pen state is accurate */
429 plot_pen
= IS_PENDOWN
? PEN_DOWN
: PEN_UP
;
433 /* plot_detach - detach file from simulated plotter */
434 static t_stat
plot_detach (UNIT
*uptr
)
441 SETBIT(plot_dsw
, PLOT1627_DSW_NOT_READY
);
443 /* copy images to files, close files, set device to detached, free gd memory */
445 buf
= gdImageGifPtr(image
,&size
);
447 if (IS_DEBUG
) printf("failure creating GIF in-memory\n");
451 fp
= uptr
->fileref
; /* get file attached to unit */
453 if (! fseek(fp
,0,SEEK_SET
)) { /* first we reset to begin of file */
454 if (IS_DEBUG
) printf("wrote out GIF to file\n");
455 result
= fwrite(buf
,1,size
,fp
); /* write out our image to the file */
458 gdFree(buf
); /* free up the memory of GIF format */
459 gdImageDestroy(image
); /* free up the canvas memory */
461 if (result
!= size
) { /* some problem writing it */
462 if (IS_DEBUG
) printf("error in write of image file\n");
466 return detach_unit(uptr
); /* have simh close the file */
469 /* process_cmd - implement the drawing actions of the plotter */
471 static void process_cmd (void)
475 /* first see if we set any changes to pen or position, do an update */
481 /* will move pen one step or flip pen up or down */
486 case 1: /* raise pen command */
488 plot_unit
->flags
= plot_unit
->flags
& (~UNIT_PEN
);
492 case 2: /* +Y command */
493 plot_ypos
= plot_ypos
+ 1;
496 case 4: /* -Y command */
497 plot_ypos
= plot_ypos
- 1;
500 case 8: /* -X command */
501 plot_xpos
= plot_xpos
- 1;
504 case 10: /* -X +Y command */
505 plot_xpos
= plot_xpos
- 1;
506 plot_ypos
= plot_ypos
+ 1;
509 case 12: /* -X -Y command */
510 plot_xpos
= plot_xpos
- 1;
511 plot_ypos
= plot_ypos
- 1;
514 case 16: /* +X command */
515 plot_xpos
= plot_xpos
+ 1;
518 case 18: /* +X +Y command */
519 plot_xpos
= plot_xpos
+ 1;
520 plot_ypos
= plot_ypos
+ 1;
523 case 20: /* +X -Y pen command */
524 plot_xpos
= plot_xpos
+ 1;
525 plot_ypos
= plot_ypos
- 1;
528 case 32: /* lower pen command */
530 plot_unit
->flags
= plot_unit
->flags
| UNIT_PEN
;
535 if (IS_DEBUG
) printf("invalid plotter command\n");
540 /* check to see if carriage has moved off any edge */
541 if ((plot_xpos
> (plot_xmax
+1)) || (plot_ypos
> (plot_ymax
+1)) ||
542 (plot_xpos
< 0) || (plot_ypos
< 0)) {
543 /* if so, ignore as 1627 has no way of signalling error */
544 if (IS_DEBUG
) printf(
545 "attempted to move carriage off paper edge %d %d for command %d\n",
546 plot_xpos
,plot_ypos
,plot_cmd
);
550 /* only draw a line if the pen was down during the movement command */
552 gdImageLine(image
, plot_ymax
-plot_ypos
, plot_xmax
-plot_xpos
, plot_ymax
-oldy
, plot_xmax
-oldx
, gdAntiAliased
);
553 /* semantics are 0,0 point is lower right */
559 /* plot_set_length - validate and store the length of the paper */
561 static t_stat
plot_set_length (UNIT
*uptr
, int32 set
, char *ptr
, void *desc
)
566 #define LONGEST_ROLL 1440000 /* longest is 120', 14400", 1,440,000 .01"s */
568 val
= strtotv (ptr
, &cptr
, (uint32
) 10); /* sim routine to get value */
569 if ((val
< 1) | (val
>= LONGEST_ROLL
)) { /* check valid range */
570 if (IS_DEBUG
) printf("setting paper more than 120' or less than 1 inch\n");
574 /* origin zero drawing, reduce by 1 but show command will fudge by adding it back */
575 *((int32
*)((REG
*) desc
)->loc
) = val
- 1;
580 /* plot_set_pos - validate and store the new position of the carriage */
582 static t_stat
plot_set_pos (UNIT
*uptr
, int32 set
, char *ptr
, void *desc
)
588 max
= (set
== 1) ? plot_ymax
: plot_xmax
;
589 val
= strtotv (ptr
, &cptr
, (uint32
) 10);
590 if ((val
< 0) | (val
> max
)) {
591 if (IS_DEBUG
) printf("error moving carriage off paper edge\n");
595 *((int32
*)((REG
*) desc
)->loc
) = val
;
600 /* routine to display the paper length and carriage position
601 * cannot use regular simh routine because it prints values twice,
602 * once for device and once for unit
605 static t_stat
plot_show_vals (FILE *fp
, UNIT
*uptr
, int32 val
, void *descrip
)
607 fprintf(fp
, "length=%d, Xpos=%d, Ypos=%d",plot_xmax
+1, plot_xpos
,plot_ypos
);
611 /* routine to add a terminating NL character when 'show plot length'
612 * or equivalent for xpos or ypos is issued, as simh will not append for us */
614 static t_stat
plot_show_nl(FILE *fp
, UNIT
*uptr
, int32 val
, void *descrip
)
619 disp
= (val
== 2) ? plot_xmax
+ 1 : ((val
== 1) ? plot_ypos
: plot_xpos
);
620 label
= (val
== 2) ? "length=" : ((val
== 1) ? "Ypos=" : "Xpos=");
622 fprintf(fp
, "%s%d\n", label
, disp
);
626 /* plot_validate_change - force the update_pen routine to be called after user changes pen setting */
628 static t_stat
plot_validate_change (UNIT
*uptr
, int32 set
, char *ptr
, void *desc
)
634 #endif /* ENABLE_PLOT_SUPPORT */