1 // license:BSD-3-Clause
2 // copyright-holders:Phil Bennett
3 /***************************************************************************
4 
5     Fairlight CMI-01A Channel Controller Card
6 
7 ***************************************************************************/
8 
9 #include "emu.h"
10 #include "audio/cmi01a.h"
11 
12 #define VERBOSE     (0)
13 #include "logmacro.h"
14 
15 #define MASTER_OSCILLATOR       XTAL(34'291'712)
16 
17 DEFINE_DEVICE_TYPE(CMI01A_CHANNEL_CARD, cmi01a_device, "cmi_01a", "Fairlight CMI-01A Channel Card")
18 
19 
20 const uint8_t cmi01a_device::s_7497_rate_table[64][64] =
21 {
22 	{1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1},
23 	{1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1},
24 	{1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1},
25 	{1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1},
26 	{1,1,1,1,1,1,1,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,1,1,1,1,1,1,1,1},
27 	{1,1,1,1,1,1,1,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,1,1,1,1,1,1,1,0,1,1,1,1,1,1,1,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,1,1,1,1,1,1,1,1},
28 	{1,1,1,1,1,1,1,0,1,1,1,1,1,1,1,0,1,1,1,1,1,1,1,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,1,1,1,1,1,1,1,0,1,1,1,1,1,1,1,0,1,1,1,1,1,1,1,1},
29 	{1,1,1,1,1,1,1,0,1,1,1,1,1,1,1,0,1,1,1,1,1,1,1,0,1,1,1,1,1,1,1,0,1,1,1,1,1,1,1,0,1,1,1,1,1,1,1,0,1,1,1,1,1,1,1,0,1,1,1,1,1,1,1,1},
30 	{1,1,1,0,1,1,1,1,1,1,1,0,1,1,1,1,1,1,1,0,1,1,1,1,1,1,1,0,1,1,1,1,1,1,1,0,1,1,1,1,1,1,1,0,1,1,1,1,1,1,1,0,1,1,1,1,1,1,1,0,1,1,1,1},
31 	{1,1,1,0,1,1,1,1,1,1,1,0,1,1,1,1,1,1,1,0,1,1,1,1,1,1,1,0,1,1,1,0,1,1,1,0,1,1,1,1,1,1,1,0,1,1,1,1,1,1,1,0,1,1,1,1,1,1,1,0,1,1,1,1},
32 	{1,1,1,0,1,1,1,1,1,1,1,0,1,1,1,0,1,1,1,0,1,1,1,1,1,1,1,0,1,1,1,1,1,1,1,0,1,1,1,1,1,1,1,0,1,1,1,0,1,1,1,0,1,1,1,1,1,1,1,0,1,1,1,1},
33 	{1,1,1,0,1,1,1,1,1,1,1,0,1,1,1,0,1,1,1,0,1,1,1,1,1,1,1,0,1,1,1,0,1,1,1,0,1,1,1,1,1,1,1,0,1,1,1,0,1,1,1,0,1,1,1,1,1,1,1,0,1,1,1,1},
34 	{1,1,1,0,1,1,1,0,1,1,1,0,1,1,1,1,1,1,1,0,1,1,1,0,1,1,1,0,1,1,1,1,1,1,1,0,1,1,1,0,1,1,1,0,1,1,1,1,1,1,1,0,1,1,1,0,1,1,1,0,1,1,1,1},
35 	{1,1,1,0,1,1,1,0,1,1,1,0,1,1,1,1,1,1,1,0,1,1,1,0,1,1,1,0,1,1,1,0,1,1,1,0,1,1,1,0,1,1,1,0,1,1,1,1,1,1,1,0,1,1,1,0,1,1,1,0,1,1,1,1},
36 	{1,1,1,0,1,1,1,0,1,1,1,0,1,1,1,0,1,1,1,0,1,1,1,0,1,1,1,0,1,1,1,1,1,1,1,0,1,1,1,0,1,1,1,0,1,1,1,0,1,1,1,0,1,1,1,0,1,1,1,0,1,1,1,1},
37 	{1,1,1,0,1,1,1,0,1,1,1,0,1,1,1,0,1,1,1,0,1,1,1,0,1,1,1,0,1,1,1,0,1,1,1,0,1,1,1,0,1,1,1,0,1,1,1,0,1,1,1,0,1,1,1,0,1,1,1,0,1,1,1,1},
38 	{1,0,1,1,1,0,1,1,1,0,1,1,1,0,1,1,1,0,1,1,1,0,1,1,1,0,1,1,1,0,1,1,1,0,1,1,1,0,1,1,1,0,1,1,1,0,1,1,1,0,1,1,1,0,1,1,1,0,1,1,1,0,1,1},
39 	{1,0,1,1,1,0,1,1,1,0,1,1,1,0,1,1,1,0,1,1,1,0,1,1,1,0,1,1,1,0,1,0,1,0,1,1,1,0,1,1,1,0,1,1,1,0,1,1,1,0,1,1,1,0,1,1,1,0,1,1,1,0,1,1},
40 	{1,0,1,1,1,0,1,1,1,0,1,1,1,0,1,0,1,0,1,1,1,0,1,1,1,0,1,1,1,0,1,1,1,0,1,1,1,0,1,1,1,0,1,1,1,0,1,0,1,0,1,1,1,0,1,1,1,0,1,1,1,0,1,1},
41 	{1,0,1,1,1,0,1,1,1,0,1,1,1,0,1,0,1,0,1,1,1,0,1,1,1,0,1,1,1,0,1,0,1,0,1,1,1,0,1,1,1,0,1,1,1,0,1,0,1,0,1,1,1,0,1,1,1,0,1,1,1,0,1,1},
42 	{1,0,1,1,1,0,1,0,1,0,1,1,1,0,1,1,1,0,1,1,1,0,1,0,1,0,1,1,1,0,1,1,1,0,1,1,1,0,1,0,1,0,1,1,1,0,1,1,1,0,1,1,1,0,1,0,1,0,1,1,1,0,1,1},
43 	{1,0,1,1,1,0,1,0,1,0,1,1,1,0,1,1,1,0,1,1,1,0,1,0,1,0,1,1,1,0,1,0,1,0,1,1,1,0,1,0,1,0,1,1,1,0,1,1,1,0,1,1,1,0,1,0,1,0,1,1,1,0,1,1},
44 	{1,0,1,1,1,0,1,0,1,0,1,1,1,0,1,0,1,0,1,1,1,0,1,0,1,0,1,1,1,0,1,1,1,0,1,1,1,0,1,0,1,0,1,1,1,0,1,0,1,0,1,1,1,0,1,0,1,0,1,1,1,0,1,1},
45 	{1,0,1,1,1,0,1,0,1,0,1,1,1,0,1,0,1,0,1,1,1,0,1,0,1,0,1,1,1,0,1,0,1,0,1,1,1,0,1,0,1,0,1,1,1,0,1,0,1,0,1,1,1,0,1,0,1,0,1,1,1,0,1,1},
46 	{1,0,1,0,1,0,1,1,1,0,1,0,1,0,1,1,1,0,1,0,1,0,1,1,1,0,1,0,1,0,1,1,1,0,1,0,1,0,1,1,1,0,1,0,1,0,1,1,1,0,1,0,1,0,1,1,1,0,1,0,1,0,1,1},
47 	{1,0,1,0,1,0,1,1,1,0,1,0,1,0,1,1,1,0,1,0,1,0,1,1,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,1,1,0,1,0,1,0,1,1,1,0,1,0,1,0,1,1,1,0,1,0,1,0,1,1},
48 	{1,0,1,0,1,0,1,1,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,1,1,0,1,0,1,0,1,1,1,0,1,0,1,0,1,1,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,1,1,0,1,0,1,0,1,1},
49 	{1,0,1,0,1,0,1,1,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,1,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,1,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,1,1,0,1,0,1,0,1,1},
50 	{1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,1,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,1,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,1,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,1},
51 	{1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,1,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,1,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,1},
52 	{1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,1,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,1},
53 	{1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,1},
54 	{0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1},
55 	{0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,0,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1},
56 	{0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,0,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,0,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1},
57 	{0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,0,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,0,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,0,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1},
58 	{0,1,0,1,0,1,0,0,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,0,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,0,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,0,0,1,0,1,0,1,0,1},
59 	{0,1,0,1,0,1,0,0,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,0,0,1,0,1,0,1,0,0,0,1,0,1,0,1,0,0,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,0,0,1,0,1,0,1,0,1},
60 	{0,1,0,1,0,1,0,0,0,1,0,1,0,1,0,0,0,1,0,1,0,1,0,0,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,0,0,1,0,1,0,1,0,0,0,1,0,1,0,1,0,0,0,1,0,1,0,1,0,1},
61 	{0,1,0,1,0,1,0,0,0,1,0,1,0,1,0,0,0,1,0,1,0,1,0,0,0,1,0,1,0,1,0,0,0,1,0,1,0,1,0,0,0,1,0,1,0,1,0,0,0,1,0,1,0,1,0,0,0,1,0,1,0,1,0,1},
62 	{0,1,0,0,0,1,0,1,0,1,0,0,0,1,0,1,0,1,0,0,0,1,0,1,0,1,0,0,0,1,0,1,0,1,0,0,0,1,0,1,0,1,0,0,0,1,0,1,0,1,0,0,0,1,0,1,0,1,0,0,0,1,0,1},
63 	{0,1,0,0,0,1,0,1,0,1,0,0,0,1,0,1,0,1,0,0,0,1,0,1,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,1,0,1,0,0,0,1,0,1,0,1,0,0,0,1,0,1,0,1,0,0,0,1,0,1},
64 	{0,1,0,0,0,1,0,1,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,1,0,1,0,0,0,1,0,1,0,1,0,0,0,1,0,1,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,1,0,1,0,0,0,1,0,1},
65 	{0,1,0,0,0,1,0,1,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,1,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,1,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,1,0,1,0,0,0,1,0,1},
66 	{0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,1,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,1,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,1,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,1},
67 	{0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,1,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,1,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,1},
68 	{0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,1,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,1},
69 	{0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,1},
70 	{0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1},
71 	{0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,0,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1},
72 	{0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,0,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,0,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1},
73 	{0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,0,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,0,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,0,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1},
74 	{0,0,0,1,0,0,0,0,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,0,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,0,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,0,0,0,0,1,0,0,0,1},
75 	{0,0,0,1,0,0,0,0,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,0,0,0,0,1,0,0,0,1},
76 	{0,0,0,1,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,1,0,0,0,1},
77 	{0,0,0,1,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,1,0,0,0,1},
78 	{0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,1},
79 	{0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,1},
80 	{0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,1},
81 	{0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,1},
82 	{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1},
83 	{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1},
84 	{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1},
85 	{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1}
86 };
87 
cmi01a_device(const machine_config & mconfig,const char * tag,device_t * owner,uint32_t clock)88 cmi01a_device::cmi01a_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
89 	: device_t(mconfig, CMI01A_CHANNEL_CARD, tag, owner, clock)
90 	, device_sound_interface(mconfig, *this)
91 	, m_irq_merger(*this, "cmi01a_irq")
92 	, m_pia(*this, "cmi01a_pia_%u", 0U)
93 	, m_ptm(*this, "cmi01a_ptm")
94 	, m_cmi02_pia(*this, "^cmi02_pia_%u", 1U)
95 	, m_stream(nullptr)
96 	, m_irq_cb(*this)
97 {
98 }
99 
device_add_mconfig(machine_config & config)100 void cmi01a_device::device_add_mconfig(machine_config &config)
101 {
102 	PIA6821(config, m_pia[0], 0); // pia_cmi01a_1_config
103 	m_pia[0]->readcb1_handler().set(FUNC(cmi01a_device::tri_r));
104 	m_pia[0]->readpa_handler().set(FUNC(cmi01a_device::ws_dir_r));
105 	m_pia[0]->writepa_handler().set(FUNC(cmi01a_device::ws_dir_w));
106 	m_pia[0]->writepb_handler().set(FUNC(cmi01a_device::rp_w));
107 	m_pia[0]->ca2_handler().set(FUNC(cmi01a_device::load_w));
108 	m_pia[0]->cb2_handler().set(FUNC(cmi01a_device::pia_0_cb2_w));
109 	m_pia[0]->irqa_handler().set(m_irq_merger, FUNC(input_merger_device::in_w<0>));
110 	m_pia[0]->irqb_handler().set(m_irq_merger, FUNC(input_merger_device::in_w<1>));
111 	//if (m_channel == 5) m_pia[0]->enable_logging();
112 
113 	PIA6821(config, m_pia[1], 0); // pia_cmi01a_2_config
114 	m_pia[1]->readca1_handler().set(FUNC(cmi01a_device::zx_r));
115 	m_pia[1]->readcb1_handler().set(FUNC(cmi01a_device::eosi_r));
116 	m_pia[1]->readpa_handler().set(FUNC(cmi01a_device::pia_1_a_r));
117 	m_pia[1]->writepa_handler().set(FUNC(cmi01a_device::pia_1_a_w));
118 	m_pia[1]->writepb_handler().set(FUNC(cmi01a_device::pia_1_b_w));
119 	m_pia[1]->ca2_handler().set(FUNC(cmi01a_device::eload_w));
120 	m_pia[1]->cb2_handler().set(FUNC(cmi01a_device::wpe_w));
121 	m_pia[1]->irqa_handler().set(m_irq_merger, FUNC(input_merger_device::in_w<2>));
122 	m_pia[1]->irqb_handler().set(m_irq_merger, FUNC(input_merger_device::in_w<3>));
123 	//if (m_channel == 5) m_pia[1]->enable_logging();
124 
125 	PTM6840(config, m_ptm, DERIVED_CLOCK(1, 1)); // ptm_cmi01a_config
126 	m_ptm->o1_callback().set(FUNC(cmi01a_device::ptm_o1));
127 	m_ptm->o2_callback().set(FUNC(cmi01a_device::ptm_o2));
128 	m_ptm->o3_callback().set(FUNC(cmi01a_device::ptm_o3));
129 	m_ptm->irq_callback().set(FUNC(cmi01a_device::ptm_irq));
130 
131 	INPUT_MERGER_ANY_HIGH(config, m_irq_merger).output_handler().set(FUNC(cmi01a_device::cmi01a_irq));
132 }
133 
134 
sound_stream_update(sound_stream & stream,std::vector<read_stream_view> const & inputs,std::vector<write_stream_view> & outputs)135 void cmi01a_device::sound_stream_update(sound_stream &stream, std::vector<read_stream_view> const &inputs, std::vector<write_stream_view> &outputs)
136 {
137 	if (m_run)
138 	{
139 		int mask = m_load ? 0x7fff : 0x7f;
140 		int addr = m_segment_cnt;
141 
142 		uint8_t *wave_ptr = &m_wave_ram[m_segment_cnt & 0x3fff];
143 		auto &buf = outputs[0];
144 
145 		for (int sampindex = 0; sampindex < buf.samples(); sampindex++)
146 		{
147 			const uint8_t sample8 = wave_ptr[addr++ & 0x3fff];
148 			s32 sample = (int32_t)(int8_t)(sample8 ^ 0x80) * m_env * m_vol_latch;
149 			if (m_channel == 5) printf("%08x:%02x:%02x:%02x", (uint32_t)sample, sample8, m_env, m_vol_latch);
150 			buf.put_int(sampindex, (int16_t)(sample >> 8), 32768);
151 		}
152 		if (m_channel == 5) printf("\n");
153 
154 		m_segment_cnt = (m_segment_cnt & ~mask) | addr;
155 	}
156 	else
157 		outputs[0].fill(0);
158 }
159 
device_resolve_objects()160 void cmi01a_device::device_resolve_objects()
161 {
162 	m_irq_cb.resolve_safe();
163 }
164 
device_start()165 void cmi01a_device::device_start()
166 {
167 	m_wave_ram = std::make_unique<uint8_t[]>(0x4000);
168 
169 	m_zx_timer = timer_alloc(TIMER_ZX);
170 	m_eosi_timer = timer_alloc(TIMER_EOSI);
171 	m_bcas_timer = timer_alloc(TIMER_BCAS);
172 
173 	m_zx_timer->adjust(attotime::never);
174 	m_eosi_timer->adjust(attotime::never);
175 
176 	m_stream = stream_alloc(0, 1, 44100);
177 
178 	m_ptm->set_external_clocks(clock() / 8, clock() / 4, clock() / 4);
179 
180 
181 }
182 
device_reset()183 void cmi01a_device::device_reset()
184 {
185 	m_ptm->set_g1(1);
186 	m_ptm->set_g2(1);
187 	m_ptm->set_g3(1);
188 
189 	m_segment_cnt = 0;
190 	m_new_addr = 0;
191 	m_vol_latch = 0;
192 	m_flt_latch = 0;
193 	m_rp = 0;
194 	m_ws = 0;
195 	m_dir = 0;
196 	m_env = 0;
197 	m_pia0_cb2_state = 1;
198 	m_bcas_q1_ticks = 2;
199 	m_bcas_q1 = 0;
200 	m_bcas_q2_ticks = 4;
201 	m_bcas_q2 = 0;
202 	m_zx_flag = 0;
203 
204 	m_freq = 0.0;
205 
206 	m_ptm_o1 = 0;
207 	m_ptm_o2 = 0;
208 	m_ptm_o3 = 0;
209 
210 	m_load = true;
211 	m_run = false;
212 	m_gzx = true;
213 	m_nwpe = true;
214 	m_tri = true;
215 	m_pia1_ca2 = false;
216 
217 	m_eclk = false;
218 	m_env_clk = false;
219 	m_ediv_out = true;
220 	m_ediv_rate = 3;
221 	m_ediv_count = 0;
222 
223 	m_pitch = 0;
224 	m_octave = 0;
225 
226 	m_zx_timer->adjust(attotime::never);
227 	m_eosi_timer->adjust(attotime::never);
228 	//m_bcas_timer->adjust(attotime::from_hz(clock()), 0, attotime::from_hz(clock()));
229 	m_bcas_timer->adjust(attotime::never);
230 }
231 
pulse_zcint()232 void cmi01a_device::pulse_zcint()
233 {
234 	m_pia[0]->ca1_w(0);
235 	m_pia[0]->ca1_w(1);
236 
237 	pulse_gzx();
238 }
239 
pulse_gzx()240 void cmi01a_device::pulse_gzx()
241 {
242 	if (!m_pia1_ca2)
243 	{
244 		return;
245 	}
246 
247 	reset_waveform_segment();
248 	m_env = m_rp;
249 	m_env_dir = m_dir;
250 }
251 
reset_waveform_segment()252 void cmi01a_device::reset_waveform_segment()
253 {
254 	m_segment_cnt &= 0x007f;
255 	m_segment_cnt = 0x4000 | (m_ws << 7);
256 }
257 
load_w(int state)258 void cmi01a_device::load_w(int state)
259 {
260 	const bool old_load = m_load;
261 	m_load = state ? false : true;
262 
263 	if (old_load != m_load)
264 	{
265 		check_segment_load();
266 	}
267 }
268 
check_segment_load()269 void cmi01a_device::check_segment_load()
270 {
271 	bool run_strobe = false;
272 	if (m_load != m_run)
273 	{
274 		run_strobe = true;
275 		m_segment_cnt &= ~0x7f;
276 	}
277 
278 	if (!run_strobe)
279 	{
280 		return;
281 	}
282 
283 	if (!m_nwpe)
284 	{
285 		reset_waveform_segment();
286 	}
287 
288 	if (m_channel == 5) LOG("CH%d beginning load with m_segment_cnt %04x\n", m_channel, m_segment_cnt);
289 	m_pia[1]->cb1_w(1);
290 }
291 
pia_1_a_w(uint8_t data)292 void cmi01a_device::pia_1_a_w(uint8_t data)
293 {
294 	m_pitch &= 0x0ff;
295 	m_pitch |= (data & 3) << 8;
296 	m_octave = (data >> 2) & 0x0f;
297 }
298 
pia_1_a_r()299 uint8_t cmi01a_device::pia_1_a_r()
300 {
301 	return ((m_pitch >> 8) & 3) | (m_octave << 2);
302 }
303 
pia_1_b_w(uint8_t data)304 void cmi01a_device::pia_1_b_w(uint8_t data)
305 {
306 	m_pitch &= 0xf00;
307 	m_pitch |= data;
308 }
309 
rp_w(uint8_t data)310 void cmi01a_device::rp_w(uint8_t data)
311 {
312 	m_rp = data;
313 	m_ediv_rate = ((m_rp >> 2) & 0x3c) | 0x03;
314 	if (m_channel == 5) LOG("CH%d: Initial ramp value: %02x, EDIV divider: %02x\n", m_channel, data, m_ediv_rate);
315 }
316 
ws_dir_w(uint8_t data)317 void cmi01a_device::ws_dir_w(uint8_t data)
318 {
319 	if (m_channel == 5) LOG("CH%d: WS/DIR write: %02x\n", m_channel, data);
320 	m_ws = data & 0x7f;
321 	m_dir = (data >> 7) & 1;
322 }
323 
ws_dir_r()324 uint8_t cmi01a_device::ws_dir_r()
325 {
326 	return m_ws | (m_dir << 7);
327 }
328 
READ_LINE_MEMBER(cmi01a_device::tri_r)329 READ_LINE_MEMBER( cmi01a_device::tri_r )
330 {
331 	const bool top_terminal_count = (m_env_dir == ENV_DIR_UP && m_env == 0xff);
332 	const bool bottom_terminal_count = (m_env_dir == ENV_DIR_DOWN && m_env == 0x00);
333 	const int state = (top_terminal_count || bottom_terminal_count) ? 1 : 0;
334 	if (m_channel == 5) LOG("CH%d: PIA0 CB1 Read (/TRI): %d\n", m_channel, state);
335 	return state;
336 }
337 
WRITE_LINE_MEMBER(cmi01a_device::cmi01a_irq)338 WRITE_LINE_MEMBER( cmi01a_device::cmi01a_irq )
339 {
340 	m_irq_cb(state ? ASSERT_LINE : CLEAR_LINE);
341 }
342 
device_timer(emu_timer & timer,device_timer_id id,int param,void * ptr)343 void cmi01a_device::device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr)
344 {
345 	switch(id)
346 	{
347 		case TIMER_ZX:
348 			zx_timer_cb();
349 			break;
350 		case TIMER_EOSI:
351 			eosi_timer_cb();
352 			break;
353 		case TIMER_BCAS:
354 			bcas_tick();
355 	}
356 }
357 
reset_bcas_counter()358 void cmi01a_device::reset_bcas_counter()
359 {
360 	m_bcas_q1_ticks = 2;
361 	m_bcas_q1 = 0;
362 	m_bcas_q2_ticks = 4;
363 	m_bcas_q2 = 0;
364 
365 	m_ptm->set_clock(0, 0);
366 	m_ptm->set_clock(1, 0);
367 	m_ptm->set_clock(2, 0);
368 }
369 
bcas_tick()370 void cmi01a_device::bcas_tick()
371 {
372 	if (m_ptm_o1 != m_zx_ff)
373 		return;
374 }
375 
eosi_timer_cb()376 void cmi01a_device::eosi_timer_cb()
377 {
378 	m_stream->update();
379 	m_segment_cnt &= ~0x4000;
380 	m_pia[1]->cb1_w(0);
381 
382 	if (m_channel == 5) LOG("CH%d: End of sound\n", m_channel);
383 }
384 
zx_timer_cb()385 void cmi01a_device::zx_timer_cb()
386 {
387 	// Toggle ZX
388 	m_zx_flag ^= 1;
389 
390 	// Update ZX input to PIA 1
391 	m_pia[1]->ca1_w(m_zx_flag);
392 
393 	// 74LS74 A12 (1) is clocked by /ZX, so a 1->0 transition of the ZX flag is a positive clock transition
394 	if (m_zx_flag == 0)
395 	{
396 		// Pulse /ZCINT if the O1 output of the PTM has changed
397 		if (m_ptm_o1 != m_zx_ff)
398 		{
399 			reset_bcas_counter();
400 			pulse_zcint();
401 		}
402 
403 		m_zx_ff = m_ptm_o1;
404 	}
405 
406 	update_eclk();
407 }
408 
update_eclk()409 void cmi01a_device::update_eclk()
410 {
411 	bool eclk = (m_ptm_o2 && m_zx_ff) || (m_ptm_o3 && !m_zx_ff);
412 	if (m_channel == 5) logerror("CH%d: eclk = (%d && %d) || (%d && %d) = %d\n", m_channel, m_ptm_o2, m_zx_ff, m_ptm_o3, m_zx_ff ? 0 : 1, eclk ? 1 : 0);
413 	m_stream->update();
414 	set_eclk(eclk);
415 }
416 
wpe_w(int state)417 void cmi01a_device::wpe_w(int state)
418 {
419 	m_nwpe = state ? false : true;
420 }
421 
eload_w(int state)422 void cmi01a_device::eload_w(int state)
423 {
424 	if (m_channel == 5) LOG("CH%d PIA1 CA2: %d\n", m_channel, state);
425 	m_pia1_ca2 = state;
426 }
427 
clock_envelope()428 void cmi01a_device::clock_envelope()
429 {
430 	m_stream->update();
431 	const bool old_tri = m_tri;
432 	if (m_env_dir == ENV_DIR_DOWN)
433 	{
434 		if (m_env > 0)
435 		{
436 			m_env--;
437 			m_ediv_rate = ((m_env >> 2) & 0x3c) | 0x03;
438 			if (m_channel == 5) LOG("CH%d, Clocking envelope down, new rp: %02x\n", m_channel, m_env);
439 		}
440 		m_tri = m_env == 0x00;
441 	}
442 	else
443 	{
444 		if (m_env < 0xff)
445 		{
446 			m_env++;
447 			m_ediv_rate = ((~m_env >> 2) & 0x3c) | 0x03;
448 			if (m_channel == 5) LOG("CH%d, Clocking envelope up, new rp: %02x\n", m_channel, m_env);
449 		}
450 		m_tri = m_env == 0xff;
451 	}
452 	if (old_tri != m_tri)
453 	{
454 		m_pia[0]->cb1_w(m_tri ? 0 : 1);
455 	}
456 }
457 
tick_ediv()458 void cmi01a_device::tick_ediv()
459 {
460 	if (m_channel == 5) logerror("CH%d ticking ediv, rate: %02x\n", m_channel, m_ediv_rate);
461 	m_ediv_out = s_7497_rate_table[m_ediv_rate][m_ediv_count];
462 	m_ediv_count = (m_ediv_count + 1) & 0x3f;
463 }
464 
set_eclk(bool eclk)465 void cmi01a_device::set_eclk(bool eclk)
466 {
467 	bool old_eclk = m_eclk;
468 	m_eclk = eclk;
469 
470 	if (old_eclk == m_eclk)
471 		return;
472 
473 	if (!old_eclk && m_eclk)
474 	{
475 		tick_ediv();
476 	}
477 
478 	//  A   B   !(A && B)   !A || !B
479 	//  0   0   1           1
480 	//  0   1   1           1
481 	//  1   0   1           1
482 	//  1   1   0           0
483 
484 	const bool a = !m_load || !eclk;
485 	const bool b =  m_load || !m_ediv_out;
486 
487 	const bool old_env_clk = m_env_clk;
488 	m_env_clk = !a || !b;
489 	LOG("CH%d checking envelope: A: !!load(%d) || !eclk(%d) = %d\n", m_channel, m_load ? 0 : 1, eclk ? 0 : 1, a);
490 	LOG("CH%d checking envelope: B:  !load(%d) || !eout(%d) = %d\n", m_channel, m_load ? 1 : 0, m_ediv_out ? 0 : 1, b);
491 	LOG("CH%d checking envelope: C: !%d || !%d = %d\n", m_channel, a ? 1 : 0, b ? 1 : 0, m_env_clk ? 1 : 0);
492 	if (!old_env_clk && m_env_clk)
493 	{
494 		clock_envelope();
495 	}
496 }
497 
run_voice()498 void cmi01a_device::run_voice()
499 {
500 	if (m_channel == 5) LOG("CH%d running voice: Pitch = %04x\n", m_channel, (uint16_t)m_pitch);
501 	if (m_channel == 5) LOG("CH%d running voice: o_val = %x\n", m_channel, m_octave);
502 
503 	int m_tune = m_cmi02_pia[0]->b_output();
504 	if (m_channel == 5) LOG("CH%d running voice: Tuning = %02x\n", m_channel, (uint8_t)m_tune);
505 	double mfreq = (double)(0xf00 | m_tune) * ((MASTER_OSCILLATOR.dvalue() / 2.0) / 4096.0);
506 	if (m_channel == 5) LOG("CH%d running voice: mfreq = %f (%03x * %f)\n", m_channel, mfreq, 0xf00 | m_tune, (MASTER_OSCILLATOR.dvalue() / 2.0) / 4096.0);
507 
508 	double cfreq = ((double)(0x800 | (m_pitch << 1)) * mfreq) / 4096.0;
509 	if (m_channel == 5) LOG("CH%d running voice: cfreq = %f (%04x * %f) / 4096.0\n", m_channel, cfreq, 0x800 | (m_pitch << 1), mfreq, cfreq);
510 
511 	if (cfreq > MASTER_OSCILLATOR.dvalue())
512 	{
513 		if (m_channel == 5) LOG("CH%d Ignoring voice run due to excessive frequency\n");
514 		return;
515 	}
516 
517 	if (m_channel == 5) LOG("CH%d Running voice\n", m_channel);
518 	/* Octave register enabled? */
519 	if (!BIT(m_octave, 3))
520 		cfreq /= (double)(2 << ((7 ^ m_octave) & 7));
521 
522 	cfreq /= 16.0;
523 
524 	m_freq = cfreq;
525 
526 	if (m_channel == 5) LOG("CH%d running voice: Final freq: %f\n", m_channel, m_freq);
527 
528 	m_stream->set_sample_rate(cfreq);
529 
530 	// Set timers and things
531 	m_zx_flag = 0;
532 	attotime zx_period = attotime::from_ticks(64, cfreq);
533 	m_zx_timer->adjust(zx_period, 0, zx_period);
534 
535 	if (m_load)
536 	{
537 		int samples = 0x4000 - (m_segment_cnt & 0x3fff);
538 		if (m_channel == 5) LOG("CH%d voice is %04x samples long\n", m_channel, samples);
539 		m_eosi_timer->adjust(attotime::from_ticks(samples, cfreq));
540 	}
541 }
542 
WRITE_LINE_MEMBER(cmi01a_device::pia_0_cb2_w)543 WRITE_LINE_MEMBER( cmi01a_device::pia_0_cb2_w )
544 {
545 	int old_state = m_pia0_cb2_state;
546 	m_pia0_cb2_state = state;
547 	if (m_channel == 5) LOG("CH%d PIA0 CB2: %d\n", m_channel, state);
548 
549 	m_stream->update();
550 
551 	/* RUN */
552 	if (!old_state && m_pia0_cb2_state)
553 	{
554 		m_run = true;
555 
556 		/* Clear /EOSI */
557 		m_pia[1]->cb1_w(1);
558 
559 		/* Only reset address counter if LOAD not asserted */
560 		if (!m_load)
561 		{
562 			m_segment_cnt = 0x4000 | (m_ws << 7);
563 			m_new_addr = 1;
564 		}
565 
566 		/* Clear ZX */
567 		m_pia[1]->ca1_w(0);
568 
569 		/* Clear /ZCINT */
570 		m_pia[0]->ca1_w(1);
571 
572 		m_ptm->set_g1(0);
573 		m_ptm->set_g2(0);
574 		m_ptm->set_g3(0);
575 
576 		run_voice();
577 	}
578 
579 	if (old_state && !m_pia0_cb2_state)
580 	{
581 		m_run = false;
582 
583 		/* Set /EOSI */
584 		m_pia[1]->cb1_w(0);
585 
586 		m_ptm->set_g1(1);
587 		m_ptm->set_g2(1);
588 		m_ptm->set_g3(1);
589 
590 		m_zx_timer->adjust(attotime::never);
591 		m_eosi_timer->adjust(attotime::never);
592 		m_zx_ff = 0;
593 	}
594 
595 }
596 
update_wave_addr(int inc)597 void cmi01a_device::update_wave_addr(int inc)
598 {
599 	int old_cnt = m_segment_cnt;
600 
601 	if (inc)
602 		++m_segment_cnt;
603 
604 	/* Update end of sound interrupt flag */
605 	m_pia[1]->cb1_w((m_segment_cnt & 0x4000) >> 14);
606 
607 	/* TODO Update zero crossing flag */
608 	m_pia[1]->ca1_w((m_segment_cnt & 0x40) >> 6);
609 
610 	/* Clock a latch on a transition */
611 	if ((old_cnt & 0x40) && !(m_segment_cnt & 0x40))
612 	{
613 		m_pia[1]->ca2_w(1);
614 		m_pia[1]->ca2_w(0);
615 	}
616 
617 	/* Zero crossing interrupt is a pulse */
618 }
619 
WRITE_LINE_MEMBER(cmi01a_device::ptm_irq)620 WRITE_LINE_MEMBER( cmi01a_device::ptm_irq )
621 {
622 	m_irq_merger->in_w<4>(state);
623 }
624 
WRITE_LINE_MEMBER(cmi01a_device::ptm_o1)625 WRITE_LINE_MEMBER( cmi01a_device::ptm_o1 )
626 {
627 	m_ptm_o1 = state;
628 	if (m_ptm_o1 != m_zx_ff)
629 		reset_bcas_counter();
630 	update_eclk();
631 }
632 
WRITE_LINE_MEMBER(cmi01a_device::ptm_o2)633 WRITE_LINE_MEMBER( cmi01a_device::ptm_o2 )
634 {
635 	m_ptm_o2 = state;
636 	update_eclk();
637 }
638 
WRITE_LINE_MEMBER(cmi01a_device::ptm_o3)639 WRITE_LINE_MEMBER( cmi01a_device::ptm_o3 )
640 {
641 	m_ptm_o3 = state;
642 	update_eclk();
643 }
644 
READ_LINE_MEMBER(cmi01a_device::eosi_r)645 READ_LINE_MEMBER( cmi01a_device::eosi_r )
646 {
647 	if (m_channel == 5) LOG("CH%d PIA1 CB1 Read: %d\n", m_channel, BIT(m_segment_cnt, 14));
648 	return BIT(m_segment_cnt, 14);
649 }
650 
READ_LINE_MEMBER(cmi01a_device::zx_r)651 READ_LINE_MEMBER( cmi01a_device::zx_r )
652 {
653 	return (m_segment_cnt & 0x40) >> 6;
654 }
655 
write(offs_t offset,uint8_t data)656 void cmi01a_device::write(offs_t offset, uint8_t data)
657 {
658 	switch (offset)
659 	{
660 		case 0x0:
661 			if (m_channel == 5) LOG("%s: CH%d Porthole Write to %04x: %02x\n", machine().describe_context(), m_channel, m_segment_cnt & 0x3fff, data);
662 			if (m_new_addr)
663 				m_new_addr = 0;
664 
665 			m_wave_ram[m_segment_cnt & 0x3fff] = data;
666 			update_wave_addr(1);
667 			break;
668 
669 		case 0x3:
670 			if (m_channel == 5) LOG("%s: CH%d set Envelope Dir Down (%02x)\n", machine().describe_context(), m_channel, data);
671 			m_env_dir = ENV_DIR_DOWN;
672 			break;
673 
674 		case 0x4:
675 			if (m_channel == 5) LOG("%s: CH%d set Envelope Dir Up (%02x)\n", machine().describe_context(), m_channel, data);
676 			m_env_dir = ENV_DIR_UP;
677 			break;
678 
679 		case 0x5:
680 			if (m_channel == 5) LOG("%s: CH%d set Volume Latch: %02x\n", machine().describe_context(), m_channel, data);
681 			m_vol_latch = data;
682 			break;
683 
684 		case 0x6:
685 			if (m_channel == 5) LOG("%s: CH%d set Filter Latch: %02x\n", machine().describe_context(), m_channel, data);
686 			m_flt_latch = data;
687 			break;
688 
689 		case 0x8: case 0x9: case 0xa: case 0xb:
690 			if (m_channel == 5) LOG("CH%d PIA0 Write: %d = %02x\n", m_channel, offset & 3, data);
691 			m_pia[0]->write(offset & 3, data);
692 			break;
693 
694 		case 0xc: case 0xd: case 0xe: case 0xf:
695 			if (m_channel == 5) LOG("CH%d PIA1 Write: %d = %02x\n", m_channel, (BIT(offset, 0) << 1) | BIT(offset, 1), data);
696 			m_pia[1]->write((BIT(offset, 0) << 1) | BIT(offset, 1), data);
697 			break;
698 
699 		case 0x10: case 0x11: case 0x12: case 0x13: case 0x14: case 0x15: case 0x16: case 0x17:
700 		{
701 			/* PTM addressing is a little funky */
702 			int a0 = offset & 1;
703 			int a1 = (m_ptm_o1 && BIT(offset, 3)) || (!BIT(offset, 3) && BIT(offset, 2));
704 			int a2 = BIT(offset, 1);
705 
706 			if ((offset == 5 || offset == 7) && (data < 0x30))
707 				data = 0xff;
708 
709 			if (m_channel == 5) LOG("CH%d PTM Write: %d = %02x\n", m_channel, (a2 << 2) | (a1 << 1) | a0, data);
710 			m_ptm->write((a2 << 2) | (a1 << 1) | a0, data);
711 			break;
712 		}
713 
714 		default:
715 			if (m_channel == 5) LOG("%s: Unknown channel card write to E0%02X = %02X\n", machine().describe_context(), offset, data);
716 			break;
717 	}
718 }
719 
read(offs_t offset)720 uint8_t cmi01a_device::read(offs_t offset)
721 {
722 	if (machine().side_effects_disabled())
723 		return 0;
724 
725 	uint8_t data = 0;
726 
727 	switch (offset)
728 	{
729 		case 0x0:
730 			if (m_new_addr)
731 			{
732 				m_new_addr = 0;
733 				break;
734 			}
735 			data = m_wave_ram[m_segment_cnt & 0x3fff];
736 			if (m_channel == 5) LOG("%s: CH%d Porthole Read: %02x\n", machine().describe_context(), m_channel, data);
737 			update_wave_addr(1);
738 			break;
739 
740 		case 0x3:
741 			if (m_channel == 5) LOG("%s: CH%d set Envelope Dir Down (R)\n", machine().describe_context(), m_channel);
742 			m_env_dir = ENV_DIR_DOWN;
743 			break;
744 
745 		case 0x4:
746 			if (m_channel == 5) LOG("%s: CH%d set Envelope Dir Up (R)\n", machine().describe_context(), m_channel);
747 			m_env_dir = ENV_DIR_UP;
748 			break;
749 
750 		case 0x5:
751 			if (m_channel == 5) LOG("%s: CH%d read Volume Latch (ff)\n", machine().describe_context(), m_channel);
752 			data = 0xff;
753 			break;
754 
755 		case 0x8: case 0x9: case 0xa: case 0xb:
756 			data = m_pia[0]->read(offset & 3);
757 			if (m_channel == 5) LOG("CH%d PIA0 Read: %d = %02x\n", m_channel, offset & 3, data);
758 			break;
759 
760 		case 0xc: case 0xd: case 0xe: case 0xf:
761 			data = m_pia[1]->read((BIT(offset, 0) << 1) | BIT(offset, 1));
762 			if (m_channel == 5) LOG("CH%d PIA1 Read: %d = %02x\n", m_channel, (BIT(offset, 0) << 1) | BIT(offset, 1), data);
763 			break;
764 
765 		case 0x10: case 0x11: case 0x12: case 0x13: case 0x14: case 0x15: case 0x16: case 0x17:
766 		{
767 			int a0 = offset & 1;
768 			int a1 = (m_ptm_o1 && BIT(offset, 3)) || (!BIT(offset, 3) && BIT(offset, 2));
769 			int a2 = BIT(offset, 1);
770 
771 			data = m_ptm->read((a2 << 2) | (a1 << 1) | a0);
772 
773 			if (m_channel == 5) LOG("CH%d PTM Read: %d = %02x\n", m_channel, (a2 << 2) | (a1 << 1) | a0, data);
774 			break;
775 		}
776 
777 		default:
778 			if (m_channel == 5) LOG("%s: Unknown channel card %d read from E0%02X\n", machine().describe_context(), m_channel, offset);
779 			break;
780 	}
781 
782 	if (m_channel == 5) LOG("%s: channel card %d read: %02x = %02x\n", machine().describe_context(), m_channel, offset, data);
783 
784 	return data;
785 }
786