1 // license:BSD-3-Clause
2 // copyright-holders:Wilbert Pol
3 /**********************************************************************
4
5 Hudson/NEC HuC6260 Video Colour Encoder
6
7 The HuC6260 takes a stream of pixel data, looks up the correct
8 palette data and outputs a video stream.
9
10 The HuC6260 generates the tv control signals. A full line lasts
11 1365 "master" cycles (typically at 21.47727Mhz).
12
13 HSync is low for 237 and high for 1128 master cycles.
14 VSync is low for 4095 master cycles (3 lines).
15 VSync changes 30 master cycles after HSync would go low.
16
17 **********************************************************************/
18
19 #include "emu.h"
20 #include "huc6260.h"
21
22 #include "screen.h"
23
24 //#define VERBOSE 1
25 #include "logmacro.h"
26
27
28 #define HUC6260_HSYNC_LENGTH 237
29 #define HUC6260_HSYNC_START ( huc6260_device::WPF - HUC6260_HSYNC_LENGTH )
30
31
32 constexpr unsigned huc6260_device::PALETTE_SIZE;
33 constexpr unsigned huc6260_device::WPF;
34 constexpr unsigned huc6260_device::LPF;
35
palette_init()36 void huc6260_device::palette_init()
37 {
38 for (int i = 0; i < 512; i++)
39 {
40 int r = pal3bit( ( i >> 3 ) & 7 );
41 int g = pal3bit( ( i >> 6 ) & 7 );
42 int b = pal3bit( ( i ) & 7 );
43 int y = ( ( 66 * r + 129 * g + 25 * b + 128 ) >> 8 ) + 16;
44
45 set_pen_color(i, r, g, b);
46 set_pen_color(512 + i, y, y, y);
47 }
48 }
49
50
51 DEFINE_DEVICE_TYPE(HUC6260, huc6260_device, "huc6260", "Hudson HuC6260 VCE")
52
53
huc6260_device(const machine_config & mconfig,const char * tag,device_t * owner,uint32_t clock)54 huc6260_device::huc6260_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
55 : device_t(mconfig, HUC6260, tag, owner, clock),
56 device_palette_interface(mconfig, *this),
57 device_video_interface(mconfig, *this),
58 m_next_pixel_data_cb(*this),
59 m_time_til_next_event_cb(*this),
60 m_vsync_changed_cb(*this),
61 m_hsync_changed_cb(*this)
62 {
63 }
64
65
device_timer(emu_timer & timer,device_timer_id id,int param,void * ptr)66 void huc6260_device::device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr)
67 {
68 int vpos = screen().vpos();
69 int hpos = screen().hpos();
70 int h = m_last_h;
71 int v = m_last_v;
72 uint16_t *bitmap_line = &m_bmp->pix(v);
73
74 while ( h != hpos || v != vpos )
75 {
76 if ( m_pixel_clock == 0 )
77 {
78 g_profiler.start( PROFILER_VIDEO );
79 /* Get next pixel information */
80 m_pixel_data = m_next_pixel_data_cb();
81 g_profiler.stop();
82 }
83
84 bitmap_line[ h ] = m_palette[ m_pixel_data ] | m_greyscales;
85 m_pixel_clock = ( m_pixel_clock + 1 ) % m_pixels_per_clock;
86 h = ( h + 1 ) % WPF;
87
88 switch( h )
89 {
90 case HUC6260_HSYNC_START: /* Start of HSync */
91 m_hsync_changed_cb( 0 );
92 // if ( v == 0 )
93 // {
94 // /* Check if the screen should be resized */
95 // m_height = LPF - ( m_blur ? 1 : 0 );
96 // if ( m_height != video_screen_get_height( m_screen ) )
97 // {
98 // rectangle visible_area;
99 //
100 // /* TODO: Set proper visible area parameters */
101 // visible_area.min_x = 64;
102 // visible_area.min_y = 18;
103 // visible_area.max_x = 64 + 1024 + 64 - 1;
104 // visible_area.max_y = 18 + 242 - 1;
105 //
106 // video_screen_configure( m_screen, WPF, m_height, &visible_area, HZ_TO_ATTOSECONDS( device->clock / ( WPF * m_height ) ) );
107 // }
108 // }
109 break;
110
111 case 0: /* End of HSync */
112 m_hsync_changed_cb( 1 );
113 m_pixel_clock = 0;
114 v = ( v + 1 ) % m_height;
115 bitmap_line = &m_bmp->pix(v);
116 break;
117
118 case HUC6260_HSYNC_START + 30: /* End/Start of VSync */
119 if ( v>= m_height - 4 )
120 {
121 m_vsync_changed_cb( ( v >= m_height - 4 && v < m_height - 1 ) ? 0 : 1 );
122 }
123 break;
124 }
125 }
126
127 m_last_h = h;
128 m_last_v = v;
129
130 /* Reschedule timer */
131 if ( m_last_h < HUC6260_HSYNC_START )
132 {
133 /* Next event is start of HSync signal */
134 v = m_last_v;
135 h = HUC6260_HSYNC_START;
136 }
137 else if ( ( m_last_v == m_height - 4 || m_last_v == m_height - 1 ) && m_last_h < HUC6260_HSYNC_START + 30 )
138 {
139 /* Next event is start/end of VSync signal */
140 v = m_last_v;
141 h = HUC6260_HSYNC_START + 30;
142 }
143 else
144 {
145 /* Next event is end of HSync signal */
146 v = ( m_last_v + 1 ) % m_height;
147 h = 0;
148 }
149
150 /* Ask our slave device for time until next possible event */
151 {
152 uint16_t next_event_clocks = m_time_til_next_event_cb();
153 int event_hpos, event_vpos;
154
155 /* Adjust for pixel clocks per pixel */
156 next_event_clocks *= m_pixels_per_clock;
157
158 /* Adjust for clocks left to go for current pixel */
159 next_event_clocks += ( m_pixels_per_clock - ( m_pixel_clock + 1 ) );
160
161 event_hpos = hpos + next_event_clocks;
162 event_vpos = vpos;
163 while ( event_hpos > WPF )
164 {
165 event_vpos += 1;
166 event_hpos -= WPF;
167 }
168
169 if ( event_vpos < v || ( event_vpos == v && event_hpos <= h ) )
170 {
171 if ( event_vpos > vpos || ( event_vpos == vpos && event_hpos > hpos ) )
172 {
173 v = event_vpos;
174 h = event_hpos;
175 }
176 }
177 }
178
179 m_timer->adjust( screen().time_until_pos( v, h ) );
180 }
181
182
video_update(bitmap_ind16 & bitmap,const rectangle & cliprect)183 void huc6260_device::video_update( bitmap_ind16 &bitmap, const rectangle &cliprect )
184 {
185 copybitmap( bitmap, *m_bmp, 0, 0, 0, 0, cliprect );
186 }
187
188
189 // the battlera arcade board reads/writes the palette directly
palette_direct_read(offs_t offset)190 uint8_t huc6260_device::palette_direct_read(offs_t offset)
191 {
192 if (!(offset&1)) return m_palette[offset>>1];
193 else return m_palette[offset >> 1] >> 8;
194 }
195
palette_direct_write(offs_t offset,uint8_t data)196 void huc6260_device::palette_direct_write(offs_t offset, uint8_t data)
197 {
198 if (!(offset&1)) m_palette[offset>>1] = (m_palette[offset>>1] & 0xff00) | data;
199 else m_palette[offset>>1] = (m_palette[offset>>1] & 0x00ff) | (data<<8);
200 }
201
read(offs_t offset)202 uint8_t huc6260_device::read(offs_t offset)
203 {
204 uint8_t data = 0xFF;
205
206 switch ( offset & 7 )
207 {
208 case 0x04: /* Color table data LSB */
209 data = m_palette[ m_address ] & 0xFF;
210 break;
211
212 case 0x05: /* Color table data MSB */
213 data = 0xFE | ( m_palette[ m_address ] >> 8 );
214
215 /* Increment internal address */
216 m_address = ( m_address + 1 ) & 0x1FF;
217 break;
218 }
219
220 return data;
221 }
222
223
write(offs_t offset,uint8_t data)224 void huc6260_device::write(offs_t offset, uint8_t data)
225 {
226 switch ( offset & 7 )
227 {
228 case 0x00: /* Control register */
229 m_greyscales = (data & 0x80) << 2; // setup the greyscale base
230 m_blur = data & 0x04;
231 m_pixels_per_clock = ( data & 0x02 ) ? 2 : ( ( data & 0x01 ) ? 3 : 4 );
232 break;
233
234 case 0x02: /* Color table address LSB */
235 m_address = ( ( m_address & 0xFF00 ) | data ) & 0x1FF;
236 break;
237
238 case 0x03: /* Color table address MSB */
239 m_address = ( ( m_address & 0x00FF ) | ( data << 8 ) ) & 0x1FF;
240 break;
241
242 case 0x04: /* Color table data LSB */
243 m_palette[ m_address ] = ( ( m_palette[ m_address ] & 0xFF00 ) | data ) & 0x1FF;
244 break;
245
246 case 0x05: /* Color table data MSB */
247 m_palette[ m_address ] = ( ( m_palette[ m_address ] & 0x00FF ) | ( data << 8 ) ) & 0x1FF;
248
249 /* Increment internal address */
250 m_address = ( m_address + 1 ) & 0x1FF;
251 break;
252 }
253 }
254
255
device_start()256 void huc6260_device::device_start()
257 {
258 m_timer = timer_alloc();
259 m_bmp = std::make_unique<bitmap_ind16>(WPF, LPF);
260
261 /* Resolve callbacks */
262 m_hsync_changed_cb.resolve();
263 m_vsync_changed_cb.resolve();
264 m_next_pixel_data_cb.resolve();
265 m_time_til_next_event_cb.resolve();
266
267 /* We want to have a valid screen and valid callbacks */
268 assert( ! m_hsync_changed_cb.isnull() );
269 assert( ! m_vsync_changed_cb.isnull() );
270 assert( ! m_next_pixel_data_cb.isnull() );
271 assert( ! m_time_til_next_event_cb.isnull() );
272
273 palette_init();
274
275 save_item(NAME(m_last_h));
276 save_item(NAME(m_last_v));
277 save_item(NAME(m_height));
278 save_item(NAME(m_palette));
279 save_item(NAME(m_address));
280 save_item(NAME(m_greyscales));
281 save_item(NAME(m_blur));
282 save_item(NAME(m_pixels_per_clock));
283 save_item(NAME(m_pixel_data));
284 save_item(NAME(m_pixel_clock));
285 }
286
287
device_reset()288 void huc6260_device::device_reset()
289 {
290 m_address = 0;
291 m_greyscales = 0;
292 m_blur = 0;
293 m_pixels_per_clock = 4;
294 m_height = 263;
295 m_pixel_clock = 0;
296 memset(m_palette, 0x00, sizeof(m_palette));
297
298 m_last_v = screen().vpos();
299 m_last_h = screen().hpos();
300 m_timer->adjust( screen().time_until_pos( ( screen().vpos() + 1 ) % 263, 0 ) );
301 }
302