summaryrefslogtreecommitdiffstatshomepage
path: root/src/mess/drivers/vk100.c
blob: 376b54efbcb09e8ec76096dd232726b4ddfac4b8 (plain) (blame)
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
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
// license:BSD-3-Clause
// copyright-holders:Jonathan Gevaryahu
/***************************************************************************

        DEC VK100 'GIGI'

        12/05/2009 Skeleton driver.
        28/07/2009 added Guru-readme(TM)
        08/01/2012 Fleshed out driver.

        Todo:
              * fix vector generator hardware enough to pass the startup self test
                the tests are described on page 6-5 thru 6-8 of the tech reference
                * hook up the bresenham DU/DVM/ERR stuff, currently only simple directional vectors work
                * hook up the vector and sync proms to the sync counter
                * figure out how the erase prom actually works at a hardware level
                * redump the vector prom, the first two bytes look bad
              * figure out the correct meaning of systat b register - needed for communications selftest
              * hook up smc com5016t baud generator to i8251 rx and tx clocks - begun

        Notes:
              The directions for the DIR value are arranged, starting from the *
              as the vector origin:
                 3  2  1
                  \ | /
                   \|/
                 4--*--0
                   /|\
                  / | \
                 5  6  7

               The X and Y counters are techincally 12 bits long each, though
               only the low 9 and 10 bits respectively are used for ram addressing.
               The MSB bit of each counter does have a special purpose with
               regards to the RAS/ERASE prom though, perhaps to detect an
               underflow 000->FFF

 Tony DiCenzo, now the director of standards and architecture at Oracle, was on the team that developed the VK100
 see http://startup.nmnaturalhistory.org/visitorstories/view.php?ii=79
 Robert "Bob" C. Quinn was definitely lead engineer on the VT125
 Robert "Bob" T. Collins was the lead engineer on the VK100
 Pedro Ortiz (https://www.linkedin.com/pub/pedro-ortiz/16/68b/196) did the drafting for the enclosure and case

 The prototype name for the VK100 was 'SMAKY' (Smart Keyboard)

****************************************************************************/
/*
DEC VK100
DEC, 1982

This is a VK100 terminal, otherwise known as a DEC Gigi graphics terminal.
There's a technical manual dated 1982 here:
http://web.archive.org/web/20091015205827/http://www.computer.museum.uq.edu.au/pdf/EK-VK100-TM-001%20VK100%20Technical%20Manual.pdf
Installation and owner's manual is at:
http://www.bitsavers.org/pdf/dec/terminal/gigi/EK-VK100-IN-002_GIGI_Terminal_Installation_and_Owners_Manual_Apr81.pdf
An enormous amount of useful info can be derived from the VT125 technical manual:
http://www.bitsavers.org/pdf/dec/terminal/vt100/EK-VT100-TM-003_VT100_Technical_Manual_Jul82.pdf starting on page 6-70, pdf page 316
And its schematics:
http://bitsavers.org/pdf/dec/terminal/vt125/MP01053_VT125_Mar82.pdf

PCB Layout
----------

VK100 LOGICBOARD
    |-------|    |---------|  |---------|    |-| |-| |-|  |-|
|---|-20 mA-|----|---EIA---|--|HARD-COPY|----|B|-|G|-|R|--|-|--DSW(8)--|
|                                                         BW           |
|                                                             POWER    |
|                 PR2                                                  |
|                           HD46505SP              4116 4116 4116 4116 |
|                                                                      |
|                                                  4116 4116 4116 4116 |
|      PR5        INTEL           ROM1                                 |
|         PR1 PR6 P8251A                           4116 4116 4116 4116 |
|                     45.6192MHz  ROM2  PR3                            |
|                                                  4116 4116 4116 4116 |
| 4116 4116 4116  INTEL           ROM3                                 |
|                 D8202A                                               |
| 4116 4116 4116       5.0688MHz  ROM4                       PR4       |
|                                                                      |
| 4116 4116       INTEL    SMC_5016T                            PIEZO  |
|                 D8085A                        IDC40   LM556   75452  |
|----------------------------------------------------------------------|
Notes:
      ROM1 - TP-01 (C) DEC 23-031E4-00 (M) SCM91276L 8114
      ROM2 - TP-01 (C) DEC 1980 23-017E4-00 MOSTEK MK36444N 8116
      ROM3 - TP-01 (C) MICROSOFT 1979 23-018E4-00 MOSTEK MK36445N 8113
      ROM4 - TP-01 (C) DEC 1980 23-190E2-00 P8316E AMD 35517 8117DPP

    LED meanings:
    The LEDS on the vk100 (there are 7) are set up above the keyboard as:
    Label: ON LINE   LOCAL     NO SCROLL BASIC     HARD-COPY L1        L2
    Bit:   !d5       d5        !d4       !d3       !d2       !d1       !d0 (of port 0x68)
according to manual from http://www.bitsavers.org/pdf/dec/terminal/gigi/EK-VK100-IN-002_GIGI_Terminal_Installation_and_Owners_Manual_Apr81.pdf
where X = on, 0 = off, ? = variable (- = off)
    - X 0 0 0 0 0 (0x1F) = Microprocessor error
    X - 0 X X X X (0x30) "

    - X 0 0 0 0 X (0x1E) = ROM error
    X - 0 0 ? ? ? (0x3x) "
1E 3F = rom error, rom 1 (0000-0fff)
1E 3E = rom error, rom 1 (1000-1fff)
1E 3D = rom error, rom 2 (2000-2fff)
1E 3C = rom error, rom 2 (3000-3fff)
1E 3B = rom error, rom 3 (4000-4fff)
1E 3A = rom error, rom 3 (5000-5fff)
1E 39 = rom error, rom 4 (6000-6fff)

    - X 0 0 0 X 0 (0x1D) = RAM error
    X - 0 ? ? ? ? (0x3x) "

    - X 0 0 0 X X (0x1C) = CRT Controller error
    X - 0 X X X X (0x30) "
This test writes 0xF to port 00 (crtc address reg) and writes a pattern to it
via port 01 (crtc data reg) then reads it back and checks to be sure the data
matches.

    - X 0 0 X 0 0 (0x1B) = CRT Controller time-out
    X - 0 X X X X (0x30) "
This test writes 00 to all the crtc registers and checks to be sure an rst7.5
(vblank) interrupt fires on the 8085 within a certain time period.

    - X 0 0 X 0 X (0x1A) = Vector time-out error
    X - 0 X X X X (0x30) "
Not sure exactly what this tests, likely tries firing the vector generator
state machine and sees if the GO bit ever finishes and goes back to 0
*/

// named timer IDs
#define TID_I8251_RX 1
#define TID_I8251_TX 2
#define TID_SYNC 3

// show messages related to writes to the 0x4x VG registers
#undef VG40_VERBOSE
// show messages related to writes to the 0x6x VG registers
#undef VG60_VERBOSE
// show messages related to LED/beeper writes
#undef LED_VERBOSE
// show messages related to KYBD writes
#undef KBD_VERBOSE
// debug the pattern reg
#undef PAT_DEBUG
// show reads from the two systat registers
#undef SYSTAT_A_VERBOSE
#undef SYSTAT_B_VERBOSE

// debug state dump for the vector generator
#undef DEBUG_VG_STATE

#include "bus/rs232/rs232.h"
#include "cpu/i8085/i8085.h"
#include "sound/beep.h"
#include "video/mc6845.h"
#include "machine/com8116.h"
#include "machine/i8251.h"
#include "vk100.lh"

#define RS232_TAG       "rs232"
#define COM5016T_TAG    "com5016t"

class vk100_state : public driver_device
{
public:
	enum
	{
		TIMER_EXECUTE_VG
	};

	vk100_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_maincpu(*this, "maincpu"),
		m_crtc(*this, "crtc"),
		m_speaker(*this, "beeper"),
		m_uart(*this, "i8251"),
		m_dbrg(*this, COM5016T_TAG),
		//m_i8251_rx_timer(NULL),
		//m_i8251_tx_timer(NULL),
		//m_sync_timer(NULL),

		m_capsshift(*this, "CAPSSHIFT"),
		m_dipsw(*this, "SWITCHES")
	{
	}

	required_device<cpu_device> m_maincpu;
	required_device<mc6845_device> m_crtc;
	required_device<beep_device> m_speaker;
	required_device<i8251_device> m_uart;
	required_device<com8116_device> m_dbrg;
	//required_device<> m_i8251_rx_timer;
	//required_device<> m_i8251_tx_timer;
	//required_device<> m_sync_timer;

	required_ioport m_capsshift;
	required_ioport m_dipsw;

	UINT8* m_vram;
	UINT8* m_trans;
	UINT8* m_pattern;
	UINT8* m_dir;
	UINT8* m_sync;
	UINT8* m_vector;
	UINT8* m_ras_erase;
	UINT8 m_dir_a6; // latched a6 of dir rom
	UINT8 m_cout; // carry out from vgERR adder
	UINT8 m_vsync; // vsync pin of crtc
	UINT16 m_vgX; // 12 bit X value for vector draw position
	UINT16 m_vgY; // 12 bit Y value for vector draw position
	UINT16 m_vgERR; // error register can cause carries which need to be caught
	UINT8 m_vgSOPS;
	UINT8 m_vgPAT;
	UINT16 m_vgPAT_Mask; // current mask for PAT
	UINT8 m_vgPMUL; // reload value for PMUL_Count
	UINT8 m_vgPMUL_Count;
	UINT8 m_vgDownCount; // down counter = number of pixels, loaded from vgDU on execute
#define VG_DU m_vgRegFile[0]
#define VG_DVM m_vgRegFile[1]
#define VG_DIR m_vgRegFile[2]
#define VG_WOPS m_vgRegFile[3]
	UINT8 m_vgRegFile[4];
	UINT8 m_VG_MODE; // 2 bits, latched on EXEC
	UINT8 m_vgGO; // activated on next SYNC pulse after EXEC
	UINT8 m_ACTS;
	UINT8 m_ADSR;
	ioport_port* m_col_array[16];

	DECLARE_WRITE8_MEMBER(vgLD_X);
	DECLARE_WRITE8_MEMBER(vgLD_Y);
	DECLARE_WRITE8_MEMBER(vgERR);
	DECLARE_WRITE8_MEMBER(vgSOPS);
	DECLARE_WRITE8_MEMBER(vgPAT);
	DECLARE_WRITE8_MEMBER(vgPMUL);
	DECLARE_WRITE8_MEMBER(vgREG);
	DECLARE_WRITE8_MEMBER(vgEX);
	DECLARE_WRITE8_MEMBER(KBDW);
	DECLARE_WRITE8_MEMBER(BAUD);
	DECLARE_READ8_MEMBER(vk100_keyboard_column_r);
	DECLARE_READ8_MEMBER(SYSTAT_A);
	DECLARE_READ8_MEMBER(SYSTAT_B);
	DECLARE_DRIVER_INIT(vk100);
	virtual void machine_start();
	virtual void video_start();
	TIMER_CALLBACK_MEMBER(execute_vg);
	DECLARE_WRITE_LINE_MEMBER(crtc_vsync);
	DECLARE_WRITE_LINE_MEMBER(i8251_rxrdy_int);
	DECLARE_WRITE_LINE_MEMBER(i8251_txrdy_int);
	DECLARE_WRITE_LINE_MEMBER(i8251_rts);
	UINT8 vram_read();
	UINT8 vram_attr_read();
	MC6845_UPDATE_ROW(crtc_update_row);
	void vram_write(UINT8 data);

protected:
	virtual void device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr);
};

// vram access functions:
	/* figure out vram address based on tech manual page 5-24:
	 * real address to 16-bit chunk a13 a12 | a11 a10 a9  a8 | a7  a6  a5  a4 | a3  a2  a1  a0
	 * X+Y input                    Y8  Y7  | Y6  Y5  Y4  Y3 | Y2  Y1  X9' X8'| X7' X6' X5' X4'
	 *          X3' and X2' choose a 4-bit block, X1 and X0 choose a bit within that.
	 * Figure out the ram address in address space "vram" based on this:
	 * vram is 0x8000 long (0x4000 16-bit blocks in sequence)
	 * so:
	 * vram: a14 a13 a12 | a11 a10 a9  a8 | a7  a6  a5  a4 | a3  a2  a1  a0
	 * vg:   Y8  Y7  Y6  | Y5  Y4  Y3  Y2 | Y1  X9' X8' X7'| X6' X5' X4'(x3') x2' x1 x0
	 *     X3' is handled by the nybbleNum statement, as is X2'
	 *     X1 and X0 are handled by the pattern rom directly:
	 *     x0 -> a0, x1 -> a1
	 *     this handles bits like:
	 *     x1 x0
	 *      0  0 -> bit 0
	 *      0  1 -> bit 1
	 *      1  0 -> bit 2
	 *      1  1 -> bit 3
	 */

// returns one nybble from vram array based on X and Y regs
UINT8 vk100_state::vram_read()
{
	// XFinal is (X'&0x3FC)|(X&0x3)
	UINT16 XFinal = m_trans[(m_vgX&0x3FC)>>2]<<2|(m_vgX&0x3); // appears correct
	// EA is the effective ram address for a 16-bit block
	UINT16 EA = ((m_vgY&0x1FE)<<5)|(XFinal>>4); // appears correct
	// block is the 16 bit block directly (note EA has to be <<1 to correctly index a byte)
	UINT16 block = m_vram[(EA<<1)+1] | (m_vram[(EA<<1)]<<8);
	// nybbleNum is which of the four nybbles within the block to address. should NEVER be 3!
	UINT8 nybbleNum = (XFinal&0xC)>>2;
	return (block>>(4*nybbleNum))&0xF;
}

// returns the attribute nybble for the current pixel based on X and Y regs
UINT8 vk100_state::vram_attr_read()
{
	// XFinal is (X'&0x3FC)|(X&0x3)
	UINT16 XFinal = m_trans[(m_vgX&0x3FC)>>2]<<2|(m_vgX&0x3); // appears correct
	// EA is the effective ram address for a 16-bit block
	UINT16 EA = ((m_vgY&0x1FE)<<5)|(XFinal>>4); // appears correct
	// block is the 16 bit block directly (note EA has to be <<1 to correctly index a byte)
	UINT16 block = m_vram[(EA<<1)+1] | (m_vram[(EA<<1)]<<8);
	// nybbleNum is the attribute nybble, which in this case is always 3
	UINT8 nybbleNum = 3;
	return (block>>(4*nybbleNum))&0xF;
}

// writes one nybble to vram array based on X and Y regs, and updates the attrib ram if needed
void vk100_state::vram_write(UINT8 data)
{
	// XFinal is (X'&0x3FC)|(X&0x3)
	UINT16 XFinal = m_trans[(m_vgX&0x3FC)>>2]<<2|(m_vgX&0x3); // appears correct
	// EA is the effective ram address for a 16-bit block
	UINT16 EA = ((m_vgY&0x1FE)<<5)|(XFinal>>4); // appears correct
	// block is the 16 bit block directly (note EA has to be <<1 to correctly index a byte)
	UINT16 block = m_vram[(EA<<1)+1] | (m_vram[(EA<<1)]<<8);
	// nybbleNum is which of the four nybbles within the block to address. should NEVER be 3!
	UINT8 nybbleNum = (XFinal&0xC)>>2;
	block &= ~((UINT16)0xF<<(nybbleNum*4)); // mask out the part we want to replace
	block |= data<<(nybbleNum*4); // write the new part
	// NOTE: this next part may have to be made conditional on VG_MODE
	// check if the attribute nybble is supposed to be modified, and if so do so
	if (VG_WOPS&0x08) block = (block&0x0FFF)|(((UINT16)VG_WOPS&0xF0)<<8);
	m_vram[(EA<<1)+1] = block&0xFF; // write block back to vram
	m_vram[(EA<<1)] = (block&0xFF00)>>8; // ''
}

void vk100_state::device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr)
{
	switch (id)
	{
	case TIMER_EXECUTE_VG:
		execute_vg(ptr, param);
		break;
	default:
		assert_always(FALSE, "Unknown id in vk100_state::device_timer");
	}
}

/* this is the "DIRECTION ROM"  == mb6309 (256x8, 82s135)
 * see figure 5-24 on page 5-39
 * It tells the direction and enable for counting on the X and Y counters
 * and also handles the non-math related parts of the bresenham line algorithm
 * control bits:
 *            /CE1 ----- DCOUNT 0 H [verified via tracing]
 *            /CE2 ----- ENA ERROR L [verified via tracing]
 * addr bits: 76543210
 *            ||||\\\\-- DIR (vgDIR register low 4 bits)
 *            |||\------ C OUT aka ERROR CARRY (strobed in by STROBE L from the error counter's adder) [verified via tracing]
 *            ||\------- Y0 (the otherwise unused lsb of the Y register, used for bresenham) [verified via tracing]
 *            |\-------- feedback bit from d5 strobed by V CLK [verified via tracing]
 *            \--------- GND; the second half of the prom is blank (0x00)
 * data bits: 76543210
 *            |||||||\-- ENA Y (enables change on Y counter)
 *            ||||||\--- ENA X (enables change on X counter)
 *            |||||\---- Y DIRECTION (high is count down, low is count up)
 *            ||||\----- X DIRECTION (high is count down, low is count up)
 *            |||\------ PIXEL WRT
 *            ||\------- feedback bit to a6, this bit is held in PRESET/1 condition by GO being inactive, and if the vector prom is disabled it is pulled to 1 [verified via tracing and schematics]
 *            |\-------- UNUSED, always 0
 *            \--------- UNUSED, always 0
 * The VT125 prom @ E41 is literally identical to this, the same exact part: 23-059B1
 */
TIMER_CALLBACK_MEMBER(vk100_state::execute_vg)
{
	m_cout = 1; // hack for now
	UINT8 dirbyte = m_dir[(m_dir_a6<<6)|((m_vgY&1)<<5)|(m_cout<<4)|VG_DIR];
#ifdef DEBUG_VG_STATE
	static const char *const vg_functions[] = { "Move", "Dot", "Vector", "Erase" };
	fprintf(stderr, "VGMODE: %s; DIR: A:%02x; D:%02x; X: %03X; Y: %03X; DownCount: %02X\n", vg_functions[m_VG_MODE], ((m_dir_a6<<6)|((m_vgY&1)<<5)|(m_cout<<4)|VG_DIR), dirbyte, m_vgX, m_vgY, m_vgDownCount);
#endif
	m_dir_a6 = m_vgGO?((dirbyte&0x20)>>5):1;
	if (dirbyte&2) // ena_x is active
	{
		if (dirbyte&0x80) m_vgX--;
		else m_vgX++;
	}
	if (dirbyte&1) // ena_y is active
	{
		if (dirbyte&0x40) m_vgY--;
		else m_vgY++;
	}
	if (dirbyte&0x10) m_vgDownCount--; // decrement the down counter
	UINT8 thisNyb = vram_read(); // read in the nybble
	// pattern rom addressing is a complex mess. see the pattern rom def later in this file.
	UINT8 newNyb = m_pattern[((m_vgPAT&m_vgPAT_Mask)?0x200:0)|((VG_WOPS&7)<<6)|((m_vgX&3)<<4)|thisNyb]; // calculate new nybble based on pattern rom
	// finally write the block back to ram depending on the VG_MODE (sort of a hack until we get the vector and sync and dir roms all hooked up)
	// but only do it if the direction rom said so!
	switch (m_VG_MODE)
	{
		case 0: // move; adjusts the x and y but doesn't write anything. do nothing
			break;
		case 1: // dot: only write the LAST pixel in the chain? TODO: some fallthrough magic here?
			if ((m_vgDownCount) == 0x00)
			{
				if (dirbyte&0x10) vram_write(newNyb); // write out the modified nybble
			}
			break;
		case 2: // vec: draw the vector
				if (dirbyte&0x10) vram_write(newNyb); // write out the modified nybble
			break;
		case 3: // er: erase: special case here: wipe the entire screen (except for color/attrib?) and then set done.
			for (int i = 0; i < 0x8000; i++)
			{
				if (!(i&1)) // avoid stomping attribute
					m_vram[i] = m_vram[i]&0xF0;
				else // (i&1)
					m_vram[i] = 0;
			}
			m_vgGO = 0; // done
			break;
	}
	if ((m_vgDownCount) == 0x00) m_vgGO = 0; // check if the down counter hit terminal count (0), if so we're done.
	if (((++m_vgPMUL_Count)&0xF)==0) // if pattern multiplier counter overflowed
	{
		m_vgPMUL_Count = m_vgPMUL; // reload counter
		m_vgPAT_Mask >>= 1; // shift the mask
		if (m_vgPAT_Mask == 0) m_vgPAT_Mask = 0x80; // reset mask if it hits 0
	}
	if (m_vgGO) timer_set(attotime::from_hz(XTAL_45_6192Mhz/3/12/2), TIMER_EXECUTE_VG); // /3/12/2 is correct. the sync counter is clocked by the dot clock, despite the error on figure 5-21
}

/* ports 0x40 and 0x41: load low and high bytes of vector gen X register */
WRITE8_MEMBER(vk100_state::vgLD_X)
{
	m_vgX &= 0xFF << ((1-offset)*8);
	m_vgX |= ((UINT16)data) << (offset*8);
#ifdef VG40_VERBOSE
	logerror("VG: 0x%02X: X Reg loaded with %04X, new X value is %04X\n", 0x40+offset, ((UINT16)data) << (offset*8), m_vgX);
#endif
}

/* ports 0x42 and 0x43: load low and high bytes of vector gen Y register */
WRITE8_MEMBER(vk100_state::vgLD_Y)
{
	m_vgY &= 0xFF << ((1-offset)*8);
	m_vgY |= ((UINT16)data) << (offset*8);
#ifdef VG40_VERBOSE
	logerror("VG: 0x%02X: Y Reg loaded with %04X, new Y value is %04X\n", 0x42+offset, ((UINT16)data) << (offset*8), m_vgY);
#endif
}

/* port 0x44: "ERR" load bresenham line algorithm 'error' count */
WRITE8_MEMBER(vk100_state::vgERR)
{
	m_vgERR = data;
#ifdef VG40_VERBOSE
	logerror("VG: 0x44: ERR Reg loaded with %02X\n", m_vgERR);
#endif
}

/* port 0x45: "SOPS" screen options
 * (handled by 74LS273 @ E55, schematic sheet 10, all signals called 'VVG1 BDx' where x is 7 to 0)
 * Blink   --Background color--    Blink   Serial  Serial  Reverse
 * Enable  Green   Red     Blue    Control SL1     SL0     BG/FG
 * d7      d6      d5      d4      d3      d2      d1      d0
 * apparently, SLx: 00 = rs232/eia(J6), 01 = 20ma(J1), 10 = hardcopy(J7), 11 = test/loopback
 * Serial Select (SLx) routing controls are rather complex, shown on schematic
 * page 9:
 * VDC2    |  I8251 pins                                                      |  SYSTAT_B bits
 * SL1 SL0 |  8251RXD   8251RTS    8251TXD    8251/DTR    8251/DSR    8251CTS |  SYSTATB_ACTS   SYSTATB_ADSR
 * 0   0      J6 /RXD   J6 /RTS    J6 TXD     J6 /DTR     J7 URTS     GND        J6 /CTS        J6 /DSR
 * 0   1      J1 +-R    ACTS(loop) J1 +-T     J6 /DTR     J7 URTS     GND        8251RTS(loop)  J6 /DSR
 * 1   0      J7 DRXD   J7 DRTS*   J7 DTXD    J6 /DTR     J7 URTS     GND        J7 /DCTS       J6 /DSR
 * 1   1      8251TXD   ACTS(loop) 8251RXD    J6 /DTR     J7 URTS     GND        8251RTS(loop)  J6 /DSR
 *                      * and UCTS, the pin drives both pins on J7
 */
WRITE8_MEMBER(vk100_state::vgSOPS)
{
	m_vgSOPS = data;
#ifdef VG40_VERBOSE
	static const char *const serialDest[4] = { "EIA232", "20ma", "Hardcopy", "Loopback/test" };
	logerror("VG: 0x45: SOPS Reg loaded with %02X: Background KGRB: %d%d%d%d, Blink: %d, Serial select: %s, Reverse BG/FG: %d\n", m_vgSOPS, (m_vgSOPS>>7)&1, (m_vgSOPS>>6)&1, (m_vgSOPS>>5)&1, (m_vgSOPS>>4)&1, (m_vgSOPS>>3)&1, serialDest[(m_vgSOPS>>1)&3], m_vgSOPS&1);
#endif
}

/* port 0x46: "PAT" load vg Pattern register */
WRITE8_MEMBER(vk100_state::vgPAT)
{
	m_vgPAT = data;
#ifdef PAT_DEBUG
	for (int i = 7; i >= 0; i--)
	{
		printf("%s", (data&(1<<i))?"X":".");
	}
	printf("\n");
#endif
#ifdef VG40_VERBOSE
	logerror("VG: 0x46: PAT Reg loaded with %02X\n", m_vgPAT);
#endif
}

/* port 0x47: "PMUL" load vg Pattern Multiply Register
   The pattern multiply register loads a counter which counts reads from
   the pattern register and increments on each one. if it overflows from
   1111 to 0000 the pattern register is shifted right one bit and the
   counter is reloaded from PMUL */
WRITE8_MEMBER(vk100_state::vgPMUL)
{
	m_vgPMUL = data;
#ifdef VG40_VERBOSE
	logerror("VG: 0x47: PMUL Reg loaded with %02X\n", m_vgPMUL);
#endif
}

/* port 0x60: "DU" load vg vector major register */
/* port 0x61: "DVM" load vg vector minor register */
/* port 0x62: "DIR" load vg Direction register */
/* port 0x63: "WOPS" vector 'pixel' write options
 * --Attributes to change --   Enable --  Functions --
 * Blink  Green  Red    Blue   Attrib F1     F0     FN
 *                             Change
 * d7     d6     d5     d4     d3     d2     d1     d0
 */
WRITE8_MEMBER(vk100_state::vgREG)
{
	m_vgRegFile[offset] = data;
#ifdef VG60_VERBOSE
	static const char *const regDest[4] = { "DU", "DVM", "DIR", "WOPS" };
	static const char *const wopsFunctions[] = { "Overlay", "Replace", "Complement", "Erase" };
	if (offset < 3) logerror("VG: 0x%02x: %s Reg loaded with %02X\n", (0x60+offset), regDest[offset], m_vgRegFile[offset]);
	else logerror("VG: 0x63: WOPS Reg loaded with %02X: KGRB %d%d%d%d, AttrChange %d, Function %s, Negate %d\n", data, (VG_WOPS>>7)&1, (VG_WOPS>>6)&1, (VG_WOPS>>5)&1, (VG_WOPS>>4)&1, (VG_WOPS>>3)&1, wopsFunctions[(VG_WOPS>>1)&3], VG_WOPS&1);
#endif
}


/* port 0x64: "EX MOV" execute a move (relative move of x and y using du/dvm/dir/err, no writing) */
/* port 0x65: "EX DOT" execute a dot (draw a dot at x,y?) */
/* port 0x66: "EX VEC" execute a vector (draw a vector from x,y to a destination using du/dvm/dir/err ) */
/* port 0x67: "EX ER" execute an erase (clear the screen to the bg color, i.e. fill vram with zeroes) */
WRITE8_MEMBER(vk100_state::vgEX)
{
#ifdef VG60_VERBOSE
	static const char *const ex_functions[] = { "Move", "Dot", "Vector", "Erase" };
	logerror("VG Execute %s 0x%02X written with %d\n", ex_functions[offset&3], 0x67+offset, data);
	//fprintf(stderr, "VG Execute %s 0x%02X written with %d\n", ex_functions[offset&3], 0x67+offset, data);
#endif
	m_vgPMUL_Count = m_vgPMUL; // load PMUL_Count
	m_vgPAT_Mask = 0x80;
	m_vgDownCount = VG_DU; // set down counter to length of major vector
	m_VG_MODE = offset&3;
	m_vgGO = 1;
	timer_set(attotime::zero, TIMER_EXECUTE_VG);
}


/* port 0x68: "KBDW" d7 is beeper, d6 is keyclick, d5-d0 are keyboard LEDS */
WRITE8_MEMBER(vk100_state::KBDW)
{
	output_set_value("online_led",BIT(data, 5) ? 1 : 0);
	output_set_value("local_led", BIT(data, 5) ? 0 : 1);
	output_set_value("noscroll_led",BIT(data, 4) ? 1 : 0);
	output_set_value("basic_led", BIT(data, 3) ? 1 : 0);
	output_set_value("hardcopy_led", BIT(data, 2) ? 1 : 0);
	output_set_value("l1_led", BIT(data, 1) ? 1 : 0);
	output_set_value("l2_led", BIT(data, 0) ? 1 : 0);
#ifdef LED_VERBOSE
	if (BIT(data, 6)) logerror("kb keyclick bit 6 set: not emulated yet (multivibrator)!\n");
#endif
	m_speaker->set_state(BIT(data, 7));
#ifdef LED_VERBOSE
	logerror("LED state: %02X: %s %s %s %s %s %s\n", data&0xFF, (data&0x20)?"------- LOCAL ":"ON LINE ----- ", (data&0x10)?"--------- ":"NO SCROLL ", (data&0x8)?"----- ":"BASIC ", (data&0x4)?"--------- ":"HARD-COPY ", (data&0x2)?"-- ":"L1 ", (data&0x1)?"-- ":"L2 ");
#endif
}

/* port 0x6C: "BAUD" controls the smc com5016t dual baud generator which
 * controls the divisors for the rx and tx clocks on the 8251 from the
    5.0688Mhz cpu xtal.
   It has 5v,12v on pins 2 and 9, pin 10 is NC.
 * A later part that replaced this on the market is SMC COM8116(T)/8136(T), which
    was a 5v-only part (pin 9 and 10 are NC, 10 is a clock out on the 8136.
 *  Note that even on the SMC COM5016T version, SMC would allow the user
     to mask their own dividers on custom ordered chips if desired.
 *  The COM8116(T)/8136(T) came it at least 4 mask rom types meant for different
     input clocks:
     -000 or no mark for 5.0688Mhz (which exactly matches the table below) (synertek sy2661-3 also matches this exactly)
     -003 is for 6.01835MHz
     -005 is for 4.915200Mhz
     -006 is for 5.0688Mhz but omits the 2000 baud entry, instead has 200,
      and output frequencies are 2x as fast (meant for a 32X clock uart)
     -013 is for 2.76480MHz
     -013A is for 5.52960MHz
     (several other unknown refclock masks appear on partscalper sites)
    GI also made a clone of the 8116 5v chip called the AY-5-8116(T)/8136(T)
     which had at least two masks: -000/no mark and -005, matching speeds above
    WD made the WD1943 which is similarly 5v compatible, with -00, -05, -06 masks
    The COM8046(T) has 5 bits for selection instead of 4, but still expects
     a 5.0688MHz reference clock, and the second half of the table matches the
     values below; the first half of the table is the values below /2, rounded
     down (for uarts which need a clock rate of 32x baud instead of 16x).
    WD's BR1941 is also functionally compatible but uses 5v,12v,-5v on pins 2,9,10
 * The baud divisor lookup table has 16 entries, but only entries 2,5,6,7,A,C,E,F are documented/used in the vk100 tech manual
 * The others are based on page 13 of http://www.hartetechnologies.com/manuals/Tarbell/Tarbell%20Z80%20CPU%20Board%20Model%203033.pdf
 * D C B A   Divisor                                Expected Baud
 * 0 0 0 0 - 6336 (5068800 / 6336 = 16*50         = 50 baud
 * 0 0 0 1 - 4224 (5068800 / 4224 = 16*75)        = 75 baud
 * 0 0 1 0 - 2880 (5068800 / 2880 = 16*110)       = 110 baud
 * 0 0 1 1 - 2355 (5068800 / 2355 = 16*134.5223) ~= 134.5 baud
 * 0 1 0 0 - 2112 (5068800 / 2112 = 16*150)       = 150 baud
 * 0 1 0 1 - 1056 (5068800 / 1056 = 16*300)       = 300 baud
 * 0 1 1 0 - 528  (5068800 / 528 = 16*600)        = 600 baud
 * 0 1 1 1 - 264  (5068800 / 264 = 16*1200)       = 1200 baud
 * 1 0 0 0 - 176  (5068800 / 176 = 16*1800)       = 1800 baud
 * 1 0 0 1 - 158  (5068800 / 158 = 16*2005.0633) ~= 2000 baud
 * 1 0 1 0 - 132  (5068800 / 132 = 16*2400)       = 2400 baud
 * 1 0 1 1 - 88   (5068800 / 88 = 16*3600)        = 3600 baud
 * 1 1 0 0 - 66   (5068800 / 66 = 16*4800)        = 4800 baud
 * 1 1 0 1 - 44   (5068800 / 44 = 16*7200)        = 7200 baud
 * 1 1 1 0 - 33   (5068800 / 33 = 16*9600)        = 9600 baud
 * 1 1 1 1 - 16   (5068800 / 16 = 16*19800)      ~= 19200 baud
 */
WRITE8_MEMBER(vk100_state::BAUD)
{
	m_dbrg->str_w(data & 0x0f);
	m_dbrg->stt_w(data >> 4);
}

/* port 0x40-0x47: "SYSTAT A"; various status bits, poorly documented in the tech manual
 * /GO    VDM1   VDM1   VDM1   VDM1   Dip     RST7.5 GND***
 *        BIT3   BIT2   BIT1   BIT0   Switch  VSYNC
 * d7     d6     d5     d4     d3     d2      d1     d0
  bit3, 2, 1, 0 are the 4 bits output from the VRAM 12->4 multiplexer
   which are also inputs to the pattern rom; they are constantly updated
   by the sync rom and related circuitry.
  This is the only way the vram can be read by the cpu.
  d7 is from the /Q output of the GO latch
  d6,5,4,3 are from the 74ls298 at ic4 (right edge of pcb)
  d2 is where the dipswitch values are read from, based on the offset
  d1 is connected to 8085 rst7.5 (pin 7) and crtc pin 40 (VSYNC) [verified via tracing]
  d0 is tied to GND [verified via tracing] but the schematics both tie it to GND
     and call it LP FLAG, may be a leftover from development.

 31D reads and checks d7 in a loop
 205 reads, xors with 0x55 (from reg D), ANDS result with 0x78 and branches if it is not zero (checking for bit pattern 1010?)
 299 reads, rotates result right 3 times and ANDs the result with 0x0F
 2A4 reads, rotates result left 1 time and ANDS the result with 0xF0
*/
READ8_MEMBER(vk100_state::SYSTAT_A)
{
	UINT8 dipswitchLUT[8] = { 1,3,5,7,6,4,2,0 }; // the dipswitches map in a weird order to offsets
#ifdef SYSTAT_A_VERBOSE
	if (m_maincpu->pc() != 0x31D) logerror("0x%04X: SYSTAT_A Read!\n", m_maincpu->pc());
#endif
	return ((m_vgGO?0:1)<<7)|(vram_read()<<3)|(((m_dipsw->read()>>dipswitchLUT[offset])&1)?0x4:0)|(m_vsync?0x2:0);
}

/* port 0x48: "SYSTAT B"; NOT documented in the tech manual at all.
 * when in loopback/test mode, SYSTAT_B is read and expected the following, around 0x606:
 * reset 8751, modewrite of 0x5E
 * write command -> 0x20 (normal, normal, /RTS is 0, normal, normal, receive off, /DTR is 1, transmit off)
 * read SYSTAT B (and xor with 0xe), expect d7 to be CLEAR or jump to error
 * write command -> 0x05 (normal, normal, /RTS is 1, normal, normal, receive ON, /DTR is 0, transmit off)
 * read SYSTAT B (and xor with 0xe), expect d7 to be SET or jump to error
 * after this it does something and waits for an rxrdy interrupt

 shows the results of:
 * ACTS (/CTS)  ADSR (/DSR)  GND    GND    ATTR3  ATTR2  ATTR1  ATTR0
 * d7           d6           d5     d4     d3     d2     d1     d0
 * the ACTS (inverse of DCTS) signal lives in one of these bits (see 5-62)
 * it XORs the read of systat_b with the E register (which holds 0x6)
 * and checks the result
 * The 4 attribute ram bits for the cell being pointed at by the X and Y regs are readable as the low nybble.
 * The DSR pin is readable as bit 6.
 */
READ8_MEMBER(vk100_state::SYSTAT_B)
{
#ifdef SYSTAT_B_VERBOSE
	logerror("0x%04X: SYSTAT_B Read!\n", m_maincpu->pc());
#endif
	return (m_ACTS<<7)|(m_ADSR<<6)|vram_attr_read();
}

READ8_MEMBER(vk100_state::vk100_keyboard_column_r)
{
	UINT8 code = m_col_array[offset&0xF]->read() | m_capsshift->read();
#ifdef KBD_VERBOSE
	logerror("Keyboard column %X read, returning %02X\n", offset&0xF, code);
#endif
	return code;
}

static ADDRESS_MAP_START(vk100_mem, AS_PROGRAM, 8, vk100_state)
	ADDRESS_MAP_UNMAP_HIGH
	AM_RANGE( 0x0000, 0x6fff ) AM_ROM
	AM_RANGE( 0x7000, 0x700f ) AM_MIRROR(0x0ff0) AM_READ(vk100_keyboard_column_r)
	AM_RANGE( 0x8000, 0xbfff ) AM_RAM
ADDRESS_MAP_END

/*
 * 8085 IO address map (x = ignored; * = selects address within this range; ? = not sure; ** = subparts check this bit)
 * (a15 to a8 are latched the same value as a7-a0 on the 8080 and 8085)
 * [this map is derived from extensive tracing as well as some guesswork, noted for the crtc, systat_b and the uart]
   a7  a6  a5  a4  a3  a2  a1  a0
   x   0   x   x   x   x   x   0     W     CRTC address
   x   0   x   x   x   x   x   1     RW    CRTC register r/w
   x   1   *   *   *   **  **  **        read area (rightmost 74ls138):
   x   1   0   0   0   *   *   *     R     SYSTAT_A (a0-a3 chooses the bit of the dipswitches read via d3)
   x   1   0   0   1   x   x   x    R     SYSTAT_B
   x   1   0   1   0   x   x   0     R     i8251 UART data
   x   1   0   1   0   x   x   1     R     i8251 UART status
   x   1   0   1   1   x   x   x     R     unused
   x   1   1   0   0   x   x   x     R     unused
   x   1   1   0   1   x   x   x     R     unused
   x   1   1   1   0   x   x   x     R     unused
   x   1   1   1   1   x   x   x     R     unused
   x   1   0   x   x   *   *   *         write area (right 74ls138):
   x   1   0   x   x   0   0   0     W     X (low 8 bits)
   x   1   0   x   x   0   0   1     W     X (high 4 bits)
   x   1   0   x   x   0   1   0     W     Y (low 8 bits)
   x   1   0   x   x   0   1   1     W     Y (high 4 bits)
   x   1   0   x   x   1   0   0     W     ERR
   x   1   0   x   x   1   0   1     W     SOPS
   x   1   0   x   x   1   1   0     W     PAT
   x   1   0   x   x   1   1   1     W     PMUL
   x   1   1   *   *   *   **  **        write area (middle 74ls138):
   x   1   1   0   0   0   **  **          write to register file 2x 74ls670:
   x   1   1   0   0   0   0   0     W       DU
   x   1   1   0   0   0   0   1     W       DVM
   x   1   1   0   0   0   1   0     W       DIR
   x   1   1   0   0   0   1   1     W       WOPS
   x   1   1   0   0   1   *   *     W     set VG_MODE to * * XOR 3 and Execute (if GO is not active)
   x   1   1   0   1   0   x   x     W     KYBDW
   x   1   1   0   1   1   x   x     W     BAUD
   x   1   1   1   0   0   x   0     W     i8251 UART data
   x   1   1   1   0   0   x   1     W     i8251 UART control
   x   1   1   1   0   1   x   x     W     unused
   x   1   1   1   1   0   x   x     W     unused
   x   1   1   1   1   1   x   x     W     unused
*/
static ADDRESS_MAP_START(vk100_io, AS_IO, 8, vk100_state)
	ADDRESS_MAP_UNMAP_HIGH
	ADDRESS_MAP_GLOBAL_MASK(0x7f) // guess, probably correct
	AM_RANGE(0x00, 0x00) AM_MIRROR(0xBE) AM_DEVWRITE("crtc", mc6845_device, address_w)
	AM_RANGE(0x01, 0x01) AM_MIRROR(0xBE) AM_DEVREADWRITE("crtc", mc6845_device, register_r, register_w)
	// Comments are from page 118 (5-14) of http://web.archive.org/web/20091015205827/http://www.computer.museum.uq.edu.au/pdf/EK-VK100-TM-001%20VK100%20Technical%20Manual.pdf
	AM_RANGE (0x40, 0x41) AM_MIRROR(0x98) AM_WRITE(vgLD_X)  //LD X LO + HI 12 bits
	AM_RANGE (0x42, 0x43) AM_MIRROR(0x98) AM_WRITE(vgLD_Y)  //LD Y LO + HI 12 bits
	AM_RANGE (0x44, 0x44) AM_MIRROR(0x98) AM_WRITE(vgERR)    //LD ERR ('error' in bresenham algorithm)
	AM_RANGE (0x45, 0x45) AM_MIRROR(0x98) AM_WRITE(vgSOPS)   //LD SOPS (screen options (plus uart dest))
	AM_RANGE (0x46, 0x46) AM_MIRROR(0x98) AM_WRITE(vgPAT)    //LD PAT (pattern register)
	AM_RANGE (0x47, 0x47) AM_MIRROR(0x98) AM_WRITE(vgPMUL)   //LD PMUL (pattern multiplier)
	AM_RANGE (0x60, 0x63) AM_MIRROR(0x80) AM_WRITE(vgREG)     //LD DU, DVM, DIR, WOPS (register file)
	AM_RANGE (0x64, 0x67) AM_MIRROR(0x80) AM_WRITE(vgEX)    //EX MOV, DOT, VEC, ER
	AM_RANGE (0x68, 0x68) AM_MIRROR(0x83) AM_WRITE(KBDW)   //KBDW (probably AM_MIRROR(0x03))
	AM_RANGE (0x6C, 0x6C) AM_MIRROR(0x83) AM_WRITE(BAUD)   //LD BAUD (baud rate clock divider setting for i8251 tx and rx clocks) (probably AM_MIRROR(0x03))
	AM_RANGE (0x70, 0x70) AM_MIRROR(0x82) AM_DEVWRITE("i8251", i8251_device, data_w) //LD COMD (i8251 data reg)
	AM_RANGE (0x71, 0x71) AM_MIRROR(0x82) AM_DEVWRITE("i8251", i8251_device, control_w) //LD COM (i8251 control reg)
	//AM_RANGE (0x74, 0x74) AM_MIRROR(0x83) AM_WRITE(unknown_74)
	//AM_RANGE (0x78, 0x78) AM_MIRROR(0x83) AM_WRITE(kbdw)   //KBDW ?(mirror?)
	//AM_RANGE (0x7C, 0x7C) AM_MIRROR(0x83) AM_WRITE(unknown_7C)
	AM_RANGE (0x40, 0x47) AM_MIRROR(0x80) AM_READ(SYSTAT_A) // SYSTAT A (state machine done and last 4 bits of vram, as well as dipswitches)
	AM_RANGE (0x48, 0x48) AM_MIRROR(0x87/*0x80*/) AM_READ(SYSTAT_B) // SYSTAT B (uart stuff)
	AM_RANGE (0x50, 0x50) AM_MIRROR(0x86) AM_DEVREAD("i8251", i8251_device, data_r) // UART O
	AM_RANGE (0x51, 0x51) AM_MIRROR(0x86) AM_DEVREAD("i8251", i8251_device, status_r) // UAR
	//AM_RANGE (0x58, 0x58) AM_MIRROR(0x87) AM_READ(unknown_58)
	//AM_RANGE (0x60, 0x60) AM_MIRROR(0x87) AM_READ(unknown_60)
	//AM_RANGE (0x68, 0x68) AM_MIRROR(0x87) AM_READ(unknown_68) // NOT USED
	//AM_RANGE (0x70, 0x70) AM_MIRROR(0x87) AM_READ(unknown_70)
	//AM_RANGE (0x78, 0x7f) AM_MIRROR(0x87) AM_READ(unknown_78)
ADDRESS_MAP_END

/* Input ports */
static INPUT_PORTS_START( vk100 )
	// the dipswitches are common ground: when open (upward) the lines are pulled to 5v, otherwise they read as 0
	PORT_START("SWITCHES")
		PORT_DIPNAME( 0x01, 0x00, "Power Frequency" )           PORT_DIPLOCATION("SW:!1")
		PORT_DIPSETTING( 0x00, "60Hz" )
		PORT_DIPSETTING( 0x01, "50Hz" )
		PORT_DIPNAME( 0x02, 0x00, "Default Serial Port" )           PORT_DIPLOCATION("SW:!2")
		PORT_DIPSETTING( 0x00, "EIA port" )
		PORT_DIPSETTING( 0x02, "20ma port" )
		PORT_DIPNAME( 0x04, 0x00, "Default US/UK" )         PORT_DIPLOCATION("SW:!3")
		PORT_DIPSETTING( 0x00, "US" )
		PORT_DIPSETTING( 0x04, "UK" )
		PORT_DIPNAME( 0x18, 0x00, "Default Parity" )            PORT_DIPLOCATION("SW:!4,!5")
		PORT_DIPSETTING( 0x00, "Off" )
		PORT_DIPSETTING( 0x10, "Even" )
		PORT_DIPSETTING( 0x08, "Odd" )
		PORT_DIPSETTING( 0x18, "Do Not Use This Setting" )
		PORT_DIPNAME( 0xe0, 0x00, "Default Baud Rate" )         PORT_DIPLOCATION("SW:!6,!7,!8")
		PORT_DIPSETTING( 0x00, "110" )
		PORT_DIPSETTING( 0x80, "300" )
		PORT_DIPSETTING( 0x40, "600" )
		PORT_DIPSETTING( 0xc0, "1200" )
		PORT_DIPSETTING( 0x20, "2400" )
		PORT_DIPSETTING( 0xa0, "4800" )
		PORT_DIPSETTING( 0x60, "9600" )
		PORT_DIPSETTING( 0xe0, "19200" )

	PORT_START("CAPSSHIFT") // CAPS LOCK and SHIFT appear as the high 2 bits on all rows
		PORT_BIT(0x3f, IP_ACTIVE_HIGH, IPT_UNUSED)
		PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Shift") PORT_CODE(KEYCODE_LSHIFT) PORT_CODE(KEYCODE_RSHIFT)
		PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_TOGGLE PORT_NAME("Caps lock") PORT_CODE(KEYCODE_CAPSLOCK)
	PORT_START("COL0")
		PORT_BIT(0x1f, IP_ACTIVE_LOW, IPT_UNUSED)
		PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_UNUSED) // row 0 bit 6 is always low, checked by keyboard test
		PORT_BIT(0xc0, IP_ACTIVE_HIGH, IPT_UNUSED) // all rows have these bits left low to save a mask op later
	PORT_START("COL1")
		PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Set Up") PORT_CODE(KEYCODE_F5)
		PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Esc") PORT_CODE(KEYCODE_ESC)
		PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Tab") PORT_CODE(KEYCODE_TAB)
		PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_UNUSED)
		PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Num Enter") PORT_CODE(KEYCODE_ENTER_PAD)
		PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("PF1/Hardcopy") PORT_CODE(KEYCODE_F1)
		PORT_BIT(0xc0, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_START("COL2")
		PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("1") PORT_CODE(KEYCODE_1)
		PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Q") PORT_CODE(KEYCODE_Q)
		PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("A") PORT_CODE(KEYCODE_A)
		PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("No scroll") PORT_CODE(KEYCODE_LALT)
		PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Num 1") PORT_CODE(KEYCODE_1_PAD)
		PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("PF2/Locator") PORT_CODE(KEYCODE_F2)
		PORT_BIT(0xc0, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_START("COL3")
		PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("2") PORT_CODE(KEYCODE_2)
		PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("W") PORT_CODE(KEYCODE_W)
		PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("S") PORT_CODE(KEYCODE_S)
		PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Z") PORT_CODE(KEYCODE_Z)
		PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Num 2") PORT_CODE(KEYCODE_2_PAD)
		PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("PF3/Text") PORT_CODE(KEYCODE_F3)
		PORT_BIT(0xc0, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_START("COL4")
		PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("3") PORT_CODE(KEYCODE_3)
		PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("E") PORT_CODE(KEYCODE_E)
		PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("D") PORT_CODE(KEYCODE_D)
		PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("X") PORT_CODE(KEYCODE_X)
		PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Num 3") PORT_CODE(KEYCODE_3_PAD)
		PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("PF4/Reset") PORT_CODE(KEYCODE_F4)
		PORT_BIT(0xc0, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_START("COL5")
		PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("4") PORT_CODE(KEYCODE_4)
		PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("R") PORT_CODE(KEYCODE_R)
		PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("F") PORT_CODE(KEYCODE_F)
		PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("C") PORT_CODE(KEYCODE_C)
		PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Num 4") PORT_CODE(KEYCODE_4_PAD)
		PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Up") PORT_CODE(KEYCODE_UP)
		PORT_BIT(0xc0, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_START("COL6")
		PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("5") PORT_CODE(KEYCODE_5)
		PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("T") PORT_CODE(KEYCODE_T)
		PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("G") PORT_CODE(KEYCODE_G)
		PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("V") PORT_CODE(KEYCODE_V)
		PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Num 5") PORT_CODE(KEYCODE_5_PAD)
		PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Down") PORT_CODE(KEYCODE_DOWN)
		PORT_BIT(0xc0, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_START("COL7")
		PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("6") PORT_CODE(KEYCODE_6)
		PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Y") PORT_CODE(KEYCODE_Y)
		PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("H") PORT_CODE(KEYCODE_H)
		PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("B") PORT_CODE(KEYCODE_B)
		PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Num 6") PORT_CODE(KEYCODE_6_PAD)
		PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Left") PORT_CODE(KEYCODE_LEFT)
		PORT_BIT(0xc0, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_START("COL8")
		PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("7") PORT_CODE(KEYCODE_7)
		PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("U") PORT_CODE(KEYCODE_U)
		PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("J") PORT_CODE(KEYCODE_J)
		PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Space") PORT_CODE(KEYCODE_SPACE)
		PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Num 7") PORT_CODE(KEYCODE_7_PAD)
		PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Right") PORT_CODE(KEYCODE_RIGHT)
		PORT_BIT(0xc0, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_START("COL9")
		PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("8") PORT_CODE(KEYCODE_8)
		PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("I") PORT_CODE(KEYCODE_I)
		PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("K") PORT_CODE(KEYCODE_K)
		PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("N") PORT_CODE(KEYCODE_N)
		PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Num 8") PORT_CODE(KEYCODE_8_PAD)
		PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_UNUSED)
		PORT_BIT(0xc0, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_START("COLA")
		PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("9") PORT_CODE(KEYCODE_9)
		PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("O") PORT_CODE(KEYCODE_O)
		PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("L") PORT_CODE(KEYCODE_L)
		PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("M") PORT_CODE(KEYCODE_M)
		PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Num 9") PORT_CODE(KEYCODE_9_PAD)
		PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_UNUSED)
		PORT_BIT(0xc0, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_START("COLB")
		PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("0") PORT_CODE(KEYCODE_0)
		PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("P") PORT_CODE(KEYCODE_P)
		PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(";") PORT_CODE(KEYCODE_COLON)
		PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(",") PORT_CODE(KEYCODE_COMMA)
		PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Num 0") PORT_CODE(KEYCODE_0_PAD)
		PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_UNUSED)
		PORT_BIT(0xc0, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_START("COLC")
		PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("-") PORT_CODE(KEYCODE_MINUS)
		PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("[") PORT_CODE(KEYCODE_OPENBRACE)
		PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("'") PORT_CODE(KEYCODE_QUOTE)
		PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(".") PORT_CODE(KEYCODE_STOP)
		PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Num -") PORT_CODE(KEYCODE_MINUS_PAD)
		PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_UNUSED)
		PORT_BIT(0xc0, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_START("COLD")
		PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("=") PORT_CODE(KEYCODE_EQUALS)
		PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("]") PORT_CODE(KEYCODE_CLOSEBRACE)
		PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_UNUSED)
		PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("/") PORT_CODE(KEYCODE_SLASH)
		PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Num ,") PORT_CODE(KEYCODE_PLUS_PAD)
		PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_UNUSED)
		PORT_BIT(0xc0, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_START("COLE")
		PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("~") PORT_CODE(KEYCODE_TILDE)
		PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Delete") PORT_CODE(KEYCODE_DEL)
		PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("\\") PORT_CODE(KEYCODE_BACKSLASH)
		PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Line feed") PORT_CODE(KEYCODE_RALT)
		PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Num .") PORT_CODE(KEYCODE_DEL_PAD)
		PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_UNUSED)
		PORT_BIT(0xc0, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_START("COLF")
		PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Backspace") PORT_CODE(KEYCODE_BACKSPACE)
		PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Break") PORT_CODE(KEYCODE_F6)
		PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Return") PORT_CODE(KEYCODE_ENTER)
		PORT_BIT(0x18, IP_ACTIVE_LOW, IPT_UNUSED)
		PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Ctrl") PORT_CODE(KEYCODE_LCONTROL) PORT_CODE(KEYCODE_RCONTROL)
		PORT_BIT(0xc0, IP_ACTIVE_HIGH, IPT_UNUSED)
INPUT_PORTS_END


void vk100_state::machine_start()
{
	m_speaker->set_frequency(116); //116 hz (page 172 of TM), but duty cycle is wrong here!
	output_set_value("online_led",1);
	output_set_value("local_led", 0);
	output_set_value("noscroll_led",1);
	output_set_value("basic_led", 1);
	output_set_value("hardcopy_led", 1);
	output_set_value("l1_led", 1);
	output_set_value("l2_led", 1);
	m_vsync = 0;
	m_dir_a6 = 1;
	m_cout = 0;
	m_vgX = 0;
	m_vgY = 0;
	m_vgERR = 0;
	m_vgSOPS = 0;
	m_vgPAT = 0;
	m_vgPAT_Mask = 0x80;
	m_vgPMUL = 0;
	m_vgPMUL_Count = 0;
	m_vgDownCount = 0;
	VG_DU = 0;
	VG_DVM = 0;
	VG_DIR = 0;
	VG_WOPS = 0;
	m_VG_MODE = 0;
	m_vgGO = 0;
	m_ACTS = 1;
	m_ADSR = 1;
	char kbdcol[8];
	// look up all 16 tags 'the slow way' but only once on reset
	for (int i = 0; i < 16; i++)
	{
		sprintf(kbdcol,"COL%X", i);
		m_col_array[i] = ioport(kbdcol);
	}
}

WRITE_LINE_MEMBER(vk100_state::crtc_vsync)
{
	m_maincpu->set_input_line(I8085_RST75_LINE, state? ASSERT_LINE : CLEAR_LINE);
	m_vsync = state;
}

WRITE_LINE_MEMBER(vk100_state::i8251_rxrdy_int)
{
	m_maincpu->set_input_line(I8085_RST65_LINE, state?ASSERT_LINE:CLEAR_LINE);
}

WRITE_LINE_MEMBER(vk100_state::i8251_txrdy_int)
{
	m_maincpu->set_input_line(I8085_RST55_LINE, state?ASSERT_LINE:CLEAR_LINE);
}

WRITE_LINE_MEMBER(vk100_state::i8251_rts)
{
	logerror("callback: RTS state changed to %d\n", state);
	// TODO: only change this during loopback mode!
	m_ACTS = state;
}

DRIVER_INIT_MEMBER(vk100_state,vk100)
{
	// figure out how the heck to initialize the timers here
	//m_i8251_rx_timer = timer_alloc(TID_I8251_RX);
	//m_i8251_tx_timer = timer_alloc(TID_I8251_TX);
	//m_i8251_sync_timer = timer_alloc(TID_SYNC);
	//machine().scheduler().timer_set(attotime::from_hz(10000), FUNC(i8251_rx_clk));
}

void vk100_state::video_start()
{
	m_vram = memregion("vram")->base();
	m_trans = memregion("trans")->base();
	m_pattern = memregion("pattern")->base();
	m_dir = memregion("dir")->base();
	m_sync = memregion("sync")->base();
	m_vector = memregion("vector")->base();
	m_ras_erase = memregion("ras_erase")->base();
}

MC6845_UPDATE_ROW( vk100_state::crtc_update_row )
{
	static const UINT32 colorTable[16] = {
	0x000000, 0x0000FF, 0xFF0000, 0xFF00FF, 0x00FF00, 0x00FFFF, 0xFFFF00, 0xFFFFFF,
	0x000000, 0x0000FF, 0xFF0000, 0xFF00FF, 0x00FF00, 0x00FFFF, 0xFFFF00, 0xFFFFFF };
	static const UINT32 colorTable2[16] = {
	0x000000, 0x0000FF, 0xFF0000, 0xFF00FF, 0x00FF00, 0x00FFFF, 0xFFFF00, 0xFFFFFF,
	0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000 };
	//printf("y=%d, ma=%04x, ra=%02x, x_count=%02x ", y, ma, ra, x_count);
	/* figure out ram address based on tech manual page 5-23:
	 * real address to 16-bit chunk a13  a12  a11 a10 a9  a8  a7  a6  a5  a4  a3  a2  a1  a0
	 * crtc input                  MA11 MA10 MA9 MA8 MA7 MA6 RA1 RA0 MA5 MA4 MA3 MA2 MA1 MA0
	 */
	UINT16 EA = ((ma&0xfc0)<<2)|((ra&0x3)<<6)|(ma&0x3F);
	// display the 64 different 12-bit-wide chunks
	for (int i = 0; i < 64; i++)
	{
		UINT16 block = m_vram[(EA<<1)+(2*i)+1] | (m_vram[(EA<<1)+(2*i)]<<8);
		UINT32 fgColor = (m_vgSOPS&0x08)?colorTable[(block&0xF000)>>12]:colorTable2[(block&0xF000)>>12];
		UINT32 bgColor = (m_vgSOPS&0x08)?colorTable[(m_vgSOPS&0xF0)>>4]:colorTable2[(m_vgSOPS&0xF0)>>4];
		// display a 12-bit wide chunk
		for (int j = 0; j < 12; j++)
		{
			bitmap.pix32(y, (12*i)+j) = (((block&(0x0001<<j))?1:0)^(m_vgSOPS&1))?fgColor:bgColor;
		}
	}
}


static MACHINE_CONFIG_START( vk100, vk100_state )
	/* basic machine hardware */
	MCFG_CPU_ADD("maincpu", I8085A, XTAL_5_0688MHz)
	MCFG_CPU_PROGRAM_MAP(vk100_mem)
	MCFG_CPU_IO_MAP(vk100_io)

	/* video hardware */
	MCFG_SCREEN_ADD("screen", RASTER)
	MCFG_SCREEN_RAW_PARAMS(XTAL_45_6192Mhz/3, 882, 0, 720, 370, 0, 350 ) // fake screen timings for startup until 6845 sets real ones
	MCFG_SCREEN_UPDATE_DEVICE( "crtc", mc6845_device, screen_update )

	MCFG_MC6845_ADD( "crtc", H46505, "screen", XTAL_45_6192Mhz/3/12)
	MCFG_MC6845_SHOW_BORDER_AREA(false)
	MCFG_MC6845_CHAR_WIDTH(12)
	MCFG_MC6845_UPDATE_ROW_CB(vk100_state, crtc_update_row)
	MCFG_MC6845_OUT_VSYNC_CB(WRITELINE(vk100_state, crtc_vsync))

	/* i8251 uart */
	MCFG_DEVICE_ADD("i8251", I8251, 0)
	MCFG_I8251_TXD_HANDLER(DEVWRITELINE(RS232_TAG, rs232_port_device, write_txd))
	MCFG_I8251_DTR_HANDLER(DEVWRITELINE(RS232_TAG, rs232_port_device, write_dtr))
	MCFG_I8251_RTS_HANDLER(DEVWRITELINE(RS232_TAG, rs232_port_device, write_rts))
	MCFG_I8251_RXRDY_HANDLER(WRITELINE(vk100_state, i8251_rxrdy_int))
	MCFG_I8251_TXRDY_HANDLER(WRITELINE(vk100_state, i8251_txrdy_int))

	MCFG_RS232_PORT_ADD(RS232_TAG, default_rs232_devices, NULL)
	MCFG_RS232_RXD_HANDLER(DEVWRITELINE("i8251", i8251_device, write_rxd))
	MCFG_RS232_DSR_HANDLER(DEVWRITELINE("i8251", i8251_device, write_dsr))

	MCFG_DEVICE_ADD(COM5016T_TAG, COM8116, XTAL_5_0688MHz)
	MCFG_COM8116_FR_HANDLER(DEVWRITELINE("i8251", i8251_device, write_rxc))
	MCFG_COM8116_FT_HANDLER(DEVWRITELINE("i8251", i8251_device, write_txc))

	MCFG_DEFAULT_LAYOUT( layout_vk100 )

	MCFG_SPEAKER_STANDARD_MONO( "mono" )
	MCFG_SOUND_ADD( "beeper", BEEP, 0 )
	MCFG_SOUND_ROUTE( ALL_OUTPUTS, "mono", 0.25 )
MACHINE_CONFIG_END

/* ROM definition */
/* according to http://www.computer.museum.uq.edu.au/pdf/EK-VK100-TM-001%20VK100%20Technical%20Manual.pdf
page 5-10 (pdf pg 114), The 4 firmware roms should go from 0x0000-0x1fff,
0x2000-0x3fff, 0x4000-0x5fff and 0x6000-0x63ff; The last rom is actually a
little bit longer and goes to 67ff.
*/
ROM_START( vk100 )
	ROM_REGION( 0x10000, "maincpu", ROMREGION_ERASEFF )
	ROM_LOAD( "23-031e4-00.rom1.e53", 0x0000, 0x2000, CRC(c8596398) SHA1(a8dc833dcdfb7550c030ac3d4143e266b1eab03a))
	ROM_LOAD( "23-017e4-00.rom2.e52", 0x2000, 0x2000, CRC(e857a01e) SHA1(914b2c51c43d0d181ffb74e3ea59d74e70ab0813))
	ROM_LOAD( "23-018e4-00.rom3.e51", 0x4000, 0x2000, CRC(b3e7903b) SHA1(8ad6ed25cd9b04a9968aa09ab69ba526d35ca550))
	ROM_LOAD( "23-190e2-00.rom4.e50", 0x6000, 0x1000, CRC(ad596fa5) SHA1(b30a24155640d32c1b47a3a16ea33cd8df2624f6)) // probably an overdump, e2 implies the size is 0x800 not 0x1000, and the end is all blank

	ROM_REGION( 0x8000, "vram", ROMREGION_ERASE00 ) // 32k of vram

	ROM_REGION( 0x400, "pattern", ROMREGION_ERASEFF )
	/* This is the "PATTERN ROM", (1k*4, 82s137)
	 * it contains a table of 4 output bits based on input of: (from figure 5-18):
	 * 4 input bits bank-selected from RAM (a0, a1, a2, a3)
	 * 2 select bits to choose one of the 4 bits, sourced from the X reg LSBs (a4, a5)
	 * 3 pattern function bits from WOPS (F0 "N" is a6, F1 and F2 are a7 and a8
	 * and one bit from the lsb of the pattern register shifter (a9)
	 * control bits:
	 *            /CE1 ----- GND [verified via tracing]
	 *            /CE2 ----- GND [verified via tracing]
	 * addr bits 9876543210
	 *           ||||||\\\\- input from ram (A)
	 *           ||||\\----- bit select (from x reg lsb)
	 *           |||\------- negate (N) \___ low 3 bits of WOPS
	 *           |\\-------- function   /
	 *           \---------- pattern bit (P)
	 * data bits: 3210
	 *            \\\\-- output to ram (M), but gated by an io line from vector rom
	 *    functions are:
	 *    Overlay: M=A|(P^N)
	 *    Replace: M=P^N
	 *    Complement: M=A^(P^N)
	 *    Erase: M=N
	 */
	ROM_LOAD( "wb8201_656f1.m1-7643-5.pr4.ic14", 0x0000, 0x0400, CRC(e8ecf59f) SHA1(49e9d109dad3d203d45471a3f4ca4985d556161f)) // label verified from nigwil's board

	ROM_REGION(0x100, "trans", ROMREGION_ERASEFF )
	/* this is the "TRANSLATOR ROM" described in figure 5-17 on page 5-27 (256*8, 82s135)
	 * it contains a table of 256 values which skips every fourth value so 00 01 02 04 05 06 08.. etc, wraps at the end
	 * control bits:
	 *            /CE1 ----- GND [verified via tracing]
	 *            /CE2 ----- GND [verified via tracing]
	 * addr bits: 76543210
	 *            \\\\\\\\- X9 thru X2
	 * data bits: 76543210
	 *            \\\\\\\\- X'9 thru X'2
	 * The VT125 prom @ E60 is literally identical to this, the same exact part: 23-060B1
	 */
	ROM_LOAD( "wb---0_060b1.mmi6309.pr2.ic82", 0x0000, 0x0100, CRC(198317fc) SHA1(00e97104952b3fbe03a4f18d800d608b837d10ae)) // label verified from nigwil's board

	ROM_REGION(0x100, "dir", ROMREGION_ERASEFF )
		/* this is the "DIRECTION ROM"  == mb6309 (256x8, 82s135)
		* see figure 5-24 on page 5-39
		* It tells the direction and enable for counting on the X and Y counters
		* and also handles the non-math related parts of the bresenham line algorithm
		* control bits:
		*            /CE1 ----- DCOUNT 0 H [verified via tracing]
		*            /CE2 ----- ENA ERROR L [verified via tracing]
		* addr bits: 76543210
		*            ||||\\\\-- DIR (vgDIR register low 4 bits)
		*            |||\------ C OUT aka ERROR CARRY (strobed in by STROBE L from the error counter's adder) [verified via tracing]
		*            ||\------- Y0 (the otherwise unused lsb of the Y register, used for bresenham) [verified via tracing]
		*            |\-------- feedback bit from d5 strobed by V CLK [verified via tracing]
		*            \--------- GND; the second half of the prom is blank (0x00)
		* data bits: 76543210
		*            |||||||\-- ENA Y (enables change on X counter) [works with code]
		*            ||||||\--- ENA X (enables change on Y counter) [works with code]
		*            |||||\---- Y DIRECTION (high is count down, low is count up)
		*            ||||\----- X DIRECTION (high is count down, low is count up)
		*            |||\------ PIXEL WRT
		*            ||\------- feedback bit to a6, this bit is held in PRESET/1 condition by GO being inactive, and if the vector prom is disabled it is pulled to 1 [verified via tracing and schematics]
		*            |\-------- UNUSED, always 0
		*            \--------- UNUSED, always 0
		* The VT125 prom @ E41 is literally identical to this, the same exact part: 23-059B1
		*/
	ROM_LOAD( "wb8141_059b1.tbp18s22.pr5.ic111", 0x0000, 0x0100, CRC(4b63857a) SHA1(3217247d983521f0b0499b5c4ef6b5de9844c465))  // label verified from andy's board

	ROM_REGION( 0x100, "ras_erase", ROMREGION_ERASEFF )
	/* this is the "RAS/ERASE ROM" involved with driving the RAS lines and erasing VRAM dram (256*4, 82s129)
	 * control bits:
	 *            /CE1 ----- /WRITE aka WRITE L (pin 6 of vector rom after being latched by its ls273) [verified via tracing and vt125 schematic]
	 *            /CE2 ----- /ENA WRITE aka ENA WRITE L [verified via tracing and vt125 schematic]
	 *                       (INHIBIT WRITE L (pin 5 of ls74 to extreme left edge of translate prom) XOR STROBE D COUNT (pin 5 of ls74 near the 20ma port) (pin 3 of the ls86 above the crtc)
	 * addr bits: 76543210
	 *            |||||||\-- X'2 [verified via tracing]
	 *            ||||||\--- X'3 [verified via tracing]
	 *            |||||\---- register file bit 3/upper file MSB (DIR prom pin 4, ls191 2nd from left edge left of the hd46505 pin 9, upper ls670n pin 6, ls283 at the left edge left of the hd46505 pin 15) [verified via tracing]
	 *            ||||\----- (Y8 NOR !(X10 NAND X9)) (pins 4,5,6 of ls32 left of 8085, and pins 1,2 of the ls04 in the lower left corner, pins 10,9,8 of the ls00 at the left edge drawn from the ls74 between the 8251 and 8202) [verified via tracing]
	 *            |||\------ (X10 NOR Y10) (pins 10,9,8 of ls32 left of 8085) [verified via tracing]
	 *            ||\------- X11 (D out of ls191 left of ls191 left of hd46505) [verified via tracing]
	 *            |\-------- Y11 (D out of ls191 left of hd46505) [verified via tracing]
	 *            \--------- ERASE L/d5 on the vector rom [verified via tracing]
	 * data bits: 3210
	 *            |||\-- /WE for VRAM Attribute bits
	 *            ||\--- /WE for VRAM bits 0-3 (leftmost bits, first to be shifted out)
	 *            |\---- /WE for VRAM bits 4-7
	 *            \----- /WE for VRAM bits 8-11 (rightmost bits, last to be shifted out)
	 * The VT125 prom E93 is mostly equivalent to the ras/erase prom; On the vt125 version, the inputs are:
	 *  (X'10 NOR X'11)
	 *  (Y9 NOR Y10)
	 *  Y11
	 *  X8 (aka PX8)
	 *  X9 (aka PX9)
	 *  ERASE L
	 *  X'3
	 *  X'2
	 * and the outputs are:
	 *  MWR 2
	 *  MWR 1
	 *  MWR 0
	 * since the vt125 has ram laid out differently than the vk100 and
	   only has 3 banks (but 2 planes of 3 banks), I (LN) assume there
	   are four wr banks on the v100, one per nybble, and the X'3 and
	   X'2 inputs lend credence to this.
	 *
	 */
	ROM_LOAD( "wb8151_573a2.mmi6301.pr3.ic41", 0x0000, 0x0100, CRC(75885a9f) SHA1(c721dad6a69c291dd86dad102ed3a8ddd620ecc4)) // Stamp/silkscreen: "WB8151 // 573A2" (23-573A2), 82S129 equivalent @ E41
	// label verified from nigwil's and andy's board

	ROM_REGION( 0x100, "vector", ROMREGION_ERASEFF )
	// WARNING: it is possible that the first two bytes of this prom are bad!
	// The PROM on andy's board appears to be damaged, this will need to be redumped from another board.
	/* this is the "VECTOR ROM" (256*8, 82s135) which runs the vector generator state machine
	 * the vector rom bits are complex and are unfortunately poorly documented
	 * in the tech manual. see figure 5-23.
	 * control bits:
	 *            /CE1 ----- /GO aka GO L [verified via tracing]
	 *            /CE2 ----- GND [verified via tracing]
	 * addr bits: 76543210
	 *            ||||\\\\-- To sync counter, which counts 0xC 0x3 0x2 0x1 0x0 0x5 0x4 0xB 0xA 0x9 0x8 0xD in that order
	 *            |||\------ (MODE 0 NAND GO) \_This effectively means when GO is low, these bits are both 1, when GO is high, these bits are the inverse of the MODE bits; this is DIFFERENT from the vt125, and was probably removed as unnecessary given that /GO is an enable for the vector rom anyway [verified via tracing]
	 *            ||\------- (MODE 1 NAND GO) /
	 *            |\-------- ? C OUT (when set, only one /LD ERROR pulse occurs instead of two)
	 *            \--------- ? FINISH (/LD ERROR only goes active (low) when this is unset)
	 *
	 * data bits: 76543210
	 *            |||||||\-- /WRITE aka WRITE L (fig 5-20, page 5-32, writes the post-pattern-converted value back to vram at X,Y)
	 *            ||||||\--- DONE L [verified via tracing]
	 *            |||||\---- VECTOR CLK aka V CLK [verified via tracing]
	 *            ||||\----- /LD ERROR aka STROBE ERROR L (strobes the adder result value into the vgERR register)
	 *            |||\------ D LOAD [by process of elimination and limited tracing]
	 *            ||\------- ERASE L (latched, forces a4 on the sync rom low and also forces a7 on the ras/erase rom; the counter rom may be involved in blanking all of vram) [verified via tracing]
	 *            |\-------- C0 aka C IN (high during DVM read, low otherwise, a carry in to the adder so DVM is converted from 1s to 2s complement)
	 *            \--------- SHIFT ENA [verified via tracing]
	 * The data bits all have pull-ups to +5v if the /CE1 pin is not low
	 * According to the VT125 tech manual (vt100 tech manual rev 3, page 6-85) the 8 signals here are:
	 * ERASE L - d5
	 * SHIFT ENA - d7
	 * C IN - d6
	 * D LOAD - d4
	 * WRITE L - d0
	 * DONE L - d1
	 * VECTOR CLK - d2
	 * STROBE ERROR L - d3
	 *
	 * The VT125 prom E71 and its latch E70 is mostly equivalent to the vector prom, but the address order is different
	 */
	ROM_LOAD( "wb8146_058b1.mmi6309.pr1.ic99", 0x0000, 0x0100, BAD_DUMP CRC(71b01864) SHA1(e552f5b0bc3f443299282b1da7e9dbfec60e12bf))  // label verified from nigwil's and andy's board

	ROM_REGION( 0x20, "sync", ROMREGION_ERASEFF )
	/* this is the "SYNC ROM" == mb6331 (32x8, 82s123)
	 * It generates the ram RAS/CAS and a few other signals, see figure 5-20 on page 5-32
	 * The exact pins for each signal are not documented.
	 * control bits:
	 *            /CE1 -- GND(Unused)
	 * addr bits: 43210
	 *            |\\\\-- To sync counter, which counts 0xC 0x3 0x2 0x1 0x0 0x5 0x4 0xB 0xA 0x9 0x8 0xD in that order
	 *            \------ comes from the gated ERASE L/d5 from the vector rom, only low during VG_MODE == ER (ERase Screen) [verified via tracing]
	 *                      when high: the sync rom matches figure 5-20 (page 5-32) and 5-23 (page 5-38)
	 *                      when low: RA/RB is fixed on WOPS in the register file
	 *                                LD SHFR does NOT output pulses (effectively blanking the screen)
	 *                                WRT/RD is held at /RD so vg to vram do nothing,
	 *                                however while the crtc is refreshing the screen it instead is writing zeroes to every location
	 * data bits: 76543210
	 *            |||||||\-- WRT/RD (write high when x,y (vg) drives vram, read low when ma (crtc) drives vram)
	 *            ||||||\--- /RAS (for vram)
	 *            |||||\---- STROBE aka STROBE L aka VG STROBE(latches the carry bit from the adder to the direction rom AND causes the data at the X,Y address of vram to be read into the holding register before pattern is applied)
	 *            ||||\----- LD SHFR (loads the 12-bit shift register of data to be shifted to screen)
	 *            |||\------ /CAS (for vram)
	 *            ||\------- RA\__selects which slot of the 8x4 register file (du, dvm, dir, or wops) is selected
	 *            |\-------- RB/
	 *            \--------- SYNC (latches the EXECUTE signal from an EXEC * write to activate the GO signal and enable the Vector rom) [verified via tracing]
	 * The VT125 proms E64/E66 and their respective latches E65 and E83 are mostly equivalent to the sync rom
	 */
	ROM_LOAD( "wb8014_297a1.74s288.pr6.ic89", 0x0000, 0x0020, CRC(e2f7c566) SHA1(a4c3dc5d07667141ad799168a862cb3c489b4934)) // label verified from nigwil's and andy's board
ROM_END

/* Driver */

/*    YEAR  NAME    PARENT  COMPAT   MACHINE    INPUT    INIT    COMPANY                       FULLNAME       FLAGS */
COMP( 1980, vk100,  0,      0,       vk100,     vk100, vk100_state,   vk100,  "Digital Equipment Corporation", "VK100 'GIGI'", MACHINE_NOT_WORKING | MACHINE_IMPERFECT_SOUND)