summaryrefslogtreecommitdiffstatshomepage
path: root/src/mame/drivers/notetaker.cpp
blob: 31f10ad463864c2810649d6bad11e679ea9808df (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
// license:BSD-3-Clause
// copyright-holders:Jonathan Gevaryahu
/* Xerox NoteTaker, 1978
 * Driver by Jonathan Gevaryahu

 *  Notetaker Team At Xerox PARC 1976-1981:
     Alan Kay - Team Lead
     Bruce Horn - BIOS code and NoteTaker Hardware/Electronics Design (Ethernet, others)
     Ted Kaehler - SmallTalk-76 code porting[2] and more ( http://tedkaehler.weather-dimensions.com/us/ted/index.html )
     Dan Ingalls - Later BitBlt engine and SmallTalk-76 kernel and more[3]
     Doug Fairbairn - NoteTaker Hardware/Electronics Design (EmulatorP, IOP, ADC, Alto Debug/Test Interface)
         ( http://www.computerhistory.org/atchm/author/dfairbairn/ )
     Ed Wakida - NoteTaker Hardware/Electronics Design (Tablet/Touch interface)
     Bob Nishimura - NoteTaker Hardware/Electronics Design (PSU/Cabling/Machining/Battery)
     James "Jim" Leung - NoteTaker Hardware/Electronics Design (Alto Debug/Test Interface)
     Ron Freeman - NoteTaker Hardware/Electronics Design (Keyboard, Disk/Display)
     Ben Sato - NoteTaker Hardware/Electronics Design (Memory/Timing, ECC)
     B. Wang - NoteTaker Hardware/Electronics Design (Keyboard)
     Larry Tesler - NoteTaker Hardware/Electronics Design (Ethernet, others)
     Dale Mann - NoteTaker Hardware/Electronics Design (Ethernet, Circuit Assembly)
     Lawrence "Larry" D. Stewart - NoteTaker Hardware/Electronics Design (Mouse)
     Jim Althoff - Smalltalk-78 and Smalltalk-80
     Adele Goldberg - Smalltalk team
     Diana Merry-Shapiro - Original BitBlt from Smalltalk-72
     Dave Robson - Smalltalk team
     Ted Strollo - IC/VLSI design (MPC580 cell library)
     Bert Sutherland - Manager of Systems Science Laboratory (SSL)
     Terri Doughty - Administration, Editing
     Chris Jeffers - Smalltalk music
     <there are probably others I've missed>

 * History of the machine can be found at http://freudenbergs.de/bert/publications/Ingalls-2014-Smalltalk78.pdf

 * The notetaker has an 8-slot backplane, with the following cards in it:
   * I/O Processor card (8086@8Mhz, 8259pic, 4k ROM, Keyboard UART, DAC1200 (multiplexed to 2 channels))
   * Emulation Processor card (8086@5Mhz, 8259pic, 8k of local RAM with Parity check logic)
   * Disk/Display card (WD1791 FDC, CRT5027 CRTC, EIA UART, AD571 ADC, 8->1 Analog Multiplexer)
   * Memory Control Module \_ (bus control, buffering, refresh, Parity/ECC/Syndrome logic lives on these boards)
   * Memory Data Module    /
   * Memory Storage Module x2 (the 4116 DRAMs live on these boards)
   * Battery Module *OR* debugger module type A or B (debugger module has an
     i8255 on it for alto<->notetaker comms, and allows alto to halt the cpus
     [type A and B can debug either the emulator cpu or the iop respectively]
     and dump registers to alto screen, etc)

   * In 1980-1981 an Ethernet card with another 8086 on it was developed, but
     it is unclear if this was ever fully functional, or if smalltalk-78
     could even use it.

 * Prototypes only, 10 units[2] manufactured 1978-1980
   Known surviving units:
   * One at CHM (missing? mouse, no media, has BIOP-2.0 roms)
   * One at Xerox Museum at PARC (with mouse and 2? floppies, floppies were not imaged to the best of my knowledge, unknown roms)
   * Rumor has it at least a few of the remaining units survived beyond these two.

 * The NoteTaker used the BitBlt graphical operation (from SmallTalk-76) to do most graphical functions, in order to fit the SmallTalk code and programs within 256K of RAM[2]. The actual BitBlt code lives in ROM[3].

 * As far as I am aware, no media (world disks/boot disks) for the NoteTaker have survived (except maybe the two disks at Xerox Museum at PARC), but an incomplete dump of the Smalltalk-76 'world' which was used to bootstrap Smalltalk-78 originally did survive on the Alto disks at CHM

 * We are missing the dump for the i8748 Keyboard MCU which does row-column scanning and mouse quadrature reading, and talks to the main system via serial

 * see http://bitsavers.informatik.uni-stuttgart.de/pdf/xerox/notetaker for additional information
 * see http://xeroxalto.computerhistory.org/Filene/Smalltalk-76/ for the smalltalk-76 dump
 * see http://xeroxalto.computerhistory.org/Indigo/BasicDisks/Smalltalk14.bfs!1_/ for more notetaker/smalltalk related files, including SmallTalk-80 files based on the notetaker smalltalk-78

 References:
 * [1] http://freudenbergs.de/bert/publications/Ingalls-2014-Smalltalk78.pdf
 * [2] "Smalltalk and Object Orientation: An Introduction" By John Hunt, pages 45-46 [ISBN 978-3-540-76115-0]
 * [3] http://bitsavers.trailing-edge.com/pdf/xerox/notetaker/memos/19790620_Z-IOP_1.5_ls.pdf
 * [4] http://xeroxalto.computerhistory.org/Filene/Smalltalk-76/
 * [5] http://bitsavers.trailing-edge.com/pdf/xerox/notetaker/memos/19790118_NoteTaker_System_Manual.pdf

TODO: everything below.
* Get smalltalk-78 loaded as a rom and forced into ram on startup, since no boot disks have survived (or if any survived, they are not dumped)
* Hack together a functional boot disk using the recovered EP bootloader and smalltalk engine code from the alto disks.
* Harris 6402 keyboard UART (within keyboard, next to MCU)
* The harris 6402 UART is pin compatible with WD TR1865 UART, as well as the AY-3-1015A/D
   (These are 5v-only versions of the older WD TR1602 and AY-5-1013 parts which used 5v and 12v)
* HLE for the missing i8748[5] MCU in the keyboard which reads the mouse quadratures and buttons and talks serially to the Keyboard UART
* floppy controller wd1791 and its interrupt
  According to [3] and [5] the format is double density/MFM, 128 bytes per sector, 16 sectors per track, 1 or 2 sided, for 170K or 340K per disk. Drive spins at 300RPM.
* hook up the DiskInt from the wd1791 either using m_fdc->intrq_r() polling or using device lines (latter is a much better idea)

WIP:
* crt5027 video controller - the iop side is hooked up, screen drawing 'works' but is scrambled due to not emulating the clock chain halting and clock changing yet. The crt5027 core also needs the odd/even interrupt hooked up, and proper interlace support as well as clock change/screen resize support (down to DC/no clock, which I guess should be a 1x1 single black pixel!)
* pic8259 interrupt controller - this is attached as a device, but only the vblank interrupt is hooked to it yet.
* Harris 6402 serial/EIA UART - connected to iop, other end isn't connected anywhere, interrupt is not connected
* Harris 6402 keyboard UART (within notetaker) - connected to iop, other end isn't connected anywhere, interrupt is not connected
* The DAC, its FIFO and its timer are hooked up and the v2.0 bios beeps, but the stereo latches are not hooked up at all, DAC is treated as mono for now

DONE:
* i/o cpu i/o area needs the memory map worked out per the schematics - done
* figure out the correct memory maps for the 256kB of shared ram, and what part of ram constitutes the framebuffer - done
  - 256k of shared ram maps at 00000-3ffff for both cpus with special mem regs at fffec,fffee. the ram mirrors 4 times on the emulatorcpu only, iop the 40000-fffff area is open bus.
  - framebuffer, at least for bios 1.5, lives from 0x4000-0xd5ff, exactly 640x480 pixels 1bpp, interlaced (even? plane is at 4000-8aff, odd? plane is at 8b00-d5ff); however the starting address of the framebuffer is configurable to any address within the 0x0000-0x1ffff range? (this exact range is unclear)
* figure out how the emulation-cpu boots and where its 8k of local ram maps to - done
  - both cpus boot, reset and system int controls are accessed at fffea from either cpu; emulatorcpu's 8k of ram lives at the beginning of its address space, but can be disabled in favor of mainram at the same addresses
* 82s147 DISKSEP PROM regenerated from original BCPL code
* SETMEMREQ PROM retyped from binary listing
* TIMING PROM retyped from binary listing
*/

#include "emu.h"
#include "cpu/i86/i86.h"
#include "imagedev/floppy.h"
#include "machine/ay31015.h"
#include "machine/clock.h"
#include "machine/pic8259.h"
#include "machine/wd_fdc.h"
#include "sound/dac.h"
#include "sound/volt_reg.h"
#include "video/tms9927.h"
#include "emupal.h"
#include "screen.h"
#include "speaker.h"


class notetaker_state : public driver_device
{
public:
	notetaker_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag) ,
		m_iop_cpu(*this, "iop_cpu"),
		m_iop_pic(*this, "iop_pic8259"),
		m_ep_cpu(*this, "ep_cpu"),
		m_ep_pic(*this, "ep_pic8259"),
		m_kbduart(*this, "kbduart"),
		m_eiauart(*this, "eiauart"),
		m_crtc(*this, "crt5027"),
		m_dac(*this, "dac"),
		m_fdc(*this, "wd1791"),
		m_floppy0(*this, "wd1791:0"),
		m_floppy(nullptr)
	{
	}

	void notetakr(machine_config &config);

	void init_notetakr();

private:
	enum
	{
		TIMER_FIFOCLK,
	};

// devices
	required_device<cpu_device> m_iop_cpu;
	required_device<pic8259_device> m_iop_pic;
	required_device<cpu_device> m_ep_cpu;
	required_device<pic8259_device> m_ep_pic;
	required_device<ay31015_device> m_kbduart;
	required_device<ay31015_device> m_eiauart;
	required_device<crt5027_device> m_crtc;
	required_device<dac_word_interface> m_dac;
	required_device<fd1791_device> m_fdc;
	required_device<floppy_connector> m_floppy0;
	floppy_image_device *m_floppy;

//declarations
	// screen
	uint32_t screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect);
	// basic io
	DECLARE_WRITE16_MEMBER(IPConReg_w);
	DECLARE_WRITE16_MEMBER(EPConReg_w);
	DECLARE_WRITE16_MEMBER(FIFOReg_w);
	DECLARE_WRITE16_MEMBER(FIFOBus_w);
	DECLARE_WRITE16_MEMBER(DiskReg_w);
	DECLARE_WRITE16_MEMBER(LoadDispAddr_w);

	// uarts
	DECLARE_READ16_MEMBER(ReadOPStatus_r);
	DECLARE_WRITE16_MEMBER(LoadKeyCtlReg_w);
	DECLARE_WRITE16_MEMBER(KeyDataReset_w);
	DECLARE_WRITE16_MEMBER(KeyChipReset_w);
	DECLARE_READ16_MEMBER(ReadEIAStatus_r);
	DECLARE_WRITE16_MEMBER(LoadEIACtlReg_w);
	DECLARE_WRITE16_MEMBER(EIADataReset_w);
	DECLARE_WRITE16_MEMBER(EIAChipReset_w);
	// mem map stuff
	DECLARE_READ16_MEMBER(iop_r);
	DECLARE_WRITE16_MEMBER(iop_w);
	//variables
	//  IPConReg
	uint8_t m_BootSeqDone;
	uint8_t m_ProcLock;
	uint8_t m_CharCtr;
	uint8_t m_DisableROM;
	uint8_t m_CorrOn_q;
	uint8_t m_LedInd6;
	uint8_t m_LedInd7;
	uint8_t m_LedInd8;
	//  FIFOReg
	uint8_t m_TabletYOn;
	uint8_t m_TabletXOn;
	uint8_t m_FrSel2;
	uint8_t m_FrSel1;
	uint8_t m_FrSel0;
	uint8_t m_SHConB;
	uint8_t m_SHConA;
	uint8_t m_SetSH;
	// DiskReg
	uint8_t m_ADCSpd0;
	uint8_t m_ADCSpd1;
	uint8_t m_StopWordClock_q;
	uint8_t m_ClrDiskCont_q;
	uint8_t m_ProgBitClk1;
	uint8_t m_ProgBitClk2;
	uint8_t m_ProgBitClk3;
	uint8_t m_AnSel4;
	uint8_t m_AnSel2;
	uint8_t m_AnSel1;
	uint8_t m_DriveSel1;
	uint8_t m_DriveSel2;
	uint8_t m_DriveSel3;
	uint8_t m_SideSelect;
	uint8_t m_Disk5VOn;
	uint8_t m_Disk12VOn;
	// output fifo, for DAC
	uint16_t m_outfifo[16]; // technically three 74LS225 5bit*16stage FIFO chips, arranged as a 16 stage, 12-bit wide fifo (one bit unused per chip)
	uint8_t m_outfifo_count;
	uint8_t m_outfifo_tail_ptr;
	uint8_t m_outfifo_head_ptr;
	// fifo timer
	emu_timer *m_FIFO_timer;
	TIMER_CALLBACK_MEMBER(timer_fifoclk);
	// framebuffer display starting address
	uint16_t m_DispAddr;

// separate cpu resets
	void iop_reset();
	void ep_reset();

// overrides
	virtual void machine_start() override;
	virtual void machine_reset() override;

	void iop_io(address_map &map);
	void iop_mem(address_map &map);
	void ep_io(address_map &map);
	void ep_mem(address_map &map);

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

void notetaker_state::device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr)
{
	switch (id)
	{
	case TIMER_FIFOCLK:
		timer_fifoclk(ptr, param);
		break;
	default:
		assert_always(false, "Unknown id in notetaker_state::device_timer");
	}
}

TIMER_CALLBACK_MEMBER(notetaker_state::timer_fifoclk)
{
	uint16_t data;
	//pop a value off the fifo and send it to the dac.
#ifdef FIFO_VERBOSE
	if (m_outfifo_count == 0) logerror("output fifo is EMPTY! repeating previous sample!\n");
#endif
	data = m_outfifo[m_outfifo_tail_ptr];
	// if fifo is empty (tail ptr == head ptr), do not increment the tail ptr, otherwise do.
	if (m_outfifo_count > 0)
	{
		m_outfifo_tail_ptr++;
		m_outfifo_count--;
	}
	m_outfifo_tail_ptr&=0xF;
	m_dac->write(data);
	m_FIFO_timer->adjust(attotime::from_hz(((960_kHz_XTAL/10)/4)/((m_FrSel0<<3)+(m_FrSel1<<2)+(m_FrSel2<<1)+1)));
}

uint32_t notetaker_state::screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect)
{
	// have to figure out what resolution we're drawing to here and draw appropriately to screen
	// code borrowed/stolen from video/mac.cpp
	uint32_t video_base;
	uint16_t word;
	uint16_t *line;
	int y, x, b;

	video_base = (m_DispAddr << 3)&0x1FFFF;
#ifdef DEBUG_VIDEO
	logerror("Video Base = 0x%05x\n", video_base);
#endif
	const uint16_t *video_ram_field1 = (uint16_t *)(memregion("mainram")->base()+video_base);
	const uint16_t *video_ram_field2 = (uint16_t *)(memregion("mainram")->base()+video_base+0x4B00);

	for (y = 0; y < 480; y++)
	{
		line = &bitmap.pix16(y);

		for (x = 0; x < 640; x += 16)
		{
			if ((y&1)==0) word = *(video_ram_field1++); else word = *(video_ram_field2++);
			for (b = 0; b < 16; b++)
			{
				line[x + b] = (word >> (15 - b)) & 0x0001;
			}
		}
	}
	return 0;
}

WRITE16_MEMBER(notetaker_state::IPConReg_w)
{
	m_BootSeqDone = (data&0x80)?1:0;
	m_ProcLock = (data&0x40)?1:0; // bus lock for this processor (hold other processor in wait state)
	m_CharCtr = (data&0x20)?1:0; // battery charge control (incorrectly called 'Char counter' in source code)
	m_DisableROM = (data&0x10)?1:0; // disable rom at 0000-0fff
	m_CorrOn_q = (data&0x08)?1:0; // CorrectionOn (ECC correction enabled); also LedInd5
	m_LedInd6 = (data&0x04)?1:0;
	m_LedInd7 = (data&0x02)?1:0;
	m_LedInd8 = (data&0x01)?1:0;
	popmessage("LEDS: CR1: %d, CR2: %d, CR3: %d, CR4: %d", (data&0x04)>>2, (data&0x08)>>3, (data&0x02)>>1, (data&0x01)); // cr1 and 2 are in the reverse order as expected, according to the schematic
}

/* handlers for the two system hd6402s (ay-5-1013 equivalent) */
/* * Keyboard hd6402 */
READ16_MEMBER( notetaker_state::ReadOPStatus_r ) // 74ls368 hex inverter at #l7 provides 4 bits, inverted
{
	uint16_t data = 0xFFF0;
	data |= (m_outfifo_count >= 1) ? 0 : 0x08; // m_FIFOOutRdy is true if the fifo has at least 1 word in it, false otherwise
	data |= (m_outfifo_count < 16) ? 0 : 0x04; // m_FIFOInRdy is true if the fifo has less than 16 words in it, false otherwise
	// note /SWE is permanently enabled, so we don't enable it here for HD6402 reading
	data |= m_kbduart->dav_r( ) ? 0 : 0x02; // DR - pin 19
	data |= m_kbduart->tbmt_r() ? 0 : 0x01; // TBRE - pin 22
#ifdef DEBUG_READOPSTATUS
	logerror("ReadOPStatus read, returning %04x\n", data);
#endif
	return data;
}

WRITE16_MEMBER( notetaker_state::LoadKeyCtlReg_w )
{
	m_kbduart->write_cs(0);
	m_kbduart->write_np(BIT(data, 4)); // PI - pin 35
	m_kbduart->write_tsb(BIT(data, 3)); // SBS - pin 36
	m_kbduart->write_nb2(BIT(data, 2)); // CLS2 - pin 37
	m_kbduart->write_nb1(BIT(data, 1)); // CLS1 - pin 38
	m_kbduart->write_eps(BIT(data, 0)); // EPE - pin 39
	m_kbduart->write_cs(1);
}

WRITE16_MEMBER( notetaker_state::KeyDataReset_w )
{
	m_kbduart->write_rdav(0); // DDR - pin 18
	m_kbduart->write_rdav(1); // ''
}

WRITE16_MEMBER( notetaker_state::KeyChipReset_w )
{
	m_kbduart->write_xr(0); // MR - pin 21
	m_kbduart->write_xr(1); // ''
}

/* FIFO (DAC) Stuff and ADC stuff */
WRITE16_MEMBER(notetaker_state::FIFOReg_w)
{
	m_SetSH = (data&0x8000)?1:0;
	m_SHConA = (data&0x4000)?1:0;
	m_SHConB = (data&0x2000)?1:0;
	m_FrSel0 = (data&0x1000)?1:0;
	m_FrSel1 = (data&0x0800)?1:0;
	m_FrSel2 = (data&0x0400)?1:0;
	m_TabletXOn = (data&0x0200)?1:0;
	m_TabletYOn = (data&0x0100)?1:0;
	m_FIFO_timer->adjust(attotime::from_hz(((960_kHz_XTAL/10)/4)/((m_FrSel0<<3)+(m_FrSel1<<2)+(m_FrSel2<<1)+1)));
	/* FIFO timer is clocked by 960khz divided by 10 (74ls162 decade counter),
	divided by 4 (mc14568B with divider 1 pins set to 4), divided by
	1,3,5,7,9,11,13,15 (or 0,2,4,6,8,10,12,14?)
	*/
	// todo: handle tablet and sample/hold stuff as well
	logerror("Write to 0x60 FIFOReg_w of %04x; fifo timer set to %d hz\n", data, ((((960'000)/10)/4)/((m_FrSel0<<3)+(m_FrSel1<<2)+(m_FrSel2<<1)+1)));
}

WRITE16_MEMBER(notetaker_state::FIFOBus_w)
{
	if (m_outfifo_count == 16)
	{
#ifdef SPC_LOG_DSP
		logerror("outfifo was full, write ignored!\n");
#endif
		return;
	}
	m_outfifo[m_outfifo_head_ptr] = data >> 4;
	m_outfifo_head_ptr++;
	m_outfifo_count++;
	m_outfifo_head_ptr&=0xF;
}

WRITE16_MEMBER( notetaker_state::DiskReg_w )
{
	/* See http://bitsavers.trailing-edge.com/pdf/xerox/notetaker/memos/19781023_More_NoteTaker_IO_Information.pdf
	   but note that bit 12 (called bit 3 in documentation) was changed between
	   oct 1978 and 1979 to reset the disk controller digital-PLL as
	   ClrDiskCont' rather than acting as ProgBitClk0, which is permanently
	   wired high instead, meaning only the 4.5Mhz - 18Mhz dot clocks are
	   available for the CRTC. */
	m_ADCSpd0 = (data&0x8000)?1:0;
	m_ADCSpd1 = (data&0x4000)?1:0;
	m_StopWordClock_q = (data&0x2000)?1:0;
	//if ((!(m_ClrDiskCont_q)) && (data&0x1000)) m_floppy->device_reset(); // reset on rising edge
	m_ClrDiskCont_q = (data&0x1000)?1:0; // originally ProgBitClk0, but co-opted later to reset the FDC's external PLL
	m_ProgBitClk1 = (data&0x0800)?1:0;
	m_ProgBitClk2 = (data&0x0400)?1:0;
	m_ProgBitClk3 = (data&0x0200)?1:0;
	m_AnSel4 = (data&0x0100)?1:0;
	m_AnSel2 = (data&0x80)?1:0;
	m_AnSel1 = (data&0x40)?1:0;
	m_DriveSel1 = (data&0x20)?1:0;
	m_DriveSel2 = (data&0x10)?1:0; // drive 2 not present on hardware, but could work if present
	m_DriveSel3 = (data&0x08)?1:0; // drive 3 not present on hardware, but could work if present
	m_SideSelect = (data&0x04)?1:0;
	m_Disk5VOn = (data&0x02)?1:0;
	m_Disk12VOn = (data&0x01)?1:0;

	// ADC stuff
	//TODO

	// FDC stuff
	// first handle the motor stuff; we'll clobber whatever was in m_floppy, then reset it to what it should be
	m_floppy = m_floppy0->get_device();
	// Disk5VOn and 12VOn can be thought of as a crude MotorOn signal as the motor won't run with either? of them missing.
	// However, a tech note involves adding a patch so that MotorOn is only activated if the drive is actually selected.
	m_floppy->mon_w(!(m_Disk5VOn && m_Disk12VOn && m_DriveSel1));
	//m_floppy = m_floppy1->get_device();
	//m_floppy->mon_w(!(m_Disk5VOn && m_Disk12VOn && m_DriveSel2)); // Disk5VOn and 12VOn can be thought of as a crude MotorOn signal as the motor won't run with either? of them missing.
	//m_floppy = m_floppy2->get_device();
	//m_floppy->mon_w(!(m_Disk5VOn && m_Disk12VOn && m_DriveSel3)); // Disk5VOn and 12VOn can be thought of as a crude MotorOn signal as the motor won't run with either? of them missing.
	// now restore m_floppy state to what it should be
	if (m_DriveSel1) m_floppy = m_floppy0->get_device();
	//else if (m_DriveSel2) m_floppy = m_floppy1->get_device();
	//else if (m_DriveSel3) m_floppy = m_floppy2->get_device();
	else m_floppy = nullptr;
	m_fdc->set_floppy(m_floppy); // select the floppy
	if (m_floppy)
	{
		m_floppy->ss_w(m_SideSelect);
	}

	// CRTC clock rate stuff
	//TODO
}

WRITE16_MEMBER( notetaker_state::LoadDispAddr_w )
{
	m_DispAddr = data;
	// for future low level emulation: clear the current counter position here as well, as well as empty/reset the display fifo, and the setmemrq state.
}

/* EIA hd6402 */
READ16_MEMBER( notetaker_state::ReadEIAStatus_r ) // 74ls368 hex inverter at #f1 provides 2 bits, inverted
{
	uint16_t data = 0xFFFC;
	// note /SWE is permanently enabled, so we don't enable it here for HD6402 reading
	data |= m_eiauart->dav_r( ) ? 0 : 0x02; // DR - pin 19
	data |= m_eiauart->tbmt_r() ? 0 : 0x01; // TBRE - pin 22
	return data;
}

WRITE16_MEMBER( notetaker_state::LoadEIACtlReg_w )
{
	m_eiauart->write_cs(0);
	m_eiauart->write_np(BIT(data, 4)); // PI - pin 35
	m_eiauart->write_tsb(BIT(data, 3)); // SBS - pin 36
	m_eiauart->write_nb2(BIT(data, 2)); // CLS2 - pin 37
	m_eiauart->write_nb1(BIT(data, 1)); // CLS1 - pin 38
	m_eiauart->write_eps(BIT(data, 0)); // EPE - pin 39
	m_eiauart->write_cs(1);
}

WRITE16_MEMBER( notetaker_state::EIADataReset_w )
{
	m_eiauart->write_rdav(0); // DDR - pin 18
	m_eiauart->write_rdav(1); // ''
}

WRITE16_MEMBER( notetaker_state::EIAChipReset_w )
{
	m_eiauart->write_xr(0); // MR - pin 21
	m_eiauart->write_xr(1); // ''
}


/* These next two members are memory map related for the iop */
READ16_MEMBER(notetaker_state::iop_r)
{
	uint16_t *rom = (uint16_t *)(memregion("iop")->base());
	rom += 0x7f800;
	uint16_t *ram = (uint16_t *)(memregion("mainram")->base());
	if ( (m_BootSeqDone == 0) || ((m_DisableROM == 0) && ((offset&0x7F800) == 0)) )
	{
		rom += (offset&0x7FF);
		return *rom;
	}
	else
	{
		// are we in the FFFE8-FFFEF area where the parity/int/reset/etc stuff lives?
		if (offset >= 0x7fff4) { logerror("attempt to read processor control regs at %d\n", offset<<1); return 0xFFFF; }
		ram += offset;
		return *ram;
	}
}

WRITE16_MEMBER(notetaker_state::iop_w)
{
	//uint16_t tempword;
	uint16_t *ram = (uint16_t *)(memregion("mainram")->base());
	if ( (m_BootSeqDone == 0) || ((m_DisableROM == 0) && ((offset&0x7F800) == 0)) )
	{
		logerror("attempt to write %04X to ROM-mapped area at %06X ignored\n", data, offset<<1);
		return;
	}
	// are we in the FFFE8-FFFEF area where the parity/int/reset/etc stuff lives?
	if (offset >= 0x7fff4) { logerror("attempt to write processor control regs at %d with %02X ignored\n", offset<<1, data); }
	COMBINE_DATA(&ram[offset]);
}

/* Address map comes from http://bitsavers.informatik.uni-stuttgart.de/pdf/xerox/notetaker/schematics/19790423_Notetaker_IO_Processor.pdf
a19 a18 a17 a16  a15 a14 a13 a12  a11 a10 a9  a8   a7  a6  a5  a4   a3  a2  a1  a0   BootSeqDone  DisableROM
mode 0: (to get the boot vector and for maybe 1 or 2 instructions afterward)
x   x   x   x    x   x   x   x    *   *   *   *    *   *   *   *    *   *   *   *    0            x          R   ROM (writes are open bus)
mode 1: (during most of boot)
0   0   0   0    0   0   0   0    *   *   *   *    *   *   *   *    *   *   *   *    1            0          R   ROM (writes are open bus)
0   0   0   0    0   0   0   1    *   *   *   *    *   *   *   *    *   *   *   *    1            0          RW  RAM
0   0   0   0    0   0   1   *    *   *   *   *    *   *   *   *    *   *   *   *    1            0          RW  RAM
<anything not all zeroes>x   x    x   x   x   x    x   x   x   x    x   x   x   x    1            0          .   Open Bus
mode 2: (during load of the emulatorcpu's firmware to the first 8k of shared ram which is on the emulatorcpu board)
0   0   *   *    *   *   *   *    *   *   *   *    *   *   *   *    *   *   *   *    1            1          RW  RAM
<anything not all zeroes>x   x    x   x   x   x    x   x   x   x    x   x   x   x    1            1          .   Open Bus
   EXCEPT for the following, decoded by the memory address logic board:
1   1   1   1    1   1   1   1    1   1   1   1    1   1   1   0    1   0   0   x    1            x          .   Open Bus
1   1   1   1    1   1   1   1    1   1   1   1    1   1   1   0    1   0   1   x    1            x          W   FFFEA (Multiprocessor Control (reset/int/boot for each proc; data bits 3,2,1,0 = 0010 means IP, bits 3210 = 0111 means EP; all others ignored.))
1   1   1   1    1   1   1   1    1   1   1   1    1   1   1   0    1   1   0   x    1            x          R   FFFEC (Syndrome bits (gnd bit 15, parity bit 14, exp (ECC) bits 13-8, bits 7-0 are 'other' (?maybe highest address bits?)
1   1   1   1    1   1   1   1    1   1   1   1    1   1   1   0    1   1   1   x    1            x          R   FFFEE (Parity Error Address: row bits 15-8, column bits 7-0; reading this acknowledges a parity interrupt)

More or less:
BootSeqDone is 0, DisableROM is ignored, mem map is 0x00000-0xfffff reading is the 0x1000-long ROM, repeated every 0x1000 bytes. writing goes nowhere.
BootSeqDone is 1, DisableROM is 0,       mem map is 0x00000-0x00fff reading is the 0x1000-long ROM, remainder of memory map goes to RAM or open bus. writing the ROM area goes nowhere, writing RAM goes to RAM.
BootSeqDone is 1, DisableROM is 1,       mem map is entirely RAM or open bus for both reading and writing.
*/

void notetaker_state::iop_mem(address_map &map)
{
	/*
	map(0x00000, 0x00fff).rom().region("iop", 0xff000); // rom is here if either BootSeqDone OR DisableROM are zero. the 1.5 source code and the schematics implies writes here are ignored while rom is enabled; if disablerom is 1 this goes to mainram
	map(0x01000, 0x3ffff).ram().region("mainram", 0); // 256k of ram (less 8k), shared between both processors. rom goes here if bootseqdone is 0
	// note 4000-d5ff is the framebuffer for the screen, in two sets of fields for odd/even interlace?
	map(0xff000, 0xfffe7).rom().region("iop", 0xff000); // rom is only banked in here if bootseqdone is 0, so the reset vector is in the proper place. otherwise the memory control regs live at fffe8-fffef
	//map(0xfffea, 0xfffeb).w(FUNC(notetaker_state::cpuCtl_w));
	//map(0xfffec, 0xfffed).r(FUNC(notetaker_state::parityErrHi_r));
	//map(0xfffee, 0xfffef).r(this. FUNC(notetaker_state::parityErrLo_r));
	map(0xffff0, 0xfffff).rom().region("iop", 0xffff0);
	*/
	map(0x00000, 0xfffff).rw(FUNC(notetaker_state::iop_r), FUNC(notetaker_state::iop_w)); // bypass MAME's memory map system as we need finer grained control
}

/* iop memory map comes from http://bitsavers.informatik.uni-stuttgart.de/pdf/xerox/notetaker/memos/19790605_Definition_of_8086_Ports.pdf
   and from the schematic at http://bitsavers.informatik.uni-stuttgart.de/pdf/xerox/notetaker/schematics/19790423_Notetaker_IO_Processor.pdf
a19 a18 a17 a16  a15 a14 a13 a12  a11 a10 a9  a8   a7  a6  a5  a4   a3  a2  a1  a0
x   x   x   x    0   x   x   x    x   x   x   0    0   0   0   x    x   x   *   .       RW  IntCon (PIC8259)
x   x   x   x    0   x   x   x    x   x   x   0    0   0   1   x    x   x   x   .       W   IPConReg
x   x   x   x    0   x   x   x    x   x   x   0    0   1   0   x    0   0   0   .       .   KbdInt:Open Bus
x   x   x   x    0   x   x   x    x   x   x   0    0   1   0   x    0   0   1   .       R   KbdInt:ReadKeyData
x   x   x   x    0   x   x   x    x   x   x   0    0   1   0   x    0   1   0   .       R   KbdInt:ReadOPStatus
x   x   x   x    0   x   x   x    x   x   x   0    0   1   0   x    0   1   1   .       .   KbdInt:Open Bus
x   x   x   x    0   x   x   x    x   x   x   0    0   1   0   x    1   0   0   .       W   KbdInt:LoadKeyCtlReg
x   x   x   x    0   x   x   x    x   x   x   0    0   1   0   x    1   0   1   .       W   KbdInt:LoadKeyData
x   x   x   x    0   x   x   x    x   x   x   0    0   1   0   x    1   1   0   .       W   KbdInt:KeyDataReset
x   x   x   x    0   x   x   x    x   x   x   0    0   1   0   x    1   1   1   .       W   KbdInt:KeyChipReset
x   x   x   x    0   x   x   x    x   x   x   0    0   1   1   x    x   x   x   .       W   FIFOReg
x   x   x   x    0   x   x   x    x   x   x   0    1   0   0   x    x   x   x   .       .   Open Bus(originally ReadOpStatus)
x   x   x   x    0   x   x   x    x   x   x   0    1   0   1   x    x   *   *   .       RW  SelPIA (debugger board 8255 PIA)[Open Bus on normal units]
x   x   x   x    0   x   x   x    x   x   x   0    1   1   0   x    x   x   x   .       W   FIFOBus
x   x   x   x    0   x   x   x    x   x   x   0    1   1   1   x    x   x   x   .       .   Open Bus
x   x   x   x    0   x   x   x    x   x   x   1    0   0   0   x    x   x   x   .       RW  SelDiskReg
x   x   x   x    0   x   x   x    x   x   x   1    0   0   1   x    x   *   *   .       RW  SelDiskInt
x   x   x   x    0   x   x   x    x   x   x   1    0   1   0   *    *   *   *   .       W   SelCrtInt
x   x   x   x    0   x   x   x    x   x   x   1    0   1   1   x    x   x   x   .       W   LoadDispAddr
x   x   x   x    0   x   x   x    x   x   x   1    1   0   0   x    x   x   x   .       .   Open Bus(originally ReadEIAStatus)
x   x   x   x    0   x   x   x    x   x   x   1    1   0   1   x    0   0   0   .       R   SelEIA:ReadEIAStatus
x   x   x   x    0   x   x   x    x   x   x   1    1   0   1   x    0   0   1   .       R   SelEIA:ReadEIAData
x   x   x   x    0   x   x   x    x   x   x   1    1   0   1   x    0   1   0   .       .   SelEIA:Open Bus
x   x   x   x    0   x   x   x    x   x   x   1    1   0   1   x    0   1   1   .       .   SelEIA:Open Bus
x   x   x   x    0   x   x   x    x   x   x   1    1   0   1   x    1   0   0   .       W   SelEIA:LoadEIACtlReg
x   x   x   x    0   x   x   x    x   x   x   1    1   0   1   x    1   0   1   .       W   SelEIA:LoadEIAData
x   x   x   x    0   x   x   x    x   x   x   1    1   0   1   x    1   1   0   .       W   SelEIA:EIADataReset
x   x   x   x    0   x   x   x    x   x   x   1    1   0   1   x    1   1   1   .       W   SelEIA:EIAChipReset
x   x   x   x    0   x   x   x    x   x   x   1    1   1   0   x    x   x   x   .       R   SelADCHi
x   x   x   x    0   x   x   x    x   x   x   1    1   1   1   x    x   x   x   .       W   CRTSwitch
*/
void notetaker_state::iop_io(address_map &map)
{
	map.unmap_value_high();
	map(0x00, 0x03).mirror(0x7e1c).rw(m_iop_pic, FUNC(pic8259_device::read), FUNC(pic8259_device::write)).umask16(0x00ff);
	map(0x20, 0x21).mirror(0x7e1e).w(FUNC(notetaker_state::IPConReg_w)); // I/O processor (rom mapping, etc) control register
	map(0x42, 0x42).mirror(0x7e10).r(m_kbduart, FUNC(ay31015_device::receive)); // read keyboard data
	map(0x44, 0x45).mirror(0x7e10).r(FUNC(notetaker_state::ReadOPStatus_r)); // read keyboard fifo state
	map(0x48, 0x49).mirror(0x7e10).w(FUNC(notetaker_state::LoadKeyCtlReg_w)); // kbd uart control register
	map(0x4a, 0x4a).mirror(0x7e10).w(m_kbduart, FUNC(ay31015_device::transmit)); // kbd uart data register
	map(0x4c, 0x4d).mirror(0x7e10).w(FUNC(notetaker_state::KeyDataReset_w)); // kbd uart ddr switch (data reset)
	map(0x4e, 0x4f).mirror(0x7e10).w(FUNC(notetaker_state::KeyChipReset_w)); // kbd uart reset
	map(0x60, 0x61).mirror(0x7e1e).w(FUNC(notetaker_state::FIFOReg_w)); // DAC sample and hold and frequency setup
	//map(0xa0, 0xa1).mirror(0x7e18).rw("debug8255", FUNC(8255_device::read), FUNC(8255_device::write)); // debugger board 8255
	map(0xc0, 0xc1).mirror(0x7e1e).w(FUNC(notetaker_state::FIFOBus_w)); // DAC data write to FIFO
	map(0x100, 0x101).mirror(0x7e1e).w(FUNC(notetaker_state::DiskReg_w)); // I/O register (adc speed, crtc pixel clock and clock enable, +5 and +12v relays for floppy, etc)
	map(0x120, 0x127).mirror(0x7e18).rw(m_fdc, FUNC(fd1791_device::read), FUNC(fd1791_device::write)).umask16(0x00ff); // floppy controller
	map(0x140, 0x15f).mirror(0x7e00).rw(m_crtc, FUNC(crt5027_device::read), FUNC(crt5027_device::write)).umask16(0x00ff); // crt controller
	map(0x160, 0x161).mirror(0x7e1e).w(FUNC(notetaker_state::LoadDispAddr_w)); // loads the start address for the display framebuffer
	map(0x1a0, 0x1a1).mirror(0x7e10).r(FUNC(notetaker_state::ReadEIAStatus_r)); // read eia fifo state
	map(0x1a2, 0x1a2).mirror(0x7e10).r(m_eiauart, FUNC(ay31015_device::receive)); // read eia data
	map(0x1a8, 0x1a9).mirror(0x7e10).w(FUNC(notetaker_state::LoadEIACtlReg_w)); // eia uart control register
	map(0x1aa, 0x1aa).mirror(0x7e10).w(m_eiauart, FUNC(ay31015_device::transmit)); // eia uart data register
	map(0x1ac, 0x1ad).mirror(0x7e10).w(FUNC(notetaker_state::EIADataReset_w)); // eia uart ddr switch (data reset)
	map(0x1ae, 0x1af).mirror(0x7e10).w(FUNC(notetaker_state::EIAChipReset_w)); // eia uart reset
	//map(0x1c0, 0x1c1).mirror(0x7e1e).r(FUNC(notetaker_state::SelADCHi_r)); // ADC read
	//map(0x1e0, 0x1e1).mirror(0x7e1e).r(FUNC(notetaker_state::CRTSwitch_w)); // CRT power enable?
}

/* iop_pic8259 interrupts:
irq0    parity error (parity error syndrome data will be in fffdx/fffex) - currently ignored
irq1    IPSysInt (interrupt triggered by the emulator cpu)
irq2    DiskInt (interrupt triggered by the IRQ or DRQ pins from the WD1791)
irq3    EIAInt  (interrupt triggered by the datareceived pin from the eiauart)
irq4    OddInt  (interrupt triggered by the O/E Odd/Even pin from the crt5027)
irq5    ADCInt  (interrupt triggered at the ADCSpd rate interrupt from 74c161 counter on the disk/display module to indicate adc conversion finished)
irq6    KbdInt  (interrupt triggered by the datareceived pin from the kbduart)
irq7    VSync   (interrupt from the VSYN VSync pin from the crt5027)
*/

/* writes during boot of io roms v2.0:
0x88 to port 0x020 (PCR; BootSeqDone(1), processor not locked(0), battery charger off(0), rom not disabled(0) correction off&cr4 off(1), cr3 on(0), cr2 on(0), cr1 on (0);)
0x0002 to port 0x100 (IOR write: enable 5v only relay control)
0x0003 to port 0x100 (IOR write: in addition to above, enable 12v relay control)
<dram memory 0x00000-0x3ffff is zeroed here>
0x13 to port 0x000 PIC (ICW1, 8085 vector 0b000[ignored], edge trigger mode, interval of 8, single mode (no cascade/ICW3), ICW4 needed )
0x08 to port 0x002 PIC (ICW2, T7-T3 = 0b00001)
0x0D to port 0x002 PIC (ICW4, SFNM OFF, Buffered Mode MASTER, Normal EOI, 8086 mode)
0xff to port 0x002 PIC (OCW1, All Ints Masked (disabled/suppressed))
0x0000 to port 0x04e (reset keyboard fifo/controller)
0x0000 to port 0x1ae (reset UART)
0x0016 to port 0x048 (kbd control reg write)
0x0005 to port 0x1a8 (UART control reg write)
0x5f to port 0x140 (reg0 95 horizontal lines)               \
0xf2 to port 0x142 (reg1 interlaced, hswidth=0xE, hsdelay=2) \
0x7d to port 0x144 (reg2 16 scans/row, 5 chars/datarow)       \
0x1d to port 0x146 (reg3 0 skew bits, 0x1D datarows/frame)     \_ set up CRTC
0x04 to port 0x148 (reg4 4 scan lines/frame)                   /
0x10 to port 0x14a (reg5 0x10 vdatastart)                     /
0x00 to port 0x154 (reset the crtc)                          /
0x1e to port 0x15a (reg8 load cursor line address = 0x1e)   /
0x0a03 to port 0x100 (IOR write: set bit clock to 12Mhz)
0x2a03 to port 0x100 (IOR write: enable crtc clock chain)
0x00 to port 0x15c (fire off crtc timing chain)
read from 0x0002 (byte wide) (IMR, read interrupt mask, will be 0xFF from above)
0xaf to port 0x002 PIC (mask out with 0xEF and 0xBF to unnmask interrupts IR4(OddInt) and IR6(KbdInt), and write back to IMR)
0x0400 to 0x060 (select DAC fifo frequency 2)
read from 0x44 (byte wide) to check input fifo status
... more stuff here missing relating to making the beep tone through fifo
(around pc=6b6) read keyboard uart until mouse button is clicked (WaitNoBug)
(around pc=6bc) read keyboard uart until mouse button is released (WaitBug)
0x2a23 to port 0x100 (select drive 1)
0x2a23 to port 0x100 (select drive 1)
0x3a23 to port 0x100 (unset disk separator clear (allow disk head reading))
0x3a27 to port 0x100 (select disk side 1)
0x3a07 to port 0x100 (unselect all drives)

*/



/* Emulator CPU */

WRITE16_MEMBER(notetaker_state::EPConReg_w)
{
	/*m_EP_LED1 = m_EP_ParityError; // if parity checking is enabled AND the last access was to the low 8k AND there was a parity error, the parity error latch is latched here. It triggers an interrupt.
	m_EP_LED2 = (data&0x40)?1:0;
	m_EP_LED3 = (data&0x20)?1:0;
	m_EP_LED4 = (data&0x10)?1:0;
	m_EP_LED_SelROM_q = (data&0x08)?1:0; // this doesn't appear to be hooked anywhere, andjust drives an LED
	// originally, SelROM_q enabled two 2716 EPROMS, later 82s137 PROMS to map code to the FFC00-FFFFF area but this was dropped in the 1979 design revision in favor of having the IOP write the boot vectors for the EP to the shared ram instead. See below for how the top two address bits are disconnected to allow this to work with the way the shared ram is mapped.
	m_EP_ProcLock = (data&0x04)?1:0; // bus lock for this processor (hold other processor in wait state)
	m_EP_SetParity_q = (data&0x02)?1:0; // enable parity checking on local ram if low
	m_EP_DisLMem_q = (data&0x01)?1:0; // if low, the low 8k of local memory is disabled and accesses the shared memory instead.
	popmessage("EP LEDS: CR1: %d, CR2: %d, CR3: %d, CR4: %d", (data&0x80)>>2, (data&0x40)>>3, (data&0x20)>>1, (data&0x10));*/
}

/*
Emulator cpu mem map:
(The top two address bits are disconnected, to allow the ram board, which maps itself only at 00000-3ffff, to appear at "ffff0" to the ep processor when /reset is de-asserted by the iop)
a19 a18 a17 a16  a15 a14 a13 a12  a11 a10 a9  a8   a7  a6  a5  a4   a3  a2  a1  a0   DisLMem_q
x   x   0   0    0   0   0   *    *   *   *   *    *   *   *   *    *   *   *   *    0                       RW  Local (fast) RAM
x   x   0   0    0   0   0   *    *   *   *   *    *   *   *   *    *   *   *   *    1                       RW  System/Shared RAM
<anything not all zeroes >   *    *   *   *   *    *   *   *   *    *   *   *   *    x                       RW  System/Shared RAM
   EXCEPT for the following, decoded by the EP board and superseding above:
x   x   1   1    1   1   1   1    1   1   1   1    1   1   0   x    x   x   x   x    x                       RW  FFFC0-FFFDF (trigger ILLINST interrupt on EP, data ignored?)
   And the following, decoded by the memory address logic board:
x   x   1   1    1   1   1   1    1   1   1   1    1   1   1   0    1   0   0   x    x                       .   Open Bus
x   x   1   1    1   1   1   1    1   1   1   1    1   1   1   0    1   0   1   x    x                       W   FFFEA (Multiprocessor Control (reset(bit 6)/int(bit 5)/boot(bit 4) for each processor; data bits 3,2,1,0 are 'processor address'; 0010 means IP, 0111 means EP; all others ignored.))
x   x   1   1    1   1   1   1    1   1   1   1    1   1   1   0    1   1   0   x    x                       R   FFFEC (Syndrome bits (gnd bit 15, parity bit 14, exp(syndrome) bits 13-8, bits 7-0 are the highest address bits)
x   x   1   1    1   1   1   1    1   1   1   1    1   1   1   0    1   1   1   x    x                       R   FFFEE (Parity Error Address: row bits 15-8, column bits 7-0; reading this also acknowledges a parity interrupt)
*/

void notetaker_state::ep_mem(address_map &map)
{
	map(0x00000, 0x01fff).mirror(0xc0000).ram(); // actually a banked block of ram, 8kb (4kw)
	map(0x02000, 0x3ffff).mirror(0xc0000).ram().region("mainram", 0x2000); // 256k of ram (less 8k), shared between both processors, mirrored 4 times
	//map(0xfffc0, 0xfffdf).mirror(0xc0000).rw(FUNC(notetaker_state::proc_illinst_r), FUNC(notetaker_state::proc_illinst_w));
	//map(0xfffe0, 0xfffef).mirror(0xc0000).rw(FUNC(notetaker_state::proc_control_r), FUNC(notetaker_state::proc_control_w));
}

/* note everything in the emulatorcpu's io range is incompletely decoded; so if
   0x1800 is accessed it will write to both the debug 8255 AND the pic8259!
   I'm not sure the code abuses this or not, but it might do so to both write
   registers and clear parity at once, or something similar. */
/*
Emulator cpu i/o map:
a19 a18 a17 a16  a15 a14 a13 a12  a11 a10 a9  a8   a7  a6  a5  a4   a3  a2  a1  a0   DisLMem_q
x   x   x   x    x   x   x   x    1   x   x   x    x   x   x   x    x   x   *   x    x                       RW  8259
x   x   x   x    x   x   x   1    x   x   x   x    x   x   x   x    x   *   *   x    x                       RW  EP debugger 8255, same exact interface on both cpu and alto side as the IOP debugger 8255
x   x   x   x    x   x   1   x    x   x   x   x    x   x   x   x    x   x   x   x    x                       W   EPConReg
x   x   x   x    x   1   x   x    x   x   x   x    x   x   x   x    x   x   x   x    x                       W   Writing anything here clears the parity error latch
*/

void notetaker_state::ep_io(address_map &map)
{
	map.unmap_value_high();
	map(0x800, 0x803).mirror(0x07fc).rw(m_ep_pic, FUNC(pic8259_device::read), FUNC(pic8259_device::write)).umask16(0x00ff);
	//map(0x1000, 0x1001) AM_MIRROR(0x07fe) AM_DEVREADWRITE("debug8255", 8255_device, read, write) // debugger board 8255, is this the same one as the iop accesses? or are these two 8255s on separate cards?
	map(0x2000, 0x2001).mirror(0x07fe).w(FUNC(notetaker_state::EPConReg_w)); // emu processor control reg & leds
	//map(0x4000, 0x4001) AM_MIRROR(0x07fe) AM_WRITE(EmuClearParity_w) // writes here clear the local 8k-ram parity error register
}

/* Input ports */

/* Floppy Image Interface */
static void notetaker_floppies(device_slot_interface &device)
{
	device.option_add("525dd", FLOPPY_525_DD);
}

/* Machine Start; allocate timers and savestate stuff */
void notetaker_state::machine_start()
{
	// allocate the DAC timer, and set it to fire NEVER. We'll set it up properly in IPReset.
	m_FIFO_timer = machine().scheduler().timer_alloc(timer_expired_delegate(FUNC(notetaker_state::timer_fifoclk),this));
	m_FIFO_timer->adjust(attotime::never);
	// FDC: /DDEN is tied permanently LOW so MFM mode is ALWAYS ON
	m_fdc->dden_w(0);
	// Keyboard UART: /SWE is tied permanently LOW
	m_kbduart->write_swe(0); // status word outputs are permanently enabled (pin 16 SFD(SWE) tied low, active)
	// EIA UART: /SWE is tied permanently LOW
	m_eiauart->write_swe(0); // status word outputs are permanently enabled (pin 16 SFD(SWE) tied low, active)
	// savestate stuff
	// TODO: add me!
}

/* Machine Reset; this emulates the full system reset, triggered by ExtReset' (cardcage pin <50>) or the PowerOnReset' circuit */
void notetaker_state::machine_reset()
{
	iop_reset();
	ep_reset();
}

/* IP Reset; this emulates the IPReset' signal */
void notetaker_state::iop_reset()
{
	// reset the Keyboard UART
	m_kbduart->write_xr(0); // MR - pin 21
	m_kbduart->write_xr(1); // ''
	// reset the EIA UART
	m_eiauart->write_xr(0); // MR - pin 21
	m_eiauart->write_xr(1); // ''
	// reset the IPConReg ls273 latch at #f1
	IPConReg_w(m_iop_cpu->space(AS_PROGRAM), 0, 0x0000, 0xffff);
	// Clear the DAC FIFO
	for (int i=0; i<16; i++) m_outfifo[i] = 0;
	m_outfifo_count = m_outfifo_tail_ptr = m_outfifo_head_ptr = 0;
	// reset the FIFOReg latch at #h9
	FIFOReg_w(m_iop_cpu->space(AS_PROGRAM), 0, 0x0000, 0xffff);
	// reset the DiskReg latches at #c4 and #b4 on the disk/display/eia controller board
	DiskReg_w(m_iop_cpu->space(AS_PROGRAM), 0, 0x0000, 0xffff);
	// reset the framebuffer display address counter:
	m_DispAddr = 0;
}

/* EP Reset; this emulates the EPReset' signal */
void notetaker_state::ep_reset()
{
	//TODO: force ep into reset and hold it there, until the iop releases it. there's 6 'state' bits controllable by the memory mapped cpu control reg, which need to be reset for epcpu and iocpu separately
}

/* Input ports */
static INPUT_PORTS_START( notetakr )
INPUT_PORTS_END

MACHINE_CONFIG_START(notetaker_state::notetakr)
	/* basic machine hardware */
	/* IO CPU: 8086@8MHz */
	I8086(config, m_iop_cpu, 24_MHz_XTAL / 3); /* iD8086-2 @ E4A; 24Mhz crystal divided down to 8Mhz by i8284 clock generator */
	m_iop_cpu->set_addrmap(AS_PROGRAM, &notetaker_state::iop_mem);
	m_iop_cpu->set_addrmap(AS_IO, &notetaker_state::iop_io);
	m_iop_cpu->set_irq_acknowledge_callback("iop_pic8259", FUNC(pic8259_device::inta_cb));

	PIC8259(config, m_iop_pic, 0); // iP8259A-2 @ E6
	m_iop_pic->out_int_callback().set_inputline(m_iop_cpu, 0);

	/* Emulator CPU: 8086@5MHz */
	I8086(config, m_ep_cpu, 15_MHz_XTAL / 3);
	m_ep_cpu->set_disable(); // TODO: implement the cpu control bits so this doesn't execute garbage/zeroes before its firmware gets loaded
	m_ep_cpu->set_addrmap(AS_PROGRAM, &notetaker_state::ep_mem);
	m_ep_cpu->set_addrmap(AS_IO, &notetaker_state::ep_io);
	m_ep_cpu->set_irq_acknowledge_callback("ep_pic8259", FUNC(pic8259_device::inta_cb));

	PIC8259(config, m_ep_pic, 0); // iP8259A-2 @ E6
	m_ep_pic->out_int_callback().set_inputline(m_ep_cpu, 0);

	/* video hardware */
	MCFG_SCREEN_ADD("screen", RASTER)
	MCFG_SCREEN_REFRESH_RATE(60.975)
	MCFG_SCREEN_VBLANK_TIME(ATTOSECONDS_IN_USEC(250))
	MCFG_SCREEN_UPDATE_DRIVER(notetaker_state, screen_update)
	MCFG_SCREEN_SIZE(640, 480)
	MCFG_SCREEN_VISIBLE_AREA(0, 640-1, 0, 480-1)
	MCFG_SCREEN_PALETTE("palette")

	PALETTE(config, "palette", palette_device::MONOCHROME);

	/* Devices */
	CRT5027(config, m_crtc, (36_MHz_XTAL / 4) / 8); // See below
	/* the clock for the crt5027 is configurable rate; 36MHz xtal divided by 1*,
	   2, 3, 4, 5, 6, 7, or 8 (* because this is a 74s163 this setting probably
	   means divide by 1; documentation at
	   http://bitsavers.trailing-edge.com/pdf/xerox/notetaker/memos/19790605_Definition_of_8086_Ports.pdf
	   claims it is 1.5, which makes no sense) and secondarily divided by 8
	   (again by two to load the 16 bit output shifters after this).
	   on reset, bitclk is 000 so divider is (36mhz/8)/8; during boot it is
	   written with 101, changing the divider to (36mhz/4)/8 */
	// TODO: for now, we just hack it to the latter setting from start; this should be handled correctly in iop_reset();
	m_crtc->set_char_width(8); //(8 pixels per column/halfword, 16 pixels per fullword)
	// TODO: below is HACKED to trigger the odd/even int ir4 instead of vblank int ir7 since ir4 is required for anything to be drawn to screen! hence with the hack this interrupt triggers twice as often as it should
	m_crtc->vsyn_callback().set(m_iop_pic, FUNC(pic8259_device::ir4_w)); // note this triggers interrupts on both the iop (ir7) and emulatorcpu (ir4)
	m_crtc->set_screen("screen");

	AY31015(config, m_kbduart); // HD6402, == AY-3-1015D
	m_kbduart->write_dav_callback().set(m_iop_pic, FUNC(pic8259_device::ir6_w)); // DataRecvd = KbdInt

	clock_device &kbdclock(CLOCK(config, "kbdclock", 960_kHz_XTAL)); // hard-wired to 960KHz xtal #f11 (60000 baud, 16 clocks per baud)
	kbdclock.signal_handler().set(m_kbduart, FUNC(ay31015_device::write_rcp));
	kbdclock.signal_handler().append(m_kbduart, FUNC(ay31015_device::write_tcp));

	AY31015(config, m_eiauart); // HD6402, == AY-3-1015D
	m_eiauart->write_dav_callback().set(m_iop_pic, FUNC(pic8259_device::ir3_w)); // EIADataReady = EIAInt

	clock_device &eiaclock(CLOCK(config, "eiaclock", ((960_kHz_XTAL/10)/4)/5)); // hard-wired through an mc14568b divider set to divide by 4, the result set to divide by 5; this resulting 4800hz signal being 300 baud (16 clocks per baud)
	eiaclock.signal_handler().set(m_eiauart, FUNC(ay31015_device::write_rcp));
	eiaclock.signal_handler().append(m_eiauart, FUNC(ay31015_device::write_tcp));

	/* Floppy */
	FD1791(config, m_fdc, (((24_MHz_XTAL/3)/2)/2)); // 2mhz, from 24mhz ip clock divided by 6 via 8284, an additional 2 by LS161 at #e1 on display/floppy board
	FLOPPY_CONNECTOR(config, "wd1791:0", notetaker_floppies, "525dd", floppy_image_device::default_floppy_formats);

	/* sound hardware */
	SPEAKER(config, "lspeaker").front_left();
	SPEAKER(config, "rspeaker").front_right();
	// TODO: hook DAC up to two HA2425 (sample and hold) chips and hook those up to the speakers
	MCFG_DEVICE_ADD("dac", DAC1200, 0) MCFG_SOUND_ROUTE(ALL_OUTPUTS, "lspeaker", 0.5) MCFG_SOUND_ROUTE(ALL_OUTPUTS, "rspeaker", 0.5) // unknown DAC
	MCFG_DEVICE_ADD("vref", VOLTAGE_REGULATOR, 0) MCFG_VOLTAGE_REGULATOR_OUTPUT(5.0)
	MCFG_SOUND_ROUTE(0, "dac", 1.0, DAC_VREF_POS_INPUT) MCFG_SOUND_ROUTE(0, "dac", -1.0, DAC_VREF_NEG_INPUT)
MACHINE_CONFIG_END

void notetaker_state::init_notetakr()
{
	// descramble the rom; the whole thing is a gigantic scrambled mess either to ease
	// interfacing with older xerox technologies which used A0 and D0 as the MSB bits
	// or maybe because someone screwed up somewhere along the line. we may never know.
	// see http://bitsavers.informatik.uni-stuttgart.de/pdf/xerox/notetaker/schematics/19790423_Notetaker_IO_Processor.pdf pages 12 and onward
	uint16_t *romsrc = (uint16_t *)(memregion("iopload")->base());
	uint16_t *romdst = (uint16_t *)(memregion("iop")->base());
	// leave the src pointer alone, since we've only used a 0x1000 long address space
	romdst += 0x7f800; // set the dest pointer to 0xff000 (>>1 because 16 bits data)
	for (int i = 0; i < 0x800; i++)
	{
		uint16_t wordtemp = bitswap<16>(*romsrc, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15); // data bus is completely reversed
		uint16_t addrtemp = bitswap<11>(i, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10); // address bus is completely reversed; 11-15 should always be zero
		uint16_t *temppointer = romdst+(addrtemp&0x7FF);
		*temppointer = wordtemp;
		romsrc++;
	}
}

/* ROM definition */
/*
The notetaker, over its lifetime from 1978 to 1981, had three different classes of IOP roms, with multiple versions of each one.
These were:
BIOP - "Bootable", standalone "user" unit, running smalltalk-78 off of a boot disk, either single or double density; early notetakers used an fd1791 while later ones used a wd1791.
XIOP - "eXercizer" intended for initial testing of each NoteTaker system as assembled; only usable running tethered to a Xerox Alto running notex (notex.cm) as a hardware scripting language for system testing
MIOP - only bootable tethered to a Xerox Alto via a debug card, running smalltalk on the NoteTaker, but not booted off of the floppy disk.
The 'Z-iop' firmware 1.5 below seems to be a BIOP firmware.
*/

ROM_START( notetakr )
	ROM_REGION( 0x1000, "iopload", ROMREGION_ERASEFF ) // load roms here before descrambling
	ROM_SYSTEM_BIOS( 0, "v2.00", "Bootable IO Monitor v2.00" ) // dumped from Notetaker
	ROMX_LOAD( "biop__2.00_hi.b2716.h1", 0x0000, 0x0800, CRC(1119691d) SHA1(4c20b595b554e6f5489ab2c3fb364b4a052f05e3), ROM_SKIP(1) | ROM_BIOS(0))
	ROMX_LOAD( "biop__2.00_lo.b2716.g1", 0x0001, 0x0800, CRC(b72aa4c7) SHA1(85dab2399f906c7695dc92e7c18f32e2303c5892), ROM_SKIP(1) | ROM_BIOS(0))
	ROM_SYSTEM_BIOS( 1, "v1.50", "Bootable IO Monitor v1.50" ) // typed from the source listing at http://bitsavers.trailing-edge.com/pdf/xerox/notetaker/memos/19790620_Z-IOP_1.5_ls.pdf and scrambled
	ROMX_LOAD( "z-iop_1.50_hi.h1", 0x0000, 0x0800, CRC(122ffb5b) SHA1(b957fe24620e1aa98b3158dbcf459937dbd54bac), ROM_SKIP(1) | ROM_BIOS(1))
	ROMX_LOAD( "z-iop_1.50_lo.g1", 0x0001, 0x0800, CRC(2cb79a67) SHA1(692aafd2aeea27533f6288dbb1cb8678ea08fade), ROM_SKIP(1) | ROM_BIOS(1))
	ROM_REGION( 0x100000, "iop", ROMREGION_ERASEFF ) // area for descrambled roms
	ROM_REGION( 0x100000, "mainram", ROMREGION_ERASEFF ) // main ram, on 2 cards with parity/ecc/syndrome/timing/bus arbitration on another 2 cards
	ROM_REGION( 0x400, "kbmcu", ROMREGION_ERASEFF )
	ROM_LOAD( "keyboard.i8748.a10a", 0x000, 0x400, NO_DUMP ) // keyboard mcu which handles key scanning as well as reading the mouse quadratures, and issues state responses if requested by the iop
	ROM_REGION( 0x500, "proms", ROMREGION_ERASEFF )
	/* disk data separator prom from the disk/display module board:
	   there are two different versions of this prom, both generated by BCPL programs,
	   one from mid 1978 (Single density only? seems very buggy and might not even work)
	   and one from 1979 (which should work and appears here).
	   Note that the bit order for the state counter (data bits 6,5,4,3) may be
	   reversed vs the real machine, but since the prom address bus is the only
	   thing that ever sees the prom data bus, this prom will work even if the
	   bit order for those bits is backwards.
	*/
	ROM_LOAD( "disksep.82s147.a4", 0x000, 0x200, CRC(38363714) SHA1(c995d2702573f5afb5fc919150d3a5661013f999) ) // 1979 version
	ROM_LOAD( "timingprom.82s147.b1", 0x200, 0x200, CRC(3003b50a) SHA1(77d9ffe4716c2297708b8e5ebce7f930619c3cc3) ) // memory cas/ras/write state machine prom from the memory address logic board; the contents of this are listed in http://www.bitsavers.org/pdf/xerox/notetaker/schematics/19781027_Memory_Address_Timing.pdf
	ROM_LOAD( "memreqprom.82s126.d9", 0x400, 0x100, CRC(56b2be8b) SHA1(5df0579ed8afeb59113700be6f2982ef85f64b44) ) // SETMEMRQ memory timing prom from the disk/display module board; The equations for this one are actually listed on the schematic and the prom dump can be generated from these:
	/*
	SetMemRq:
	Address:
	76543210
	|||||||\- WCtr.0 (MSB)
	||||||\-- WCtr.1
	|||||\--- WCtr.2
	||||\---- WCtr.3 (LSB)
	|||\----- RCtr.0 (MSB)
	||\------ RCtr.1
	|\------- RCtr.2
	\-------- RCtr.3 (LSB)

	The schematic has an error here, showing the SetMemRq_q output coming from data bit 0, in reality based on the listing it comes from data bit 3
	Data:
	3210
	|\\\- N/C (always zero)
	\---- SetMemRq_q

	Equation: SETMEMRQ == (
	  ((Rctr == 0) && ((Wctr == 0)||(Wctr == 4)||(Wctr == 8)))
	||((Rctr == 4) && ((Wctr == 4)||(Wctr == 8)||(Wctr == 12)))
	||((Rctr == 8) && ((Wctr == 8)||(Wctr == 12)||(Wctr == 0)))
	||((Rctr == 12) && ((Wctr == 12)||(Wctr == 0)||(Wctr == 4)))
	)
	The PROM output is SetMemRq_q and is inverted compared to the equation above.
	*/
ROM_END

/* Driver */

//    YEAR  NAME      PARENT  COMPAT  MACHINE   INPUT     CLASS            INIT           COMPANY  FULLNAME     FLAGS
COMP( 1978, notetakr, 0,      0,      notetakr, notetakr, notetaker_state, init_notetakr, "Xerox", "NoteTaker", MACHINE_IS_SKELETON)