src/emu/video/huc6261.c
| r0 | r15985 | |
| 1 | /********************************************************************** |
| 2 | |
| 3 | Hudson/NEC HuC6261 Video Colour Encoder |
| 4 | |
| 5 | The HuC6261 generates the tv control signals. A full line lasts |
| 6 | 1365 "master" cycles (typically at 21.47727Mhz). |
| 7 | |
| 8 | HSync is low for 237 and high for 1128 master cycles. |
| 9 | VSync is low for 4095 master cycles (3 lines). |
| 10 | VSync changes 30 master cycles after HSync would go low. |
| 11 | |
| 12 | **********************************************************************/ |
| 13 | |
| 14 | #include "emu.h" |
| 15 | #include "profiler.h" |
| 16 | #include "huc6261.h" |
| 17 | |
| 18 | #define LOG 0 |
| 19 | |
| 20 | #define HUC6261_HSYNC_LENGTH 237 |
| 21 | #define HUC6261_HSYNC_START ( HUC6261_WPF - HUC6261_HSYNC_LENGTH ) |
| 22 | |
| 23 | |
| 24 | const device_type HUC6261 = &device_creator<huc6261_device>; |
| 25 | |
| 26 | |
| 27 | void huc6261_device::device_config_complete() |
| 28 | { |
| 29 | const huc6261_interface *intf = reinterpret_cast<const huc6261_interface *>(static_config()); |
| 30 | |
| 31 | if ( intf != NULL ) |
| 32 | { |
| 33 | *static_cast<huc6261_interface *>(this) = *intf; |
| 34 | } |
| 35 | else |
| 36 | { |
| 37 | } |
| 38 | } |
| 39 | |
| 40 | |
| 41 | huc6261_device::huc6261_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) |
| 42 | : device_t(mconfig, HUC6261, "HuC6261", tag, owner, clock) |
| 43 | { |
| 44 | } |
| 45 | |
| 46 | |
| 47 | void huc6261_device::device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr) |
| 48 | { |
| 49 | int vpos = m_screen->vpos(); |
| 50 | int hpos = m_screen->hpos(); |
| 51 | int h = m_last_h; |
| 52 | int v = m_last_v; |
| 53 | UINT32 *bitmap_line = &m_bmp->pix32(v); |
| 54 | |
| 55 | while ( h != hpos || v != vpos ) |
| 56 | { |
| 57 | if ( m_pixel_clock == 0 ) |
| 58 | { |
| 59 | g_profiler.start( PROFILER_VIDEO ); |
| 60 | /* Get next pixel information */ |
| 61 | m_pixel_data = 0; //m_get_next_pixel_data( 0, 0xffff ); |
| 62 | g_profiler.stop(); |
| 63 | } |
| 64 | |
| 65 | bitmap_line[ h ] = m_palette[ m_pixel_data ]; |
| 66 | m_pixel_clock = ( m_pixel_clock + 1 ) % m_pixels_per_clock; |
| 67 | h = ( h + 1 ) % HUC6261_WPF; |
| 68 | |
| 69 | switch( h ) |
| 70 | { |
| 71 | case HUC6261_HSYNC_START: /* Start of HSync */ |
| 72 | m_huc6270_a->hsync_changed( 0 ); |
| 73 | m_huc6270_b->hsync_changed( 0 ); |
| 74 | // if ( v == 0 ) |
| 75 | // { |
| 76 | // /* Check if the screen should be resized */ |
| 77 | // m_height = HUC6261_LPF - ( m_blur ? 1 : 0 ); |
| 78 | // if ( m_height != video_screen_get_height( m_screen ) ) |
| 79 | // { |
| 80 | // rectangle visible_area; |
| 81 | // |
| 82 | // /* TODO: Set proper visible area parameters */ |
| 83 | // visible_area.min_x = 64; |
| 84 | // visible_area.min_y = 18; |
| 85 | // visible_area.max_x = 64 + 1024 + 64 - 1; |
| 86 | // visible_area.max_y = 18 + 242 - 1; |
| 87 | // |
| 88 | // video_screen_configure( m_screen, HUC6261_WPF, m_height, &visible_area, HZ_TO_ATTOSECONDS( device->clock / ( HUC6261_WPF * m_height ) ) ); |
| 89 | // } |
| 90 | // } |
| 91 | break; |
| 92 | |
| 93 | case 0: /* End of HSync */ |
| 94 | m_huc6270_a->hsync_changed( 1 ); |
| 95 | m_huc6270_b->hsync_changed( 1 ); |
| 96 | m_pixel_clock = 0; |
| 97 | v = ( v + 1 ) % m_height; |
| 98 | bitmap_line = &m_bmp->pix32(v); |
| 99 | break; |
| 100 | |
| 101 | case HUC6261_HSYNC_START + 30: /* End/Start of VSync */ |
| 102 | if ( v>= m_height - 4 ) |
| 103 | { |
| 104 | int vsync = ( v >= m_height - 4 && v < m_height - 1 ) ? 0 : 1; |
| 105 | |
| 106 | m_huc6270_a->vsync_changed( vsync ); |
| 107 | m_huc6270_b->vsync_changed( vsync ); |
| 108 | } |
| 109 | break; |
| 110 | } |
| 111 | } |
| 112 | |
| 113 | m_last_h = h; |
| 114 | m_last_v = v; |
| 115 | |
| 116 | /* Reschedule timer */ |
| 117 | if ( m_last_h < HUC6261_HSYNC_START ) |
| 118 | { |
| 119 | /* Next event is start of HSync signal */ |
| 120 | v = m_last_v; |
| 121 | h = HUC6261_HSYNC_START; |
| 122 | } |
| 123 | else if ( ( m_last_v == m_height - 4 || m_last_v == m_height - 1 ) && m_last_h < HUC6261_HSYNC_START + 30 ) |
| 124 | { |
| 125 | /* Next event is start/end of VSync signal */ |
| 126 | v = m_last_v; |
| 127 | h = HUC6261_HSYNC_START + 30; |
| 128 | } |
| 129 | else |
| 130 | { |
| 131 | /* Next event is end of HSync signal */ |
| 132 | v = ( m_last_v + 1 ) % m_height; |
| 133 | h = 0; |
| 134 | } |
| 135 | |
| 136 | /* Ask our slave device for time until next possible event */ |
| 137 | { |
| 138 | UINT16 next_event_clocks = HUC6261_WPF; //m_get_time_til_next_event( 0, 0xffff ); |
| 139 | int event_hpos, event_vpos; |
| 140 | |
| 141 | /* Adjust for pixel clocks per pixel */ |
| 142 | next_event_clocks *= m_pixels_per_clock; |
| 143 | |
| 144 | /* Adjust for clocks left to go for current pixel */ |
| 145 | next_event_clocks += ( m_pixels_per_clock - ( m_pixel_clock + 1 ) ); |
| 146 | |
| 147 | event_hpos = hpos + next_event_clocks; |
| 148 | event_vpos = vpos; |
| 149 | while ( event_hpos > HUC6261_WPF ) |
| 150 | { |
| 151 | event_vpos += 1; |
| 152 | event_hpos -= HUC6261_WPF; |
| 153 | } |
| 154 | |
| 155 | if ( event_vpos < v || ( event_vpos == v && event_hpos <= h ) ) |
| 156 | { |
| 157 | if ( event_vpos > vpos || ( event_vpos == vpos && event_hpos > hpos ) ) |
| 158 | { |
| 159 | v = event_vpos; |
| 160 | h = event_hpos; |
| 161 | } |
| 162 | } |
| 163 | } |
| 164 | |
| 165 | m_timer->adjust( m_screen->time_until_pos( v, h ) ); |
| 166 | } |
| 167 | |
| 168 | |
| 169 | void huc6261_device::video_update( bitmap_rgb32 &bitmap, const rectangle &cliprect ) |
| 170 | { |
| 171 | copybitmap( bitmap, *m_bmp, 0, 0, 0, 0, cliprect ); |
| 172 | } |
| 173 | |
| 174 | |
| 175 | READ16_MEMBER( huc6261_device::read ) |
| 176 | { |
| 177 | UINT16 data = 0xFFFF; |
| 178 | |
| 179 | switch ( offset & 1 ) |
| 180 | { |
| 181 | /* Status info */ |
| 182 | case 0x00: |
| 183 | { |
| 184 | UINT16 vpos = m_screen->vpos(); |
| 185 | UINT16 hpos = m_screen->hpos(); |
| 186 | |
| 187 | data = ( vpos << 5 ) | ( m_register & 0x1F); |
| 188 | |
| 189 | if ( vpos >= 22 && vpos < 262 && hpos < HUC6261_HSYNC_START ) |
| 190 | { |
| 191 | data |= 0x8000; |
| 192 | } |
| 193 | } |
| 194 | break; |
| 195 | |
| 196 | /* Register contents(?) */ |
| 197 | case 0x01: |
| 198 | switch( m_register ) |
| 199 | { |
| 200 | case 0x00: |
| 201 | data = m_control; |
| 202 | break; |
| 203 | |
| 204 | case 0x01: |
| 205 | data = m_address; |
| 206 | break; |
| 207 | |
| 208 | case 0x02: |
| 209 | case 0x03: |
| 210 | data = m_palette_latch; |
| 211 | m_address = ( m_address + 1 ) & 0x1FF; |
| 212 | m_palette_latch = m_palette[ m_address ]; |
| 213 | break; |
| 214 | |
| 215 | case 0x08: |
| 216 | data = m_priority[4] | ( m_priority[5] << 4 ) | ( m_priority[6] << 8 ); |
| 217 | break; |
| 218 | |
| 219 | case 0x09: |
| 220 | data = m_priority[0] | ( m_priority[1] << 4 ) | ( m_priority[2] << 8 ) | ( m_priority[3] << 12 );; |
| 221 | break; |
| 222 | } |
| 223 | break; |
| 224 | } |
| 225 | |
| 226 | return data; |
| 227 | } |
| 228 | |
| 229 | |
| 230 | WRITE16_MEMBER( huc6261_device::write ) |
| 231 | { |
| 232 | switch ( offset & 1 ) |
| 233 | { |
| 234 | /* Register */ |
| 235 | case 0x00: |
| 236 | m_register = data; |
| 237 | break; |
| 238 | |
| 239 | case 0x01: |
| 240 | logerror("huc6261: writing 0x%04x to register 0x%02x\n", data, m_register ); |
| 241 | switch( m_register ) |
| 242 | { |
| 243 | /* Control register */ |
| 244 | // -x-------------- Enable HuC6271: 0 - disabled, 1 - enabled |
| 245 | // --x------------- Enable HuC6272 BG3: 0 - disabled, 1 - enabled |
| 246 | // ---x------------ Enable HuC6272 BG2: 0 - disabled, 1 - enabled |
| 247 | // ----x----------- Enable Huc6272 BG1: 0 - disabled, 1 - enabled |
| 248 | // -----x---------- Enable HuC6272 BG0: 0 - disabled, 1 - enabled |
| 249 | // ------x--------- Enable HuC6270 SPR: 0 - disabled, 1 - enabled |
| 250 | // -------x-------- Enable HuC6270 BG: 0 - disabled, 1 - enabled |
| 251 | // --------x------- Number of SPR colors?: 0 - 16, 1 - 256 |
| 252 | // ---------x------ Number of BG colors?: 0 - 16, 1 - 256 |
| 253 | // ------------x--- Dot clock: 0 - 5MHz, 1 - 7MHz |
| 254 | // -------------x-- Synchronization: 0 - internal, 1 - external |
| 255 | // --------------xx Screen height: 00 - 262 lines, 01 - 263 lines, 10 - interlace, 11 - unknown/undefined |
| 256 | case 0x00: |
| 257 | m_control = data; |
| 258 | m_pixels_per_clock = ( data & 0x04 ) ? 3 : 4; |
| 259 | break; |
| 260 | |
| 261 | // Palette address |
| 262 | case 0x01: |
| 263 | m_address = data & 0x1FF; |
| 264 | m_palette_latch = m_palette[ m_address ]; |
| 265 | break; |
| 266 | |
| 267 | // Palette data |
| 268 | case 0x02: |
| 269 | m_palette_latch = data; |
| 270 | m_palette[ m_address ] = m_palette_latch; |
| 271 | m_address = ( m_address + 1 ) & 0x1FF; |
| 272 | break; |
| 273 | |
| 274 | // Palette offset 0 |
| 275 | case 0x04: |
| 276 | break; |
| 277 | |
| 278 | // Palette offset 1 |
| 279 | case 0x05: |
| 280 | break; |
| 281 | |
| 282 | // Palette offset 2 |
| 283 | case 0x06: |
| 284 | break; |
| 285 | |
| 286 | // Palette offset 3 |
| 287 | case 0x07: |
| 288 | break; |
| 289 | |
| 290 | // Priority 0 |
| 291 | // -----xxx-------- HuC6271 Rainbow priority |
| 292 | // ---------xxx---- HuC6270 SPR priority |
| 293 | // -------------xxx HuC6270 BG priority |
| 294 | case 0x08: |
| 295 | m_priority[4] = ( data >> 0 ) & 0x07; |
| 296 | m_priority[5] = ( data >> 4 ) & 0x07; |
| 297 | m_priority[6] = ( data >> 8 ) & 0x07; |
| 298 | break; |
| 299 | |
| 300 | // Priority 1 |
| 301 | // -xxx------------ HuC6272 BG3 priority |
| 302 | // -----xxx-------- HuC6272 BG2 priority |
| 303 | // ---------xxx---- HuC6272 BG1 priority |
| 304 | // -------------xxx HuC6272 BG0 priority |
| 305 | case 0x09: |
| 306 | m_priority[0] = ( data >> 0 ) & 0x07; |
| 307 | m_priority[1] = ( data >> 4 ) & 0x07; |
| 308 | m_priority[2] = ( data >> 8 ) & 0x07; |
| 309 | m_priority[3] = ( data >> 12 ) & 0x07; |
| 310 | break; |
| 311 | |
| 312 | // Chroma key Y |
| 313 | case 0x0A: |
| 314 | break; |
| 315 | |
| 316 | // Chroma key U |
| 317 | case 0x0B: |
| 318 | break; |
| 319 | |
| 320 | // Chroma key V |
| 321 | case 0x0C: |
| 322 | break; |
| 323 | |
| 324 | // |
| 325 | case 0x0D: |
| 326 | break; |
| 327 | |
| 328 | // |
| 329 | case 0x0E: |
| 330 | break; |
| 331 | |
| 332 | // |
| 333 | case 0x0F: |
| 334 | break; |
| 335 | |
| 336 | // |
| 337 | case 0x10: |
| 338 | break; |
| 339 | |
| 340 | // |
| 341 | case 0x11: |
| 342 | break; |
| 343 | |
| 344 | // |
| 345 | case 0x12: |
| 346 | break; |
| 347 | |
| 348 | // |
| 349 | case 0x13: |
| 350 | break; |
| 351 | |
| 352 | // |
| 353 | case 0x14: |
| 354 | break; |
| 355 | |
| 356 | // |
| 357 | case 0x15: |
| 358 | break; |
| 359 | } |
| 360 | break; |
| 361 | } |
| 362 | } |
| 363 | |
| 364 | |
| 365 | void huc6261_device::device_start() |
| 366 | { |
| 367 | /* Make sure we are supplied all our mandatory tags */ |
| 368 | assert( screen_tag != NULL ); |
| 369 | assert( huc6270_a_tag != NULL ); |
| 370 | assert( huc6270_b_tag != NULL ); |
| 371 | |
| 372 | m_timer = timer_alloc(); |
| 373 | m_screen = machine().device<screen_device>( screen_tag ); |
| 374 | m_huc6270_a = machine().device<huc6270_device>( huc6270_a_tag ); |
| 375 | m_huc6270_b = machine().device<huc6270_device>( huc6270_b_tag ); |
| 376 | |
| 377 | m_bmp = auto_bitmap_rgb32_alloc( machine(), HUC6261_WPF, HUC6261_LPF ); |
| 378 | |
| 379 | /* We want to have valid devices */ |
| 380 | assert( m_screen != NULL ); |
| 381 | assert( m_huc6270_a != NULL ); |
| 382 | assert( m_huc6270_b != NULL ); |
| 383 | |
| 384 | save_item(NAME(m_last_h)); |
| 385 | save_item(NAME(m_last_v)); |
| 386 | save_item(NAME(m_height)); |
| 387 | save_item(NAME(m_palette)); |
| 388 | save_item(NAME(m_palette_latch)); |
| 389 | save_item(NAME(m_address)); |
| 390 | save_item(NAME(m_register)); |
| 391 | save_item(NAME(m_control)); |
| 392 | save_item(NAME(m_priority)); |
| 393 | save_item(NAME(m_pixels_per_clock)); |
| 394 | save_item(NAME(m_pixel_data)); |
| 395 | save_item(NAME(m_pixel_clock)); |
| 396 | } |
| 397 | |
| 398 | |
| 399 | void huc6261_device::device_reset() |
| 400 | { |
| 401 | m_register = 0; |
| 402 | m_pixels_per_clock = 4; |
| 403 | m_height = 263; |
| 404 | |
| 405 | m_last_v = m_screen->vpos(); |
| 406 | m_last_h = m_screen->hpos(); |
| 407 | m_timer->adjust( m_screen->time_until_pos( ( m_screen->vpos() + 1 ) % 263, 0 ) ); |
| 408 | } |
| 409 | |
src/emu/video/huc6261.h
| r0 | r15985 | |
| 1 | /********************************************************************** |
| 2 | |
| 3 | Hudson/NEC HuC6261 interface and definitions |
| 4 | |
| 5 | **********************************************************************/ |
| 6 | |
| 7 | |
| 8 | #ifndef __HUC6261_H_ |
| 9 | #define __HUC6261_H_ |
| 10 | |
| 11 | #include "emu.h" |
| 12 | #include "machine/devhelpr.h" |
| 13 | #include "video/huc6270.h" |
| 14 | |
| 15 | |
| 16 | /* Screen timing stuff */ |
| 17 | #define HUC6261_WPF 1365 /* width of a line in frame including blanking areas */ |
| 18 | #define HUC6261_LPF 263 /* max number of lines in a single frame */ |
| 19 | |
| 20 | |
| 21 | #define MCFG_HUC6261_ADD( _tag, clock, _intrf ) \ |
| 22 | MCFG_DEVICE_ADD( _tag, HUC6261, clock ) \ |
| 23 | MCFG_DEVICE_CONFIG( _intrf ) |
| 24 | |
| 25 | |
| 26 | typedef struct _huc6261_interface huc6261_interface; |
| 27 | struct _huc6261_interface |
| 28 | { |
| 29 | /* Tag for the screen we will be drawing on */ |
| 30 | const char *screen_tag; |
| 31 | |
| 32 | /* Tags for the 2 HuC6270 devices */ |
| 33 | const char *huc6270_a_tag; |
| 34 | const char *huc6270_b_tag; |
| 35 | }; |
| 36 | |
| 37 | |
| 38 | class huc6261_device : public device_t, |
| 39 | public huc6261_interface |
| 40 | { |
| 41 | public: |
| 42 | // construction/destruction |
| 43 | huc6261_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); |
| 44 | |
| 45 | void video_update(bitmap_rgb32 &bitmap, const rectangle &cliprect); |
| 46 | DECLARE_READ16_MEMBER( read ); |
| 47 | DECLARE_WRITE16_MEMBER( write ); |
| 48 | |
| 49 | protected: |
| 50 | // device-level overrides |
| 51 | virtual void device_config_complete(); |
| 52 | virtual void device_start(); |
| 53 | virtual void device_reset(); |
| 54 | virtual void device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr); |
| 55 | |
| 56 | private: |
| 57 | screen_device *m_screen; |
| 58 | huc6270_device *m_huc6270_a; |
| 59 | huc6270_device *m_huc6270_b; |
| 60 | int m_last_h; |
| 61 | int m_last_v; |
| 62 | int m_height; |
| 63 | |
| 64 | UINT16 m_palette[512]; |
| 65 | UINT16 m_address; |
| 66 | UINT16 m_palette_latch; |
| 67 | UINT16 m_register; |
| 68 | UINT16 m_control; |
| 69 | UINT8 m_priority[7]; |
| 70 | |
| 71 | UINT8 m_pixels_per_clock; /* Number of pixels to output per colour clock */ |
| 72 | UINT16 m_pixel_data; |
| 73 | UINT8 m_pixel_clock; |
| 74 | |
| 75 | emu_timer *m_timer; |
| 76 | bitmap_rgb32 *m_bmp; |
| 77 | }; |
| 78 | |
| 79 | |
| 80 | extern const device_type HUC6261; |
| 81 | |
| 82 | |
| 83 | #endif |