-
Notifications
You must be signed in to change notification settings - Fork 304
/
Copy pathaltairz80_sio.c
2040 lines (1841 loc) · 98 KB
/
altairz80_sio.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
/* altairz80_sio.c: MITS Altair serial I/O card
Copyright (c) 2002-2023, Peter Schorn
Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the "Software"),
to deal in the Software without restriction, including without limitation
the rights to use, copy, modify, merge, publish, distribute, sublicense,
and/or sell copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
PETER SCHORN BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
Except as contained in this notice, the name of Peter Schorn shall not
be used in advertising or otherwise to promote the sale, use or other dealings
in this Software without prior written authorization from Peter Schorn.
Based on work by Charles E Owen (c) 1997
These functions support a simulated MITS 2SIO interface card.
The card had two physical I/O ports which could be connected
to any serial I/O device that would connect to a current loop,
RS232, or TTY interface. Available baud rates were jumper
selectable for each port from 110 to 9600.
All I/O is via programmed I/O. Each device has a status port
and a data port. A write to the status port can select
some options for the device (0x03 will reset the port).
A read of the status port gets the port status:
+---+---+---+---+---+---+---+---+
| X | X | X | X | X | X | O | I |
+---+---+---+---+---+---+---+---+
I - A 1 in this bit position means a character has been received
on the data port and is ready to be read.
O - A 1 in this bit means the port is ready to receive a character
on the data port and transmit it out over the serial line.
A read to the data port gets the buffered character, a write
to the data port writes the character to the device.
*/
#include "altairz80_defs.h"
#include "sim_tmxr.h"
uint8 *URLContents(const char *URL, uint32 *length);
#ifndef URL_READER_SUPPORT
uint8 *URLContents(const char *URL, uint32 *length) {
*length = 0;
return (uint8*)NULL;
}
#endif
/* Debug flags */
#define IN_MSG (1 << 0)
#define OUT_MSG (1 << 1)
#define CMD_MSG (1 << 2)
#define VERBOSE_MSG (1 << 3)
#define BUFFER_EMPTY_MSG (1 << 4)
#define UNIT_V_SIO_ANSI (UNIT_V_UF + 0) /* ANSI mode, strip bit 8 on output */
#define UNIT_SIO_ANSI (1 << UNIT_V_SIO_ANSI)
#define UNIT_V_SIO_UPPER (UNIT_V_UF + 1) /* upper case mode */
#define UNIT_SIO_UPPER (1 << UNIT_V_SIO_UPPER)
#define UNIT_V_SIO_BS (UNIT_V_UF + 2) /* map delete to backspace */
#define UNIT_SIO_BS (1 << UNIT_V_SIO_BS)
#define UNIT_V_SIO_VERBOSE (UNIT_V_UF + 3) /* verbose mode, i.e. show error messages */
#define UNIT_SIO_VERBOSE (1 << UNIT_V_SIO_VERBOSE)
#define UNIT_V_SIO_MAP (UNIT_V_UF + 4) /* mapping mode on */
#define UNIT_SIO_MAP (1 << UNIT_V_SIO_MAP)
#define UNIT_V_SIO_BELL (UNIT_V_UF + 5) /* ^G (bell character) rings bell */
#define UNIT_SIO_BELL (1 << UNIT_V_SIO_BELL)
#define UNIT_V_SIO_INTERRUPT (UNIT_V_UF + 6) /* create keyboard interrupts */
#define UNIT_SIO_INTERRUPT (1 << UNIT_V_SIO_INTERRUPT)
#define UNIT_V_SIO_SLEEP (UNIT_V_UF + 7) /* sleep after keyboard status check */
#define UNIT_SIO_SLEEP (1 << UNIT_V_SIO_SLEEP)
#define UNIT_V_SIMH_TIMERON (UNIT_V_UF + 1) /* SIMH pseudo device timer generate interrupts */
#define UNIT_SIMH_TIMERON (1 << UNIT_V_SIMH_TIMERON)
#define TERMINALS 33 /* lines per mux (increased to 33 for IF3 board)*/
#define SIO_CAN_READ 0x01 /* bit 0 is set iff character available */
#define SIO_CAN_WRITE 0x02 /* bit 1 is set iff character can be sent */
#define SIO_RESET 0x03 /* Command to reset SIO */
#define VGSIO_CAN_READ 0x02 /* bit 1 is set iff character available */
#define VGSIO_CAN_WRITE 0x01 /* bit 0 is set iff character can be sent */
#define KBD_HAS_CHAR 0x40 /* bit 6 is set iff character available */
#define KBD_HAS_NO_CHAR 0x01 /* bit 0 is set iff no character is available */
#define BACKSPACE_CHAR 0x08 /* backspace character */
#define DELETE_CHAR 0x7f /* delete character */
#define CONTROLC_CHAR 0x03 /* control C character */
#define CONTROLG_CHAR 0x07 /* control G char., rings bell when displayed */
#define CONTROLZ_CHAR 0x1a /* control Z character */
#define PORT_TABLE_SIZE 256 /* size of port mapping table */
#define SLEEP_ALLOWED_START_DEFAULT 100 /* default initial value for sleepAllowedCounter*/
#define DEFAULT_TIMER_DELTA 100 /* default value for timer delta in ms */
#define CPM_COMMAND_LINE_LENGTH 128
#define CPM_FCB_ADDRESS 0x0080 /* Default FCB address for CP/M. */
static t_stat simh_dev_set_timeron (UNIT *uptr, int32 value, CONST char *cptr, void *desc);
static t_stat simh_dev_set_timeroff (UNIT *uptr, int32 value, CONST char *cptr, void *desc);
static t_stat sio_reset(DEVICE *dptr);
static t_stat sio_attach(UNIT *uptr, CONST char *cptr);
static t_stat sio_detach(UNIT *uptr);
static t_stat ptr_reset(DEVICE *dptr);
static t_stat ptp_reset(DEVICE *dptr);
static t_stat toBool(char tf, int32 *result);
static t_stat sio_dev_set_port(UNIT *uptr, int32 value, CONST char *cptr, void *desc);
static t_stat sio_dev_show_port(FILE *st, UNIT *uptr, int32 val, CONST void *desc);
static t_stat sio_dev_set_interrupton(UNIT *uptr, int32 value, CONST char *cptr, void *desc);
static t_stat sio_dev_set_interruptoff(UNIT *uptr, int32 value, CONST char *cptr, void *desc);
static t_stat sio_svc(UNIT *uptr);
static t_stat simh_dev_reset(DEVICE *dptr);
static t_stat simh_svc(UNIT *uptr);
static const char* sio_description(DEVICE *dptr);
static const char* simh_description(DEVICE *dptr);
static const char* ptr_description(DEVICE *dptr);
static const char* ptp_description(DEVICE *dptr);
static t_stat ptpptr_dev_set_port(UNIT *uptr, int32 value, CONST char *cptr, void *desc);
static t_stat ptpptr_dev_show_port(FILE *st, UNIT *uptr, int32 val, CONST void *desc);
static void mapAltairPorts(void);
int32 nulldev (const int32 port, const int32 io, const int32 data);
int32 sr_dev (const int32 port, const int32 io, const int32 data);
int32 simh_dev (const int32 port, const int32 io, const int32 data);
int32 sio0d (const int32 port, const int32 io, const int32 data);
int32 sio0s (const int32 port, const int32 io, const int32 data);
int32 sio1d (const int32 port, const int32 io, const int32 data);
int32 sio1s (const int32 port, const int32 io, const int32 data);
void do_SIMH_sleep(void);
static void pollConnection(void);
static int32 mapCharacter(int32 ch);
static void checkSleep(void);
static void voidSleep(void);
extern int32 getBankSelect(void);
extern void setBankSelect(const int32 b);
extern uint32 getCommon(void);
extern uint8 GetBYTEWrapper(const uint32 Addr);
extern uint32 sim_map_resource(uint32 baseaddr, uint32 size, uint32 resource_type,
int32 (*routine)(const int32, const int32, const int32), const char* name, uint8 unmap);
extern uint32 getClockFrequency(void);
extern void setClockFrequency(const uint32 Value);
extern uint32 PCX;
extern int32 SR;
extern int32 DS_S;
extern UNIT cpu_unit;
extern const char* handlerNameForPort(const int32 port);
extern uint32 vectorInterrupt; /* Interrupt Request */
extern uint8 dataBus[MAX_INT_VECTORS]; /* Data Bus Value */
/* Debug Flags */
static DEBTAB generic_dt[] = {
{ "IN", IN_MSG, "IN messages" },
{ "OUT", OUT_MSG, "OUT messages" },
{ "CMD", CMD_MSG, "Commands" },
{ "VERBOSE", VERBOSE_MSG, "Verbose messages" },
{ "BUFFEREMPTY", BUFFER_EMPTY_MSG, "IN for empty buffer" },
{ NULL, 0 }
};
/* SIMH pseudo device status registers */
/* ZSDOS clock definitions */
static int32 ClockZSDOSDelta = 0; /* delta between real clock and Altair clock */
static int32 setClockZSDOSPos = 0; /* determines state for receiving address of parameter block */
static int32 setClockZSDOSAdr = 0; /* address in M of 6 byte parameter block for setting time */
static int32 getClockZSDOSPos = 0; /* determines state for sending clock information */
/* CPM3 clock definitions */
static int32 ClockCPM3Delta = 0; /* delta between real clock and Altair clock */
static int32 setClockCPM3Pos = 0; /* determines state for receiving address of parameter block */
static int32 setClockCPM3Adr = 0; /* address in M of 5 byte parameter block for setting time */
static int32 getClockCPM3Pos = 0; /* determines state for sending clock information */
static int32 daysCPM3SinceOrg = 0; /* days since 1 Jan 1978 */
/* timer interrupt related */
static uint32 timeOfNextInterrupt; /* time when next interrupt is scheduled */
int32 timerInterrupt = FALSE; /* timer interrupt pending */
int32 timerInterruptHandler = 0x0fc00; /* default address of interrupt handling routine */
static int32 setTimerInterruptAdrPos= 0; /* determines state for receiving timerInterruptHandler */
static int32 timerDelta = DEFAULT_TIMER_DELTA; /* interrupt every 100 ms */
static int32 setTimerDeltaPos = 0; /* determines state for receiving timerDelta */
/* stop watch and timer related */
static uint32 stopWatchDelta = 0; /* stores elapsed time of stop watch */
static int32 getStopWatchDeltaPos = 0; /* determines the state for receiving stopWatchDelta */
static uint32 stopWatchNow = 0; /* stores starting time of stop watch */
static int32 markTimeSP = 0; /* stack pointer for timer stack */
uint32 SIMHSleep = 1; /* default time in milliseconds to sleep for SIMHSleepCmd is 1 */
static uint32 sleepAllowedCounter = 0; /* only sleep on no character available when == 0 */
static uint32 sleepAllowedStart = SLEEP_ALLOWED_START_DEFAULT; /* default start for above counter */
/* miscellaneous */
static int32 versionPos = 0; /* determines state for sending device identifier */
static int32 lastCPMStatus = 0; /* result of last attachCPM command */
static int32 lastCommand = 0; /* most recent command processed on port 0xfeh */
static int32 getCommonPos = 0; /* determines state for sending the 'common' register */
static int32 genInterruptPos = 0; /* determines state for receiving interrupt vector and data */
static int32 genInterruptVec = 0; /* stores interrupt vector */
/* CPU Clock Frequency related */
static uint32 newClockFrequency;
static int32 setClockFrequencyPos = 0; /* determines state for sending the clock frequency */
static int32 getClockFrequencyPos = 0; /* determines state for receiving the clock frequency */
/* Set FCB Address (needed for MS-DOS READ and WRITE commands. */
static int32 setFCBAddressPos = 0; /* determines state for setting the FCB address */
static int32 FCBAddress = CPM_FCB_ADDRESS; /* FCB Address */
/* support for wild card file expansion */
#if defined (_WIN32)
const static char hostPathSeparator = '\\'; /* back slash in Windows */
const static char hostPathSeparatorAlt = '/'; /* '/' is an alternative */
#else
const static char hostPathSeparator = '/'; /* slash in UNIX */
const static char hostPathSeparatorAlt = '/'; /* no alternative */
#endif
typedef struct NameNode {
char *name;
struct NameNode *next;
} NameNode_t;
static char cpmCommandLine[CPM_COMMAND_LINE_LENGTH];
static NameNode_t *nameListHead = NULL;
static NameNode_t *currentName = NULL;
static int32 currentNameIndex = 0;
static int32 lastPathSeparatorIndex = 0;
static int32 firstPathCharacterIndex = 0;
static void deleteNameList(void) {
while (nameListHead != NULL) {
NameNode_t *next = nameListHead -> next;
free(nameListHead -> name);
free(nameListHead);
nameListHead = next;
}
currentName = NULL;
currentNameIndex = 0;
}
static void processDirEntry (const char *directory,
const char *filename,
t_offset FileSize,
const struct stat *filestat,
void *context) {
if (filename != NULL) {
NameNode_t *top = (NameNode_t *)malloc(sizeof(NameNode_t));
if (top) {
top->name = strdup(filename);
top->next = nameListHead;
nameListHead = top;
}
}
}
/* SIO status registers */
static int32 warnLevelSIO = 3; /* display at most 'warnLevelSIO' times the same warning */
static int32 warnUnattachedPTP = 0; /* display a warning message if < warnLevel and SIO set to
VERBOSE and output to PTP without an attached file */
static int32 warnUnattachedPTR = 0; /* display a warning message if < warnLevel and SIO set to
VERBOSE and attempt to read from PTR without an attached file */
static int32 warnPTREOF = 0; /* display a warning message if < warnLevel and SIO set to
VERBOSE and attempt to read from PTR past EOF */
static int32 warnUnassignedPort = 0; /* display a warning message if < warnLevel and SIO set to
VERBOSE and attempt to perform IN or OUT on an unassigned PORT */
int32 keyboardInterrupt = FALSE; /* keyboard interrupt pending */
uint32 keyboardInterruptHandler = 0x0038;/* address of keyboard interrupt handler */
/* PTR/PTP port assignments (read only) */
static int32 ptpptrStatusPort = 0x12; /* default status port for PTP/PTR device */
static int32 ptpptrDataPort = 0x13; /* default data port for PTP/PTR device */
int32 kbdIrqPort = 0; /* Keyboard Interrupt port number. */
static TMLN TerminalLines[TERMINALS] = { /* four terminals */
{ 0 }
};
static TMXR altairTMXR = { /* mux descriptor */
TERMINALS, 0, 0, TerminalLines
};
static UNIT sio_unit = {
UDATA (&sio_svc, UNIT_ATTABLE | UNIT_SIO_MAP | UNIT_SIO_SLEEP, 0),
100000, /* wait */
FALSE, /* u3 = FALSE, no character available in buffer */
FALSE, /* u4 = FALSE, terminal input is not attached to a file */
0, /* u5 = 0, not used */
0 /* u6 = 0, not used */
};
static REG sio_reg[] = {
{ DRDATAD (SIOWLEV, warnLevelSIO, 32,
"Warn level SIO register") },
{ DRDATAD (WRNUPTP, warnUnattachedPTP, 32,
"Counter for unattached PTP access") },
{ DRDATAD (WRNUPTR, warnUnattachedPTR, 32,
"Counter for unattached PTR access") },
{ DRDATAD (WRNPTRE, warnPTREOF, 32,
"Counter for EOF reached for PTR") },
{ DRDATAD (WRUPORT, warnUnassignedPort, 32,
"Counter for unassigned port") },
{ HRDATAD (FILEATT, sio_unit.u4, 8,
"BOOL to determine whether terminal input is attached to a file"), REG_RO },
/* TRUE iff terminal input is attached to a file */
{ HRDATAD (TSTATUS, sio_unit.u3, 8,
"BOOL to determine whether a character is available") },
/* TRUE iff a character available in sio_unit.buf */
{ DRDATAD (TBUFFER, sio_unit.buf, 8,
"Input buffer register") },
/* input buffer for one character */
{ DRDATAD (KEYBDI, keyboardInterrupt, 3,
"BOOL to determine whether a keyboard interrupt is pending"), REG_RO },
{ HRDATAD (KEYBDH, keyboardInterruptHandler, 16,
"Address of keyboard interrupt handler") },
{ HRDATAD(KBDIRQPORT, kbdIrqPort, 8,
"Port number of keyboardInterrupt SIO status register."), },
{ NULL }
};
static MTAB sio_mod[] = {
{ UNIT_SIO_ANSI, 0, "TTY", "TTY", NULL, NULL, NULL,
"Do not touch bit 8 of console output"}, /* keep bit 8 as is for output */
{ UNIT_SIO_ANSI, UNIT_SIO_ANSI, "ANSI", "ANSI", NULL, NULL, NULL,
"Set bit 8 of console output to 0"}, /* set bit 8 to 0 before output */
{ UNIT_SIO_UPPER, 0, "ALL", "ALL", NULL, NULL, NULL,
"Console input remains unchanged" }, /* do not change case of input characters */
{ UNIT_SIO_UPPER, UNIT_SIO_UPPER, "UPPER", "UPPER", NULL, NULL, NULL,
"Convert console input to upper case" }, /* change input characters to upper case */
{ UNIT_SIO_BS, 0, "BS", "BS", NULL, NULL, NULL,
"Map delete to backspace" }, /* map delete to backspace */
{ UNIT_SIO_BS, UNIT_SIO_BS, "DEL", "DEL", NULL, NULL, NULL,
"Map backspace to delete" }, /* map backspace to delete */
{ UNIT_SIO_VERBOSE, 0, "QUIET", "QUIET", NULL, NULL, NULL,
"Do not display SIO error messages" }, /* quiet, no error messages */
{ UNIT_SIO_VERBOSE, UNIT_SIO_VERBOSE, "VERBOSE", "VERBOSE", NULL, NULL, NULL,
"Display verbose messages" }, /* verbose, display warning messages */
{ UNIT_SIO_MAP, 0, "NOMAP", "NOMAP", NULL, NULL, NULL,
"Do not map any character" }, /* disable character mapping */
{ UNIT_SIO_MAP, UNIT_SIO_MAP, "MAP", "MAP", NULL, NULL, NULL,
"Enable mapping of characters" }, /* enable all character mapping */
{ UNIT_SIO_BELL, 0, "BELL", "BELL", NULL, NULL, NULL,
"Control-G sounds the bell" }, /* enable bell character */
{ UNIT_SIO_BELL, UNIT_SIO_BELL, "NOBELL", "NOBELL", NULL, NULL, NULL,
"The bell sound is suppressed" }, /* suppress ringing the bell */
{ UNIT_SIO_SLEEP, 0, "NOSLEEP", "NOSLEEP", NULL, NULL, NULL,
"Do not sleep after SIO status checks" }, /* no sleep after keyboard status check */
{ UNIT_SIO_SLEEP, UNIT_SIO_SLEEP, "SLEEP", "SLEEP", NULL, NULL, NULL,
"Sleep after SIO status checks" }, /* sleep after keyboard status check */
/* no keyboard interrupts */
{ UNIT_SIO_INTERRUPT, 0, "NOINTERRUPT", "NOINTERRUPT",
&sio_dev_set_interruptoff, NULL, NULL, "Status port 0 does not create interrupts" },
/* create keyboard interrupts */
{ UNIT_SIO_INTERRUPT, UNIT_SIO_INTERRUPT, "INTERRUPT", "INTERRUPT",
&sio_dev_set_interrupton, NULL, NULL,
"Status port 0 creates an interrupt when a character becomes available" },
{ MTAB_XTD|MTAB_VDV, 0, "PORT", "PORT",
&sio_dev_set_port, &sio_dev_show_port, NULL,
"Set port to Port/Terminal/Read/NotRead/Write/Reset/Reset/Data" },
{ 0 }
};
static const char* sio_description(DEVICE *dptr) {
return "Serial Input Output";
}
DEVICE sio_dev = {
"SIO", &sio_unit, sio_reg, sio_mod,
1, 10, 31, 1, 8, 8,
NULL, NULL, &sio_reset,
NULL, &sio_attach, &sio_detach,
NULL, DEV_DEBUG | DEV_MUX, 0,
generic_dt, NULL, NULL, NULL, NULL, NULL, &sio_description
};
static MTAB ptpptr_mod[] = {
{ MTAB_XTD|MTAB_VDV, 0, "PORT", "PORT",
&ptpptr_dev_set_port, &ptpptr_dev_show_port, NULL,
"Set status and data port for PTP/PTR device" },
{ 0 }
};
static UNIT ptr_unit = {
UDATA (NULL, UNIT_ATTABLE | UNIT_ROABLE, 0)
};
static REG ptr_reg[] = {
{ HRDATAD (PTRSTATUSPORT, ptpptrStatusPort, 8, "PTR status port (shared with PTP)"), REG_RO },
{ HRDATAD (PTRDATAPORT, ptpptrDataPort, 8, "PTR data port (shared with PTP)"), REG_RO },
{ HRDATAD (PTRSTATUS, ptr_unit.u3, 8, "PTR Status register") },
{ NULL }
};
static const char* ptr_description(DEVICE *dptr) {
return "Paper Tape Reader";
}
DEVICE ptr_dev = {
"PTR", &ptr_unit, ptr_reg, ptpptr_mod,
1, 10, 31, 1, 8, 8,
NULL, NULL, &ptr_reset,
NULL, NULL, NULL,
NULL, (DEV_DISABLE | DEV_DEBUG), 0,
generic_dt, NULL, NULL, NULL, NULL, NULL, &ptr_description
};
static UNIT ptp_unit = {
UDATA (NULL, UNIT_ATTABLE, 0)
};
static REG ptp_reg[] = {
{ HRDATAD (PTPSTATUSPORT, ptpptrStatusPort, 8, "PTP status port (shared with PTR)"), REG_RO },
{ HRDATAD (PTPDATAPORT, ptpptrDataPort, 8, "PTP data port (shared with PTR)"), REG_RO },
{ NULL }
};
static const char* ptp_description(DEVICE *dptr) {
return "Paper Tape Puncher";
}
DEVICE ptp_dev = {
"PTP", &ptp_unit, ptp_reg, ptpptr_mod,
1, 10, 31, 1, 8, 8,
NULL, NULL, &ptp_reset,
NULL, NULL, NULL,
NULL, (DEV_DISABLE | DEV_DEBUG), 0,
generic_dt, NULL, NULL, NULL, NULL, NULL, &ptp_description
};
/* Synthetic device SIMH for communication
between Altair and SIMH environment using port 0xfe */
static UNIT simh_unit = {
UDATA (&simh_svc, 0, 0), KBD_POLL_WAIT
};
static REG simh_reg[] = {
{ DRDATAD (CZD, ClockZSDOSDelta, 32,
"ZSDOS Clock - Delta between real clock and AltairZ80 clock") },
{ DRDATAD (SCZP, setClockZSDOSPos, 8,
"ZSDOS Clock - Status register for receiving address of parameter block"), REG_RO },
{ HRDATAD (SCZA, setClockZSDOSAdr, 16,
"ZSDOS Clock - Address of 6 byte parameter block for setting time"), REG_RO },
{ DRDATAD (GCZP, getClockZSDOSPos, 8,
"ZSDOS Clock - Status register for sending clock information"), REG_RO },
{ DRDATAD (CC3D, ClockCPM3Delta, 32,
"CP/M 3 Clock - Delta between real clock and AltairZ80 clock") },
{ DRDATAD (SC3DP, setClockCPM3Pos, 8,
"CP/M 3 Clock - Status register for receiving address of parameter block"), REG_RO },
{ HRDATAD (SC3DA, setClockCPM3Adr, 16,
"CP/M 3 Clock - Address of 5 byte parameter block for setting time"), REG_RO },
{ DRDATAD (GC3DP, getClockCPM3Pos, 8,
"CP/M 3 Clock - Status register for sending clock information"), REG_RO },
{ DRDATAD (D3DO, daysCPM3SinceOrg, 32,
"CP/M 3 Clock - Days since 1-Jan-1978"), REG_RO },
{ DRDATAD (TOFNI, timeOfNextInterrupt, 32,
"Time when next interrupt is scheduled"), REG_RO },
{ DRDATAD (TIMI, timerInterrupt, 3,
"BOOL - determines whether a timer interrupt is pending") },
{ HRDATAD (TIMH, timerInterruptHandler, 16,
"Address of timer interrupt handling routine") },
{ DRDATAD (STIAP, setTimerInterruptAdrPos,8,
"Status register for receiving address of timer interrupt handler"), REG_RO },
{ DRDATAD (TIMD, timerDelta, 32,
"Time in milliseconds between timer interrupts") },
{ DRDATAD (STDP, setTimerDeltaPos, 8,
"Status register for receiving the timer delta"), REG_RO },
{ DRDATAD (SLEEP, SIMHSleep, 32,
"Sleep time in milliseconds after SIO status check (when enabled)") },
{ DRDATAD (VOSLP, sleepAllowedStart, 32,
"Only sleep when this many unsuccessful SIO status checks have been made") },
{ DRDATAD (STPDT, stopWatchDelta, 32,
"Elapsed time of stop watch"), REG_RO },
{ DRDATAD (STPOS, getStopWatchDeltaPos, 8,
"Status register for receiving stop watch delta"), REG_RO },
{ DRDATAD (STPNW, stopWatchNow, 32,
"Starting time of stop watch"), REG_RO },
{ DRDATAD (MTSP, markTimeSP, 8,
"Stack pointer of timer stack"), REG_RO },
{ DRDATAD (VPOS, versionPos, 8,
"Status register for sending version information"), REG_RO },
{ DRDATAD (LCPMS, lastCPMStatus, 8,
"Result of last attachCPM command"), REG_RO },
{ DRDATAD (LCMD, lastCommand, 8,
"Last command processed on SIMH port"), REG_RO },
{ DRDATAD (CPOS, getCommonPos, 8,
"Status register for sending the COMMON register"), REG_RO },
{ HRDATAD (FCBA, FCBAddress, 16,
"Address of the FCB for file operations") },
{ DRDATAD (FCBAP, setFCBAddressPos,8,
"Status register for receiving address of the FCB"), REG_RO },
{ NULL }
};
static MTAB simh_mod[] = {
/* timer generated interrupts are off */
{ UNIT_SIMH_TIMERON, 0, "TIMEROFF", "TIMEROFF", &simh_dev_set_timeroff,
NULL, NULL, "Stop periodic timer interrupts" },
/* timer generated interrupts are on */
{ UNIT_SIMH_TIMERON, UNIT_SIMH_TIMERON, "TIMERON", "TIMERON", &simh_dev_set_timeron,
NULL, NULL, "Start periodic timer interrupts" },
{ 0 }
};
const char* simh_description(DEVICE *dptr) {
return "Pseudo Device";
}
DEVICE simh_device = {
"SIMH", &simh_unit, simh_reg, simh_mod,
1, 10, 31, 1, 16, 4,
NULL, NULL, &simh_dev_reset,
NULL, NULL, NULL,
NULL, (DEV_DISABLE | DEV_DEBUG), 0,
generic_dt, NULL, NULL, NULL, NULL, NULL, &simh_description
};
static void resetSIOWarningFlags(void) {
warnUnattachedPTP = warnUnattachedPTR = warnPTREOF = warnUnassignedPort = 0;
}
static t_stat sio_attach(UNIT *uptr, CONST char *cptr) {
t_stat r = SCPE_IERR;
sio_unit.u3 = FALSE; /* no character in terminal input buffer */
get_uint(cptr, 10, 65535, &r); /* attempt to get port, discard result */
if (r == SCPE_OK) { /* string can be interpreted as port number */
sio_unit.u4 = FALSE; /* terminal input is not attached to a file */
return tmxr_attach(&altairTMXR, uptr, cptr); /* attach mux */
}
sio_unit.u4 = TRUE; /* terminal input is attached to a file */
return attach_unit(uptr, cptr);
}
static t_stat sio_detach(UNIT *uptr) {
sio_unit.u3 = FALSE; /* no character in terminal input buffer */
if (sio_unit.u4) { /* is terminal input attached to a file? */
sio_unit.u4 = FALSE; /* not anymore, detach */
return detach_unit(uptr);
}
return tmxr_detach(&altairTMXR, uptr);
}
static void pollConnection(void) {
if (sio_unit.flags & UNIT_ATT) {
int32 temp = tmxr_poll_conn(&altairTMXR); /* poll connection */
if (temp >= 0)
TerminalLines[temp].rcve = 1; /* enable receive */
tmxr_poll_rx(&altairTMXR); /* poll input */
tmxr_poll_tx(&altairTMXR); /* poll output */
}
}
/* reset routines */
static t_stat sio_reset(DEVICE *dptr) {
int32 i;
sim_debug(VERBOSE_MSG, &sio_dev, "SIO: " ADDRESS_FORMAT " Reset\n", PCX);
sio_unit.u3 = FALSE; /* no character in terminal input buffer */
sio_unit.buf = 0;
resetSIOWarningFlags();
if (sio_unit.u4) /* is terminal input attached to a file? */
rewind(sio_unit.fileref); /* yes, rewind input */
else if (sio_unit.flags & UNIT_ATT)
for (i = 0; i < TERMINALS; i++)
if (TerminalLines[i].conn)
tmxr_reset_ln(&TerminalLines[i]);
mapAltairPorts();
if (sio_unit.flags & UNIT_SIO_INTERRUPT)
sim_activate(&sio_unit, sio_unit.wait); /* activate unit */
return SCPE_OK;
}
static t_stat ptr_reset(DEVICE *dptr) {
sim_debug(VERBOSE_MSG, &ptr_dev, "PTR: " ADDRESS_FORMAT " Reset\n", PCX);
resetSIOWarningFlags();
ptr_unit.u3 = FALSE; /* End Of File not yet reached */
ptr_unit.buf = 0;
if (ptr_unit.flags & UNIT_ATT) /* attached? */
rewind(ptr_unit.fileref);
sim_map_resource(0x12, 1, RESOURCE_TYPE_IO, &sio1s, "sio1s", dptr->flags & DEV_DIS);
sim_map_resource(0x13, 1, RESOURCE_TYPE_IO, &sio1d, "sio1d", dptr->flags & DEV_DIS);
return SCPE_OK;
}
static t_stat ptp_reset(DEVICE *dptr) {
sim_debug(VERBOSE_MSG, &ptp_dev, "PTP: " ADDRESS_FORMAT " Reset\n", PCX);
resetSIOWarningFlags();
sim_map_resource(0x12, 1, RESOURCE_TYPE_IO, &sio1s, "sio1s", dptr->flags & DEV_DIS);
sim_map_resource(0x13, 1, RESOURCE_TYPE_IO, &sio1d, "sio1d", dptr->flags & DEV_DIS);
return SCPE_OK;
}
static int32 mapCharacter(int32 ch) {
ch &= 0xff;
if (sio_unit.flags & UNIT_SIO_MAP) {
if (sio_unit.flags & UNIT_SIO_BS) {
if (ch == BACKSPACE_CHAR)
return DELETE_CHAR;
} else if (ch == DELETE_CHAR)
return BACKSPACE_CHAR;
if (sio_unit.flags & UNIT_SIO_UPPER)
return toupper(ch);
}
return ch;
}
/* I/O instruction handlers, called from the CPU module when an
IN or OUT instruction is issued.
Each function is passed an 'io' flag, where 0 means a read from
the port, and 1 means a write to the port. On input, the actual
input is passed as the return value, on output, 'data' is written
to the device.
Port 1 controls console I/O. We distinguish three cases:
1) SIO attached to a file (i.e. input taken from a file )
2) SIO attached to a port (i.e. Telnet console I/O )
3) SIO not attached to a port (i.e. "regular" console I/O )
*/
typedef struct {
int32 port; /* this information belongs to port number 'port' */
int32 terminalLine; /* map to this 'terminalLine' */
int32 sio_can_read; /* bit mask to indicate that one can read from this port */
int32 sio_cannot_read; /* bit mask to indicate that one cannot read from this port */
int32 sio_can_write; /* bit mask to indicate that one can write to this port */
int32 hasReset; /* TRUE iff SIO has reset command */
int32 sio_reset; /* reset command */
int32 hasOUT; /* TRUE iff port supports OUT command */
int32 isBuiltin; /* TRUE iff mapping is built in */
} SIO_PORT_INFO;
static SIO_PORT_INFO port_table[PORT_TABLE_SIZE] = {
{0x00, 0, KBD_HAS_CHAR, KBD_HAS_NO_CHAR, SIO_CAN_WRITE, FALSE, 0, FALSE, TRUE },
{0x01, 0, 0, 0, 0, FALSE, 0, FALSE, TRUE },
{0x02, 0, VGSIO_CAN_READ, 0, VGSIO_CAN_WRITE, FALSE, 0, TRUE, TRUE },
{0x03, 0, VGSIO_CAN_READ, 0, VGSIO_CAN_WRITE, FALSE, 0, FALSE, TRUE },
{0x04, 0, VGSIO_CAN_READ, 0, VGSIO_CAN_WRITE, FALSE, 0, TRUE, TRUE },
{0x05, 0, VGSIO_CAN_READ, 0, VGSIO_CAN_WRITE, FALSE, 0, FALSE, TRUE },
{0x10, 0, SIO_CAN_READ, 0, SIO_CAN_WRITE, TRUE, SIO_RESET, FALSE, TRUE },
{0x11, 0, SIO_CAN_READ, 0, SIO_CAN_WRITE, TRUE, SIO_RESET, TRUE, TRUE },
{0x14, 1, SIO_CAN_READ, 0, SIO_CAN_WRITE, TRUE, SIO_RESET, FALSE, TRUE },
{0x15, 1, SIO_CAN_READ, 0, SIO_CAN_WRITE, TRUE, SIO_RESET, TRUE, TRUE },
{0x16, 2, SIO_CAN_READ, 0, SIO_CAN_WRITE, TRUE, SIO_RESET, FALSE, TRUE },
{0x17, 2, SIO_CAN_READ, 0, SIO_CAN_WRITE, TRUE, SIO_RESET, TRUE, TRUE },
{0x18, 3, SIO_CAN_READ, 0, SIO_CAN_WRITE, TRUE, SIO_RESET, FALSE, TRUE },
{0x19, 3, SIO_CAN_READ, 0, SIO_CAN_WRITE, TRUE, SIO_RESET, TRUE, TRUE },
/* CompuPro System Support 1 Board */
{0x5c, 0, 0x0, 0, 0, FALSE,0, TRUE, TRUE },
{0x5d, 0, 0xC2, 0, 0xC5, FALSE,0, FALSE, TRUE },
/* CompuPro Interfacer 3 (IF3) Board 0 */
{0x300, 1, 0x0, 0, 0, TRUE, SIO_RESET, TRUE, TRUE },
{0x301, 1, 0xC2, 0, 0xC5, TRUE, SIO_RESET, FALSE, TRUE },
{0x302, 2, 0x0, 0, 0, TRUE, SIO_RESET, TRUE, TRUE },
{0x303, 2, 0xC2, 0, 0xC5, TRUE, SIO_RESET, FALSE, TRUE },
{0x304, 3, 0x0, 0, 0, TRUE, SIO_RESET, TRUE, TRUE },
{0x305, 3, 0xC2, 0, 0xC5, TRUE, SIO_RESET, FALSE, TRUE },
{0x306, 4, 0x0, 0, 0, TRUE, SIO_RESET, TRUE, TRUE },
{0x307, 4, 0xC2, 0, 0xC5, TRUE, SIO_RESET, FALSE, TRUE },
{0x308, 5, 0x0, 0, 0, TRUE, SIO_RESET, TRUE, TRUE },
{0x309, 5, 0xC2, 0, 0xC5, TRUE, SIO_RESET, FALSE, TRUE },
{0x30a, 6, 0x0, 0, 0, TRUE, SIO_RESET, TRUE, TRUE },
{0x30b, 6, 0xC2, 0, 0xC5, TRUE, SIO_RESET, FALSE, TRUE },
{0x30c, 7, 0x0, 0, 0, TRUE, SIO_RESET, TRUE, TRUE },
{0x30d, 7, 0xC2, 0, 0xC5, TRUE, SIO_RESET, FALSE, TRUE },
{0x30e, 8, 0x0, 0, 0, TRUE, SIO_RESET, TRUE, TRUE },
{0x30f, 8, 0xC2, 0, 0xC5, TRUE, SIO_RESET, FALSE, TRUE },
/* CompuPro Interfacer 3 (IF3) Board 1 */
{0x310, 9, 0x0, 0, 0, TRUE, SIO_RESET, TRUE, TRUE },
{0x311, 9, 0xC2, 0, 0xC5, TRUE, SIO_RESET, FALSE, TRUE },
{0x312, 10, 0x0, 0, 0, TRUE, SIO_RESET, TRUE, TRUE },
{0x313, 10, 0xC2, 0, 0xC5, TRUE, SIO_RESET, FALSE, TRUE },
{0x314, 11, 0x0, 0, 0, TRUE, SIO_RESET, TRUE, TRUE },
{0x315, 11, 0xC2, 0, 0xC5, TRUE, SIO_RESET, FALSE, TRUE },
{0x316, 12, 0x0, 0, 0, TRUE, SIO_RESET, TRUE, TRUE },
{0x317, 12, 0xC2, 0, 0xC5, TRUE, SIO_RESET, FALSE, TRUE },
{0x318, 13, 0x0, 0, 0, TRUE, SIO_RESET, TRUE, TRUE },
{0x319, 13, 0xC2, 0, 0xC5, TRUE, SIO_RESET, FALSE, TRUE },
{0x31a, 14, 0x0, 0, 0, TRUE, SIO_RESET, TRUE, TRUE },
{0x31b, 14, 0xC2, 0, 0xC5, TRUE, SIO_RESET, FALSE, TRUE },
{0x31c, 15, 0x0, 0, 0, TRUE, SIO_RESET, TRUE, TRUE },
{0x31d, 15, 0xC2, 0, 0xC5, TRUE, SIO_RESET, FALSE, TRUE },
{0x31e, 16, 0x0, 0, 0, TRUE, SIO_RESET, TRUE, TRUE },
{0x31f, 16, 0xC2, 0, 0xC5, TRUE, SIO_RESET, FALSE, TRUE },
/* CompuPro Interfacer 3 (IF3) Board 2 */
{0x320, 17, 0x0, 0, 0, TRUE, SIO_RESET, TRUE, TRUE },
{0x321, 17, 0xC2, 0, 0xC5, TRUE, SIO_RESET, FALSE, TRUE },
{0x322, 18, 0x0, 0, 0, TRUE, SIO_RESET, TRUE, TRUE },
{0x323, 18, 0xC2, 0, 0xC5, TRUE, SIO_RESET, FALSE, TRUE },
{0x324, 19, 0x0, 0, 0, TRUE, SIO_RESET, TRUE, TRUE },
{0x325, 19, 0xC2, 0, 0xC5, TRUE, SIO_RESET, FALSE, TRUE },
{0x326, 20, 0x0, 0, 0, TRUE, SIO_RESET, TRUE, TRUE },
{0x327, 20, 0xC2, 0, 0xC5, TRUE, SIO_RESET, FALSE, TRUE },
{0x328, 21, 0x0, 0, 0, TRUE, SIO_RESET, TRUE, TRUE },
{0x329, 21, 0xC2, 0, 0xC5, TRUE, SIO_RESET, FALSE, TRUE },
{0x32a, 22, 0x0, 0, 0, TRUE, SIO_RESET, TRUE, TRUE },
{0x32b, 22, 0xC2, 0, 0xC5, TRUE, SIO_RESET, FALSE, TRUE },
{0x32c, 23, 0x0, 0, 0, TRUE, SIO_RESET, TRUE, TRUE },
{0x32d, 23, 0xC2, 0, 0xC5, TRUE, SIO_RESET, FALSE, TRUE },
{0x32e, 24, 0x0, 0, 0, TRUE, SIO_RESET, TRUE, TRUE },
{0x32f, 24, 0xC2, 0, 0xC5, TRUE, SIO_RESET, FALSE, TRUE },
/* CompuPro Interfacer 3 (IF3) Board 3 */
{0x330, 25, 0x0, 0, 0, TRUE, SIO_RESET, TRUE, TRUE },
{0x331, 25, 0xC2, 0, 0xC5, TRUE, SIO_RESET, FALSE, TRUE },
{0x332, 26, 0x0, 0, 0, TRUE, SIO_RESET, TRUE, TRUE },
{0x333, 26, 0xC2, 0, 0xC5, TRUE, SIO_RESET, FALSE, TRUE },
{0x334, 27, 0x0, 0, 0, TRUE, SIO_RESET, TRUE, TRUE },
{0x335, 27, 0xC2, 0, 0xC5, TRUE, SIO_RESET, FALSE, TRUE },
{0x336, 28, 0x0, 0, 0, TRUE, SIO_RESET, TRUE, TRUE },
{0x337, 28, 0xC2, 0, 0xC5, TRUE, SIO_RESET, FALSE, TRUE },
{0x338, 29, 0x0, 0, 0, TRUE, SIO_RESET, TRUE, TRUE },
{0x339, 29, 0xC2, 0, 0xC5, TRUE, SIO_RESET, FALSE, TRUE },
{0x33a, 30, 0x0, 0, 0, TRUE, SIO_RESET, TRUE, TRUE },
{0x33b, 30, 0xC2, 0, 0xC5, TRUE, SIO_RESET, FALSE, TRUE },
{0x33c, 31, 0x0, 0, 0, TRUE, SIO_RESET, TRUE, TRUE },
{0x33d, 31, 0xC2, 0, 0xC5, TRUE, SIO_RESET, FALSE, TRUE },
{0x33e, 32, 0x0, 0, 0, TRUE, SIO_RESET, TRUE, TRUE },
{0x33f, 32, 0xC2, 0, 0xC5, TRUE, SIO_RESET, FALSE, TRUE },
{-1, 0, 0, 0, 0, 0, 0, 0, 0} /* must be last */
};
static SIO_PORT_INFO lookupPortInfo(const int32 port, int32 *position) {
int32 i = 0;
while ((port_table[i].port != -1) && (port_table[i].port != port))
i++;
*position = i;
return port_table[i];
}
/* keyboard idle detection: sleep when feature enabled, no character available
(duty of caller) and operation not voided (e.g. when output is available) */
static void checkSleep(void) {
if (sio_unit.flags & UNIT_SIO_SLEEP) {
if (sleepAllowedCounter)
sleepAllowedCounter--;
else
do_SIMH_sleep();
}
}
/* void sleep for next 'sleepAllowedStart' tests */
static void voidSleep(void) {
sleepAllowedCounter = sleepAllowedStart;
}
/* generic status port for keyboard input / terminal output */
static int32 sio0sCore(const int32 port, const int32 io, const int32 data) {
int32 ch, result;
const SIO_PORT_INFO spi = lookupPortInfo(port, &ch);
ASSURE(spi.port == port);
pollConnection();
if (io == 0) { /* IN */
if (sio_unit.u4) { /* attached to a file? */
if (sio_unit.u3) /* character available? */
return spi.sio_can_read | spi.sio_can_write;
ch = getc(sio_unit.fileref);
if (ch == EOF) {
sio_detach(&sio_unit); /* detach file and switch to keyboard input */
return spi.sio_cannot_read | spi.sio_can_write;
} else {
sio_unit.u3 = TRUE; /* indicate character available */
sio_unit.buf = ch; /* store character in buffer */
return spi.sio_can_read | spi.sio_can_write;
}
}
if (sio_unit.flags & UNIT_ATT) { /* attached to a port? */
if (tmxr_rqln(&TerminalLines[spi.terminalLine]))
result = spi.sio_can_read;
else {
result = spi.sio_cannot_read;
if (!sim_signaled_int_char)
sim_poll_kbd(); /* check for WRU when signaling is not available */
checkSleep();
}
return result | /* read possible if character available */
(TerminalLines[spi.terminalLine].conn && TerminalLines[spi.terminalLine].xmte ? spi.sio_can_write : 0x00);
/* write possible if connected and transmit
enabled */
}
if (sio_unit.u3) /* character available? */
return spi.sio_can_read | spi.sio_can_write;
ch = sim_poll_kbd(); /* no, try to get a character */
if ((ch == SCPE_OK) && stop_cpu) {
sim_interval = 0; /* detect stop condition as soon as possible*/
return spi.sio_cannot_read | spi.sio_can_write; /* do not consume stop character */
}
if (ch) { /* character available? */
sio_unit.u3 = TRUE; /* indicate character available */
sio_unit.buf = ch; /* store character in buffer */
return spi.sio_can_read | spi.sio_can_write;
}
checkSleep();
return spi.sio_cannot_read | spi.sio_can_write;
} /* OUT follows, no fall-through from IN */
if (spi.hasReset && (data == spi.sio_reset)) { /* reset command */
if (!sio_unit.u4) /* only reset for regular console I/O */
sio_unit.u3 = FALSE; /* indicate that no character is available */
sim_debug(CMD_MSG, &sio_dev, "\tSIO_S: " ADDRESS_FORMAT
" Command OUT(0x%03x) = 0x%02x\n", PCX, port, data);
}
return 0x00; /* ignored since OUT */
}
int32 sio0s(const int32 port, const int32 io, const int32 data) {
const int32 result = sio0sCore(port, io, data);
if (io == 0) {
sim_debug(IN_MSG, &sio_dev, "\tSIO_S: " ADDRESS_FORMAT
" IN(0x%03x) = 0x%02x\n", PCX, port, result);
} else if (io) {
sim_debug(OUT_MSG, &sio_dev, "\tSIO_S: " ADDRESS_FORMAT
" OUT(0x%03x) = 0x%02x\n", PCX, port, data);
}
return result;
}
/* generic data port for keyboard input / terminal output */
static int32 sio0dCore(const int32 port, const int32 io, const int32 data) {
int32 ch;
const SIO_PORT_INFO spi = lookupPortInfo(port, &ch);
ASSURE(spi.port == port);
pollConnection();
if (io == 0) { /* IN */
if ((sio_unit.flags & UNIT_ATT) && (!sio_unit.u4))
return mapCharacter(tmxr_getc_ln(&TerminalLines[spi.terminalLine]));
if (!sio_unit.u3) {
sim_debug(BUFFER_EMPTY_MSG, &sio_dev, "\tSIO_D: " ADDRESS_FORMAT
" IN(0x%03x) for empty character buffer\n", PCX, port);
}
sio_unit.u3 = FALSE; /* no character is available any more */
return mapCharacter(sio_unit.buf); /* return previous character */
} /* OUT follows, no fall-through from IN */
if (spi.hasOUT) {
ch = sio_unit.flags & UNIT_SIO_ANSI ? data & 0x7f : data; /* clear highest bit in ANSI mode */
if ((ch != CONTROLG_CHAR) || !(sio_unit.flags & UNIT_SIO_BELL)) {
voidSleep();
if ((sio_unit.flags & UNIT_ATT) && (!sio_unit.u4)) { /* attached to a port and not to a file */
tmxr_putc_ln(&TerminalLines[spi.terminalLine], ch); /* status ignored */
tmxr_poll_tx(&altairTMXR); /* poll xmt */
} else
sim_putchar(ch);
}
}
return 0x00; /* ignored since OUT */
}
static char* printable(char* result, int32 data, const int32 isIn) {
result[0] = 0;
data &= 0x7f;
if ((0x20 <= data) && (data < 0x7f))
sprintf(result, isIn ? " <-\"%c\"" : " ->\"%c\"", data);
return result;
}
int32 sio0d(const int32 port, const int32 io, const int32 data) {
char buffer[8];
const int32 result = sio0dCore(port, io, data);
if (io == 0) {
sim_debug(IN_MSG, &sio_dev, "\tSIO_D: " ADDRESS_FORMAT
" IN(0x%03x) = 0x%02x%s\n", PCX, port, result, printable(buffer, result, TRUE));
} else if (io) {
sim_debug(OUT_MSG, &sio_dev, "\tSIO_D: " ADDRESS_FORMAT
" OUT(0x%03x) = 0x%02x%s\n", PCX, port, data, printable(buffer, data, FALSE));
}
return result;
}
/* PTR/PTP status port */
static int32 sio1sCore(const int32 port, const int32 io, const int32 data) {
if (io == 0) { /* IN */
/* reset I bit iff PTR unit not attached or
no more data available. O bit is always
set since write always possible. */
if ((ptr_unit.flags & UNIT_ATT) == 0) { /* PTR is not attached */
if ((ptr_dev.dctrl & VERBOSE_MSG) && (warnUnattachedPTR < warnLevelSIO)) {
warnUnattachedPTR++;
/*06*/ sim_debug(VERBOSE_MSG, &ptr_dev, "PTR: " ADDRESS_FORMAT
" Attempt to test status of unattached PTR[0x%02x]. 0x02 returned.\n", PCX, port);
}
return SIO_CAN_WRITE;
}
/* if EOF then SIO_CAN_WRITE else
(SIO_CAN_WRITE and SIO_CAN_READ) */
return ptr_unit.u3 ? SIO_CAN_WRITE : (SIO_CAN_READ | SIO_CAN_WRITE);
} /* OUT follows */
if (data == SIO_RESET) {
ptr_unit.u3 = FALSE; /* reset EOF indicator */
sim_debug(CMD_MSG, &ptr_dev, "PTR: " ADDRESS_FORMAT
" Command OUT(0x%03x) = 0x%02x\n", PCX, port, data);
}
return 0x00; /* ignored since OUT */
}
int32 sio1s(const int32 port, const int32 io, const int32 data) {
const int32 result = sio1sCore(port, io, data);
if (io == 0) {
sim_debug(IN_MSG, &ptr_dev, "PTR_S: " ADDRESS_FORMAT
" IN(0x%02x) = 0x%02x\n", PCX, port, result);
sim_debug(IN_MSG, &ptp_dev, "PTP_S: " ADDRESS_FORMAT
" IN(0x%02x) = 0x%02x\n", PCX, port, result);
} else if (io) {
sim_debug(OUT_MSG, &ptr_dev, "PTR_S: " ADDRESS_FORMAT
" OUT(0x%02x) = 0x%02x\n", PCX, port, data);
sim_debug(OUT_MSG, &ptp_dev, "PTP_S: " ADDRESS_FORMAT
" OUT(0x%02x) = 0x%02x\n", PCX, port, data);
}
return result;
}
/* PTR/PTP data port */
static int32 sio1dCore(const int32 port, const int32 io, const int32 data) {
int32 ch;
if (io == 0) { /* IN */
if (ptr_unit.u3) { /* EOF reached, no more data available */
if ((ptr_dev.dctrl & VERBOSE_MSG) && (warnPTREOF < warnLevelSIO)) {
warnPTREOF++;
/*07*/ sim_debug(VERBOSE_MSG, &ptr_dev, "PTR: " ADDRESS_FORMAT
" PTR[0x%02x] attempted to read past EOF. 0x00 returned.\n", PCX, port);
}
return 0x00;
}
if ((ptr_unit.flags & UNIT_ATT) == 0) { /* not attached */
if ((ptr_dev.dctrl & VERBOSE_MSG) && (warnUnattachedPTR < warnLevelSIO)) {
warnUnattachedPTR++;
/*08*/ sim_debug(VERBOSE_MSG, &ptr_dev, "PTR: " ADDRESS_FORMAT
" Attempt to read from unattached PTR[0x%02x]. 0x00 returned.\n", PCX, port);
}
return 0x00;
}
if ((ch = getc(ptr_unit.fileref)) == EOF) { /* end of file? */
ptr_unit.u3 = TRUE; /* remember EOF reached */
return CONTROLZ_CHAR; /* ^Z denotes end of text file in CP/M */
}
return ch & 0xff;
} /* OUT follows */
if (ptp_unit.flags & UNIT_ATT) /* unit must be attached */
putc(data, ptp_unit.fileref);
/* else ignore data */
else if ((ptp_dev.dctrl & VERBOSE_MSG) && (warnUnattachedPTP < warnLevelSIO)) {
warnUnattachedPTP++;
/*09*/ sim_debug(VERBOSE_MSG, &ptp_dev, "PTP: " ADDRESS_FORMAT
" Attempt to output '0x%02x' to unattached PTP[0x%02x] - ignored.\n", PCX, data, port);
}
return 0x00; /* ignored since OUT */
}
int32 sio1d(const int32 port, const int32 io, const int32 data) {
const int32 result = sio1dCore(port, io, data);
if (io == 0) {
sim_debug(IN_MSG, &ptr_dev, "PTR_D: " ADDRESS_FORMAT
" IN(0x%02x) = 0x%02x\n", PCX, port, result);
sim_debug(IN_MSG, &ptp_dev, "PTP_D: " ADDRESS_FORMAT
" IN(0x%02x) = 0x%02x\n", PCX, port, result);
} else if (io) {
sim_debug(OUT_MSG, &ptr_dev, "PTR_D: " ADDRESS_FORMAT
" OUT(0x%02x) = 0x%02x\n", PCX, port, data);
sim_debug(OUT_MSG, &ptp_dev, "PTP_D: " ADDRESS_FORMAT
" OUT(0x%02x) = 0x%02x\n", PCX, port, data);
}
return result;
}
static t_stat toBool(char tf, int32 *result) {
if (tf == 'T') {
*result = TRUE;
return SCPE_OK;
}
if (tf == 'F') {
*result = FALSE;
return SCPE_OK;
}
return SCPE_ARG;
}
static void show_sio_port_info(FILE *st, SIO_PORT_INFO sip) {
if (sio_unit.flags & UNIT_SIO_VERBOSE)
fprintf(st, "(Port=%02x/Terminal=%1i/Read=0x%02x/NotRead=0x%02x/"
"Write=0x%02x/Reset?=%s/Reset=0x%02x/Data?=%s), %s",
sip.port, sip.terminalLine, sip.sio_can_read, sip.sio_cannot_read,
sip.sio_can_write, sip.hasReset ? "True" : "False", sip.sio_reset,
sip.hasOUT ? "True" : "False",
handlerNameForPort(sip.port));
else