1 // license:BSD-3-Clause
2 // copyright-holders:Patrick Mackinlay
3
4 /*
5 * Brooktree Bt431 Monolithic CMOS 64x64 Pixel Cursor Generator.
6 *
7 * Sources:
8 * - http://bitsavers.org/components/brooktree/_dataBooks/1993_Brooktree_Graphics_and_Imaging_Product_Databook.pdf
9 *
10 * TODO:
11 * - test, profile and optimize
12 */
13
14 #include "emu.h"
15 #include "bt431.h"
16
17 #define LOG_GENERAL (1U << 0)
18
19 //#define VERBOSE (LOG_GENERAL)
20 #include "logmacro.h"
21
22 DEFINE_DEVICE_TYPE(BT431, bt431_device, "bt431", "Bt431 64x64 Pixel Cursor Generator")
23
bt431_device(machine_config const & mconfig,char const * tag,device_t * owner,u32 clock)24 bt431_device::bt431_device(machine_config const &mconfig, char const *tag, device_t *owner, u32 clock)
25 : device_t(mconfig, BT431, tag, owner, clock)
26 {
27 }
28
device_start()29 void bt431_device::device_start()
30 {
31 save_item(NAME(m_address));
32 save_item(NAME(m_command));
33
34 save_item(NAME(m_cursor_x));
35 save_item(NAME(m_cursor_y));
36 save_item(NAME(m_window_x));
37 save_item(NAME(m_window_y));
38 save_item(NAME(m_window_w));
39 save_item(NAME(m_window_h));
40
41 save_item(NAME(m_ram));
42 }
43
device_reset()44 void bt431_device::device_reset()
45 {
46 m_address = 0;
47 m_command = 0;
48
49 update();
50 }
51
map(address_map & map)52 void bt431_device::map(address_map &map)
53 {
54 map(0x00, 0x00).rw(FUNC(bt431_device::addr_r<0>), FUNC(bt431_device::addr_w<0>));
55 map(0x01, 0x01).rw(FUNC(bt431_device::addr_r<8>), FUNC(bt431_device::addr_w<8>));
56 map(0x02, 0x02).rw(FUNC(bt431_device::ram_r), FUNC(bt431_device::ram_w));
57 map(0x03, 0x03).rw(FUNC(bt431_device::reg_r), FUNC(bt431_device::reg_w));
58 }
59
ram_r()60 u8 bt431_device::ram_r()
61 {
62 u8 const data = m_ram[m_address & ADDRESS_MASK];
63
64 // increment address register
65 if (!machine().side_effects_disabled())
66 m_address = (m_address + 1) & ADDRESS_MASK;
67
68 return data;
69 }
70
ram_w(u8 data)71 void bt431_device::ram_w(u8 data)
72 {
73 m_ram[m_address & ADDRESS_MASK] = data;
74
75 // increment address register
76 if (!machine().side_effects_disabled())
77 m_address = (m_address + 1) & ADDRESS_MASK;
78 }
79
reg_r()80 u8 bt431_device::reg_r()
81 {
82 u8 data = 0;
83
84 switch (m_address & 0xf)
85 {
86 case REG_COMMAND: data = m_command; break;
87
88 case REG_CURSOR_X_LO: data = m_cursor_x & 0xff; break;
89 case REG_CURSOR_X_HI: data = (m_cursor_x >> 8); break;
90 case REG_CURSOR_Y_LO: data = m_cursor_y & 0xff; break;
91 case REG_CURSOR_Y_HI: data = (m_cursor_y >> 8); break;
92
93 case REG_WINDOW_X_LO: data = m_window_x & 0xff; break;
94 case REG_WINDOW_X_HI: data = (m_window_x >> 8); break;
95 case REG_WINDOW_Y_LO: data = m_window_y & 0xff; break;
96 case REG_WINDOW_Y_HI: data = (m_window_y >> 8); break;
97
98 case REG_WINDOW_W_LO: data = m_window_w & 0xff; break;
99 case REG_WINDOW_W_HI: data = (m_window_w >> 8); break;
100 case REG_WINDOW_H_LO: data = m_window_h & 0xff; break;
101 case REG_WINDOW_H_HI: data = (m_window_h >> 8); break;
102
103 default:
104 LOG("read from unknown address 0x%04x (%s)\n",
105 m_address, machine().describe_context());
106 break;
107 }
108
109 // increment address register
110 if (!machine().side_effects_disabled())
111 m_address = (m_address + 1) & ADDRESS_MASK;
112
113 return data;
114 }
115
reg_w(u8 data)116 void bt431_device::reg_w(u8 data)
117 {
118 switch (m_address & 0xf)
119 {
120 case REG_COMMAND:
121 m_command = data & CR_WM;
122 LOG("64x64 cursor %s, cross hair cursor %s, cursor format %s, cross hair thickness %d\n",
123 (data & CR_D6) ? "enable" : "disable",
124 (data & CR_D5) ? "enable" : "disable",
125 (data & CR_D4) ? "OR" : "XOR",
126 ((data & CR_D1D0) << 1) + 1);
127 break;
128
129 case REG_CURSOR_X_LO:
130 m_cursor_x = (m_cursor_x & 0x0f00) | data;
131 LOG("cursor x low register: 0x%02x (%s)\n", data, machine().describe_context());
132 break;
133 case REG_CURSOR_X_HI:
134 m_cursor_x = (u16(data & 0xf) << 8) | (m_cursor_x & 0xff);
135 LOG("cursor x high register: 0x%02x (%s)\n", data, machine().describe_context());
136 break;
137 case REG_CURSOR_Y_LO:
138 m_cursor_y = (m_cursor_y & 0x0f00) | data;
139 LOG("cursor y low register: 0x%02x (%s)\n", data, machine().describe_context());
140 break;
141 case REG_CURSOR_Y_HI:
142 m_cursor_y = (u16(data & 0xf) << 8) | (m_cursor_y & 0xff);
143 LOG("cursor y high register: 0x%02x (%s)\n", data, machine().describe_context());
144 break;
145
146 case REG_WINDOW_X_LO:
147 m_window_x = (m_window_x & 0x0f00) | data;
148 LOG("window x low register: 0x%02x\n", data);
149 break;
150 case REG_WINDOW_X_HI:
151 m_window_x = (u16(data & 0xf) << 8) | (m_window_x & 0xff);
152 LOG("window x high register: 0x%02x\n", data);
153 break;
154 case REG_WINDOW_Y_LO:
155 m_window_y = (m_window_y & 0x0f00) | data;
156 LOG("window y low register: 0x%02x\n", data);
157 break;
158 case REG_WINDOW_Y_HI:
159 m_window_y = (u16(data & 0xf) << 8) | (m_window_y & 0xff);
160 LOG("window y high register: 0x%02x\n", data);
161 break;
162
163 case REG_WINDOW_W_LO:
164 m_window_w = (m_window_w & 0x0f00) | data;
165 LOG("window width low register: 0x%02x\n", data);
166 break;
167 case REG_WINDOW_W_HI:
168 m_window_w = (u16(data & 0xf) << 8) | (m_window_w & 0xff);
169 LOG("window width high register: 0x%02x\n", data);
170 break;
171 case REG_WINDOW_H_LO:
172 m_window_h = (m_window_h & 0x0f00) | data;
173 LOG("window height low register: 0x%02x\n", data);
174 break;
175 case REG_WINDOW_H_HI:
176 m_window_h = (u16(data & 0xf) << 8) | (m_window_h & 0xff);
177 LOG("window height high register: 0x%02x\n", data);
178 break;
179
180 default:
181 LOG("write to unknown address 0x%04x data 0x%02x (%s)\n",
182 m_address, data, machine().describe_context());
183 break;
184 }
185
186 // increment address register
187 m_address = (m_address + 1) & ADDRESS_MASK;
188
189 update();
190 }
191
update()192 void bt431_device::update()
193 {
194 /*
195 * The cursor (x) value to be written is calculated as follows:
196 *
197 * Cx = desired display screen (x) position + D + H - P
198 *
199 * where
200 *
201 * P = 37 if 1:1 output multiplexing, 52 if 4:1 output multiplexing,
202 * 57 if 5:1 output multiplexing
203 * D = skew (in pixels) between the output cursor data and external pixel
204 * data
205 * H = number of pixels between the first rising edge of CLOCK
206 * following the falling edge of HSYNC* to active video
207 *
208 * The P value is one-half cursor RAM width + (internal pipeline delay in
209 * clock cycles * one, four or five, depending on multiplex selection).
210 *
211 * The cursor (y) value to be written is calculated as follows:
212 *
213 * Cy = desired display screen (y) position + V - 32
214 *
215 * where
216 *
217 * V = number of scan lines from the first falling edge of HSYNC* that is
218 * two or more clock cycles after the falling edge of VSYNC* to
219 * active video.
220 *
221 * Values from $0FC0 (-64) to $0FBF (+4031) may be loaded into the
222 * cursor (y) register. The negative values ($0FC0 to $0FFF) are used
223 * in situations where V < 32, and the cursor must be moved off the
224 * top of the screen.
225 */
226 const int cursor_x = m_cursor_x + (
227 (m_command & CR_D3D2) == CR_D3D2_11 ? 37 :
228 (m_command & CR_D3D2) == CR_D3D2_41 ? 52 :
229 (m_command & CR_D3D2) == CR_D3D2_51 ? 57 : 0);
230 const int cursor_y = (m_cursor_y < 0xfc0 ? m_cursor_y : m_cursor_y - 0x1000) + 32;
231
232 // update bitmap cursor drawing rectangle
233 m_bm_window.set(cursor_x - 31, cursor_x + 32, cursor_y - 31, cursor_y + 32);
234
235 // update cross hair cursor drawing rectangles
236 const int thickness = m_command & CR_D1D0;
237 if (m_window_x == 0 && m_window_y == 0 && m_window_w == 0x0fff && m_window_h == 0x0fff)
238 {
239 // full screen cross hair cursor
240 m_ch_v.set(cursor_x - thickness, cursor_x + thickness, m_window_y, m_window_y + m_window_h - 1);
241 m_ch_h.set(m_window_x, m_window_x + m_window_w - 1, cursor_y - thickness, cursor_y + thickness);
242 }
243 else
244 {
245 // windowed cross hair cursor
246 const int window_x = m_window_x + (
247 (m_command & CR_D3D2) == CR_D3D2_11 ? 5 :
248 (m_command & CR_D3D2) == CR_D3D2_41 ? 20 :
249 (m_command & CR_D3D2) == CR_D3D2_51 ? 25 : 0);
250 const int window_y = m_window_y;
251
252 const int window_w = m_window_w + (
253 (m_command & CR_D3D2) == CR_D3D2_11 ? 2 :
254 (m_command & CR_D3D2) == CR_D3D2_41 ? 8 :
255 (m_command & CR_D3D2) == CR_D3D2_51 ? 10 : 0);
256 const int window_h = m_window_h + 2;
257
258 m_ch_v.set(cursor_x - thickness, cursor_x + thickness, window_y + 1, window_y + window_h - 2);
259 m_ch_h.set(window_x + 1, window_x + window_w - 2, cursor_y - thickness, cursor_y + thickness);
260 }
261 }
262
cur_r(unsigned x,unsigned y) const263 bool bt431_device::cur_r(unsigned x, unsigned y) const
264 {
265 bool data = false;
266
267 // cross hair cursor
268 if ((m_command & CR_D5) && (m_ch_h.contains(x, y) || m_ch_v.contains(x, y)))
269 data = true;
270
271 // bitmap cursor
272 if ((m_command & CR_D6) && m_bm_window.contains(x, y))
273 {
274 bool const bit = BIT(m_ram[(y - m_bm_window.top()) * 8 + (x - m_bm_window.left()) / 8], 7 - (x - m_bm_window.left()) % 8);
275
276 if (m_command & CR_D4)
277 data |= bit;
278 else
279 data ^= bit;
280 }
281
282 return data;
283 }
284