1 /* ibm1130_plot.c: IBM 1130 1627 plotter emulation
2 
3    Based on the SIMH simulator package written by Robert M Supnik
4 
5    Brian Knittel
6    Revision History
7 
8    2004.10.22 - Written.
9    2006.1.2 - Rewritten as plotter routine by Carl V Claunch
10 
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
15  * or modifications.
16  *
17  * This is not a supported product, but I welcome bug reports and fixes.
18  * Mail to simh@ibm1130.org
19  */
20 
21 #include "ibm1130_defs.h"
22 
23 #ifndef ENABLE_PLOT_SUPPORT
24 
25 	DEVICE plot_dev = {
26 		"PLOT", NULL, NULL, NULL,
27 		0, 16, 16, 1, 16, 16,
28 		NULL, NULL, NULL,
29 		NULL, NULL, NULL};
30 
xio_1627_plotter(int32 addr,int32 func,int32 modify)31 	void xio_1627_plotter	(int32 addr, int32 func, int32 modify)
32 	{
33 		/* silently eat any plotter commands */
34 	}
35 
36 #else
37 
38 #include "gd.h"
39 
40 /***************************************************************************************
41  *  1627 model 1 plotter (based on Calcomp 535 which was sold as IBM 1627)
42  *
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
47  *
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
51  *  in landscape mode.
52  *
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.
58  *
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.
63  *
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.
70  *
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  ***************************************************************************************/
74 
75 #define PLOT1627_DSW_OP_COMPLETE			0x8000
76 #define PLOT1627_DSW_BUSY					0x0200
77 #define PLOT1627_DSW_NOT_READY				0x0100
78 
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)
82 
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 */
95 
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 */
104 
105 #define PEN_DOWN 0x80000000
106 #define PEN_UP   0x00000000
107 static int32 plot_pen = PEN_UP;						/* current pen position */
108 
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 */
122 
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 */
128 
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 */
134 
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)
143 
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)
146 
147 #define BLACK           0,0,0
148 #define BLUE            0,0,255
149 #define RED             255,0,0
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
156 
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)
161 
162 #define GET_WIDTH()		(plot_unit[0].flags & UNIT_WIDTH)
163 #define SET_WIDTH(cd)	{plot_unit[0].flags &= ~UNIT_WIDTH; un.flags |= (cd);}
164 
165 UNIT plot_unit[] = {
166 	{ UDATA (&plot_svc, UNIT_ATTABLE, 0) },
167 };
168 
169 REG plot_reg[] = {
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 */
176 	{ NULL }  };
177 
178 MTAB plot_mod[] = {
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]},
205 	{ 0 }  };
206 
207 DEVICE plot_dev = {
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};
212 
213 /* xio_1627_plotter - XIO command interpreter for the 1627 plotter model 1 */
214 
xio_1627_plotter(iocc_addr,iocc_func,iocc_mod)215 void xio_1627_plotter (iocc_addr, iocc_func, iocc_mod)
216 {
217 	char msg[80];
218 
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 */
223 	}
224 
225 	switch (iocc_func) {
226 		case XIO_READ:												/* read XIO */
227 			xio_error("Read XIO not supported by 1627 plotter");
228 			break;
229 
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");
233                  break;
234             }
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 */
239 			break;
240 
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);
246 			}
247 			break;
248 
249 		case XIO_CONTROL:											/* control XIO */
250 			xio_error("Control XIO not supported by 1627 plotter");
251 			break;
252 
253 		default:
254 			sprintf(msg, "Invalid 1627 Plotter XIO function %x", iocc_func);
255 			xio_error(msg);
256 	}
257     return;
258 }
259 
260 // plot_svc - 1627 plotter operation complete
261 
plot_svc(UNIT * uptr)262 static t_stat plot_svc (UNIT *uptr)
263 {
264 	CLRBIT(plot_dsw, PLOT1627_DSW_BUSY);			/* clear reader busy flag */
265 
266 	SETBIT(plot_dsw, PLOT1627_DSW_OP_COMPLETE);		/* indicate read complete */
267 
268 	SETBIT(ILSW[3], ILSW_3_1627_PLOTTER);			/* initiate interrupt */
269 	calc_ints();
270 
271 	return SCPE_OK;
272 }
273 
274 /* plot_reset - reset emulated plotter */
275 
plot_reset(DEVICE * dptr)276 static t_stat plot_reset (DEVICE *dptr)
277 {
278 	char * buf;
279 	int32 size;
280 
281 	sim_cancel(plot_unit);
282 
283 	CLRBIT(plot_dsw, PLOT1627_DSW_BUSY | PLOT1627_DSW_OP_COMPLETE);
284 
285     if (IS_DEBUG) printf("reset routine for Plotter\n");
286 
287 	CLRBIT(ILSW[3], ILSW_3_1627_PLOTTER);
288 	calc_ints();
289 
290 	return SCPE_OK;
291 }
292 
293 
294 /* plot_attach - attach file to simulated plotter */
295 
plot_attach(UNIT * uptr,char * cptr)296 static t_stat plot_attach (UNIT *uptr, char *cptr)
297 {
298 	t_stat result;
299 
300     CLRBIT(uptr->flags, UNIT_DEBUG);
301 	if (sim_switches & SWMASK('D')) SETBIT(uptr->flags, UNIT_DEBUG);
302 
303 	/* get the output file by using regular attach routine */
304     result = attach_unit(uptr, cptr);
305 
306     if (result != SCPE_OK) {
307        if (IS_DEBUG) printf("problem attaching file\n");
308        return result;
309     }
310 
311 	SETBIT(plot_dsw, PLOT1627_DSW_NOT_READY);				/* assume failure */
312 
313 	/* set up our canvas at the desired size */
314 	image = gdImageCreate(plot_ymax+1,plot_xmax+1);			/* create our canvas */
315     if (image == NULL) {
316        if (IS_DEBUG) printf("problem creating image canvas\n");
317        return SCPE_MEM;
318     }
319 
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 */
330 
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");
335            return SCPE_MEM;
336     }
337 
338 	CLRBIT(plot_dsw, PLOT1627_DSW_NOT_READY);				/* we're in business */
339 
340     update_pen();                                       	/* routine to ensure pen is okay */
341 
342 	return SCPE_OK;
343 }
344 
345 /* pen updating routine, called at attach and whenever we reset the values */
346 
update_pen(void)347 void update_pen (void)
348 {
349 	int color;
350 	int width;
351 
352      if (!IS_ONLINE(plot_unit)) return;     /* only do this if attached */
353 
354      /* pick up latest color as active pen */
355      color = GET_COLOR;
356      switch (color) {
357 	     case PEN_BLACK:
358 	          plot_pcolor = black_pen;
359 	          break;
360 
361 	     case PEN_RED:
362 	          plot_pcolor = red_pen;
363 	          break;
364 
365 	     case PEN_BLUE:
366 	          plot_pcolor = blue_pen;
367 	          break;
368 
369 	     case PEN_GREEN:
370 	          plot_pcolor = green_pen;
371 	          break;
372 
373 	     case PEN_YELLOW:
374 	          plot_pcolor = yellow_pen;
375 	          break;
376 
377 	     case PEN_PURPLE:
378 	          plot_pcolor = purple_pen;
379 	          break;
380 
381 	     case PEN_LTGREY:
382 	          plot_pcolor = ltgrey_pen;
383 	          break;
384 
385 	     case PEN_GREY:
386 	          plot_pcolor = grey_pen;
387 	          break;
388 
389 	     default:
390 	          if (IS_DEBUG) printf("invalid pen color state\n");
391 	          plot_pcolor = black_pen;
392 	          break;
393      }
394 
395      /* set up anti-aliasing for the line */
396      gdImageSetAntiAliased(image, plot_pcolor);
397 
398      /* pick up latest width for pen */
399      width = GET_WIDTH();
400      switch (width) {
401 	     case PEN_SINGLE:
402              plot_pwidth = 1;
403              gdImageSetThickness(image, 1);
404              break;
405 
406 	     case PEN_TRIPLE:
407              plot_pwidth = 3;
408              gdImageSetThickness(image, 3);
409              break;
410 
411 	     case PEN_DOUBLE:
412              plot_pwidth = 2;
413              gdImageSetThickness(image, 2);
414              break;
415 
416 	     case PEN_QUAD:
417              plot_pwidth = 4;
418              gdImageSetThickness(image, 4);
419              break;
420 
421 	     default:
422              if (IS_DEBUG) printf("invalid pen width\n");
423              plot_pwidth = 1;
424              gdImageSetThickness(image, 1);
425              break;
426      }
427 
428      /* now ensure the pen state is accurate */
429      plot_pen = IS_PENDOWN ? PEN_DOWN : PEN_UP;
430      return;
431 }
432 
433 /* plot_detach - detach file from simulated plotter */
plot_detach(UNIT * uptr)434 static t_stat plot_detach (UNIT *uptr)
435 {
436 	char * buf;
437 	int32 size;
438 	FILE * fp;
439 	int32 result;
440 
441     SETBIT(plot_dsw, PLOT1627_DSW_NOT_READY);
442 
443 	/* copy images to files, close files, set device to detached, free gd memory */
444 
445     buf = gdImageGifPtr(image,&size);
446     if (! buf) {
447        if (IS_DEBUG) printf("failure creating GIF in-memory\n");
448        return SCPE_MEM;
449     }
450 
451     fp = uptr->fileref;						/* get file attached to unit */
452 
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 */
456     }
457 
458     gdFree(buf);							/* free up the memory of GIF format */
459     gdImageDestroy(image);					/* free up the canvas memory */
460 
461     if (result != size) {					/* some problem writing it */
462        if (IS_DEBUG) printf("error in write of image file\n");
463        return SCPE_IOERR;
464     }
465 
466     return detach_unit(uptr);				/* have simh close the file */
467 }
468 
469 /* process_cmd - implement the drawing actions of the plotter */
470 
process_cmd(void)471 static void process_cmd (void)
472 {
473 	int32 oldx, oldy;
474 
475     /* first see if we set any changes to pen or position, do an update */
476     if (need_update) {
477        update_pen();
478        need_update = 0;
479     }
480 
481    	/* will move pen one step or flip pen up or down */
482     oldx = plot_xpos;
483     oldy = plot_ypos;
484 
485     switch (plot_cmd) {
486 	    case 1:            /* raise pen command */
487 	         plot_pen = PEN_UP;
488 	         plot_unit->flags = plot_unit->flags & (~UNIT_PEN);
489 	         return;
490 	         break;
491 
492 	    case 2:            /* +Y command */
493 	         plot_ypos = plot_ypos + 1;
494 	         break;
495 
496 	    case 4:            /* -Y command */
497 	         plot_ypos = plot_ypos - 1;
498 	         break;
499 
500 	    case 8:            /* -X command */
501 	         plot_xpos = plot_xpos - 1;
502 	         break;
503 
504 	    case 10:            /* -X +Y command */
505 	         plot_xpos = plot_xpos - 1;
506 	         plot_ypos = plot_ypos + 1;
507 	         break;
508 
509 	    case 12:            /* -X -Y command */
510 	         plot_xpos = plot_xpos - 1;
511 	         plot_ypos = plot_ypos - 1;
512 	         break;
513 
514 	    case 16:            /* +X command */
515 	         plot_xpos = plot_xpos + 1;
516 	         break;
517 
518 	    case 18:            /* +X +Y command */
519 	         plot_xpos = plot_xpos + 1;
520 	         plot_ypos = plot_ypos + 1;
521 	         break;
522 
523 	    case 20:            /* +X -Y pen command */
524 	         plot_xpos = plot_xpos + 1;
525 	         plot_ypos = plot_ypos - 1;
526 	         break;
527 
528 	    case 32:            /* lower pen command */
529 	         plot_pen = PEN_DOWN;
530 	         plot_unit->flags = plot_unit->flags | UNIT_PEN;
531 	         return;
532 	         break;
533 
534 	    default:
535 	         if (IS_DEBUG) printf("invalid plotter command\n");
536 	         return;
537 	         break;
538     }
539 
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);
547            return;
548     }
549 
550     /* only draw a line if the pen was down during the movement command */
551     if (plot_pen) {
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 */
554     }
555 
556 	return;
557 }
558 
559 /* plot_set_length - validate and store the length of the paper */
560 
plot_set_length(UNIT * uptr,int32 set,char * ptr,void * desc)561 static t_stat plot_set_length (UNIT *uptr, int32 set, char *ptr, void *desc)
562 {
563 	char *cptr;
564 	int32 val;
565 
566 #define LONGEST_ROLL 1440000                    /* longest is 120', 14400", 1,440,000 .01"s */
567 
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");
571 		return SCPE_ARG;
572 	}
573 
574 	/* origin zero drawing, reduce by 1 but show command will fudge by adding it back */
575 	*((int32 *)((REG *) desc)->loc) = val - 1;
576 
577 	return SCPE_OK;
578 }
579 
580 /* plot_set_pos - validate and store the new position of the carriage */
581 
plot_set_pos(UNIT * uptr,int32 set,char * ptr,void * desc)582 static t_stat plot_set_pos (UNIT *uptr, int32 set, char *ptr, void *desc)
583 {
584 	char *cptr;
585 	int32 val;
586 	int32 max;
587 
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");
592 			return SCPE_ARG;
593 	}
594 
595 	*((int32 *)((REG *) desc)->loc) = val;
596 
597 	return SCPE_OK;
598 }
599 
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
603  */
604 
plot_show_vals(FILE * fp,UNIT * uptr,int32 val,void * descrip)605 static t_stat plot_show_vals (FILE *fp, UNIT *uptr, int32 val, void *descrip)
606 {
607 	fprintf(fp, "length=%d, Xpos=%d, Ypos=%d",plot_xmax+1, plot_xpos,plot_ypos);
608     return SCPE_OK;
609 }
610 
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 */
613 
plot_show_nl(FILE * fp,UNIT * uptr,int32 val,void * descrip)614 static t_stat plot_show_nl(FILE *fp, UNIT *uptr, int32 val, void *descrip)
615 {
616 	int32 disp;
617 	char *label;
618 
619 	disp  = (val == 2) ? plot_xmax + 1 : ((val == 1) ? plot_ypos : plot_xpos);
620 	label = (val == 2) ? "length=" : ((val == 1) ? "Ypos=" : "Xpos=");
621 
622 	fprintf(fp, "%s%d\n", label, disp);
623     return SCPE_OK;
624 }
625 
626 /* plot_validate_change - force the update_pen routine to be called after user changes pen setting */
627 
plot_validate_change(UNIT * uptr,int32 set,char * ptr,void * desc)628 static t_stat plot_validate_change (UNIT *uptr, int32 set, char *ptr, void *desc)
629 {
630 	need_update = 1;
631 	return SCPE_OK;
632 }
633 
634 #endif /* ENABLE_PLOT_SUPPORT */
635