trunk/src/mess/video/vtvideo.c
| r31342 | r31343 | |
| 1 | 1 | /********************************************************************** |
| 2 | DEC VT Terminal video emulation |
| 3 | [ DC012 and DC011 emulation ] |
| 2 | 4 | |
| 3 | | DEC VT Terminal video emulation |
| 4 | | [ DC012 and DC011 emulation ] |
| 5 | Copyright MESS Team. |
| 6 | Visit http://mamedev.org for licensing and usage restrictions. |
| 5 | 7 | |
| 6 | | 01/05/2009 Initial implementation [Miodrag Milanovic] |
| 7 | | Portions (2013, 2014) by Karl-Ludwig Deisenhofer. |
| 8 | 01/05/2009 Initial implementation [Miodrag Milanovic] |
| 9 | Portions (2013, 2014) by Karl-Ludwig Deisenhofer. |
| 8 | 10 | |
| 9 | | DEC VIDEO : STATE AS OF MARCH 2014 |
| 10 | | ---------------------------------- |
| 11 | | - TESTS REQUIRED : do line and character attributes (plus combinations) match real hardware? |
| 11 | DEC VIDEO : STATE AS OF JULY 2014 |
| 12 | --------------------------------- |
| 13 | Code developed with Rainbow-100 hardware in mind (a PC-like machine with a video circuit similar to a VT100 equipped w. AVO) |
| 14 | Details (termination characters, option hardware, MHFU watchdog) differ when compared to VT. |
| 12 | 15 | |
| 16 | As of July 2014, the Rainbow video section is more mature than the 'VT100_VIDEO' part (essentially unchanged since 2009). |
| 17 | List of features not yet ported to VT: line doubler; 48 line mode; soft scroll; intensity / palette, AVO (ext.attributes) |
| 18 | |
| 19 | FIXME: work out the differences and identify common code between VT and Rainbow. Unify different code trees. |
| 20 | |
| 21 | - REQUIRED TODOS / TESTS : |
| 22 | * do line and character attributes (plus combinations) match real hardware? |
| 23 | * how does the AVO fit in? |
| 24 | |
| 13 | 25 | - SCROLLING REGIONS / SPLIT SCREEN SCROLLING UNTESTED (if you open > 1 file with the VAX editor EDT) |
| 14 | 26 | See VT100 Technical Manual: 4.7.4 Address Shuffling to 4.7.9 Split Screen Smooth Scrolling. |
| 15 | 27 | More on scrolling regions: Rainbow 100 B technical documentation (QV069-GZ) April 1985 page 22 |
| 28 | |
| 29 | - NEW - INTERLACED MODE (Rainbow only): |
| 30 | Vertical resolution increases from 240 to 480, while the refresh rate halves (flickers on CRTs). |
| 31 | To accomplish this, the display controller repeats even lines in odd scans. |
| 32 | VTVIDEO activates line doubling in 24 line, interlaced mode only. |
| 33 | |
| 34 | Although the DC12 has the ability to display 48 lines, most units are low on screen RAM and |
| 35 | won't even show 80 x 48. -> REASON: (83 x 48 = 3984 Byte) > (screen RAM) minus 'scratch area' |
| 36 | On a VT-180, BIOS scratch requires up to 700 bytes used for SETUP, flags, SILO, keyboard. |
| 37 | |
| 38 | - POSSIBLE IMPROVEMENTS: |
| 39 | |
| 40 | * exact colors for different VR201 monitors ('paper white', green and amber) |
| 16 | 41 | |
| 17 | | - INTERLACED MODE UNEMULATED: |
| 18 | | There is no RAM (on a Rainbow-100) to hold twice as many vertical lines, so the video chip |
| 19 | | doubles existing ones. Flickers on CRTs and is of dubious value on modern hosts. |
| 42 | * ACCURATE VIDEO DELAYS: |
| 43 | Position of the first visible scanline (relative to the vertical reset) depends on |
| 44 | content of fill bytes at the beginning of screen RAM. |
| 20 | 45 | |
| 21 | | - FUN WITH DC011 / DC012 REGISTERS (clues wanted): |
| 22 | | Public domain program SQUEEZE.COM (for DEC-100-B) _compresses_ display to X/2 and Y/2 |
| 23 | | so picture takes a quarter of the original screen. How does it accomplish this? |
| 46 | Six invisible, linked lines are initially provided (at location $EE000+ on a Rainbow). |
| 47 | Real-world DC hardware parses the (circular) chain until interrupted by blanking. |
| 48 | BIOS assumes 2 lines are omitted @ 60 and 5 lines are at 50 Hertz (-> longer blank). |
| 24 | 49 | |
| 25 | | - IMPROVEMENTS: |
| 26 | | exact colors for different VR201 monitors ('paper white', green and amber) |
| 50 | VTVIDEO keeps it simple and skips up to 6 lines considered 'illegal' (offset < $12). |
| 51 | Works even in cases where undocumented delays or lines are poked (SQUINT; VIDEO.PAS). |
| 27 | 52 | |
| 28 | | Copyright MESS Team. |
| 29 | | Visit http://mamedev.org for licensing and usage restrictions. |
| 30 | | |
| 53 | Accurate timings for 4 modes (from VT-180 manual 6-30 on): |
| 54 | Vertical frequency = (2 * HF = 31.468 Khz) / DIVIDER |
| 55 | - 50 NI: (2 * HF = 31.468 Khz) / 630 |
| 56 | - 50 interlaced: (2 * HF = 31.468 Khz) / 629 |
| 57 | - 60 NI: (2 * HF = 31.468 Khz) / 524 |
| 58 | - 60 interlaced: (2 * HF = 31.468 Khz) / 525 |
| 31 | 59 | **********************************************************************/ |
| 32 | 60 | |
| 33 | 61 | #include "emu.h" |
| 34 | 62 | #include "video/vtvideo.h" |
| 35 | 63 | |
| 36 | 64 | /*************************************************************************** |
| 37 | | PARAMETERS |
| 65 | PARAMETERS |
| 38 | 66 | ***************************************************************************/ |
| 39 | 67 | |
| 40 | 68 | #define VERBOSE 0 |
| r31342 | r31343 | |
| 47 | 75 | |
| 48 | 76 | |
| 49 | 77 | vt100_video_device::vt100_video_device(const machine_config &mconfig, device_type type, const char *name, const char *tag, device_t *owner, UINT32 clock, const char *shortname, const char *source) |
| 50 | | : device_t(mconfig, type, name, tag, owner, clock, shortname, source), |
| 51 | | device_video_interface(mconfig, *this), |
| 52 | | m_read_ram(*this), |
| 53 | | m_write_clear_video_interrupt(*this), |
| 54 | | m_char_rom_tag(""), |
| 55 | | m_palette(*this, "palette") |
| 78 | : device_t(mconfig, type, name, tag, owner, clock, shortname, source), |
| 79 | device_video_interface(mconfig, *this), |
| 80 | m_read_ram(*this), |
| 81 | m_write_clear_video_interrupt(*this), |
| 82 | m_char_rom_tag(""), |
| 83 | m_palette(*this, "palette") |
| 56 | 84 | { |
| 57 | 85 | } |
| 58 | 86 | |
| 59 | 87 | |
| 60 | 88 | vt100_video_device::vt100_video_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) |
| 61 | | : device_t(mconfig, VT100_VIDEO, "VT100 Video", tag, owner, clock, "vt100_video", __FILE__), |
| 62 | | device_video_interface(mconfig, *this), |
| 63 | | m_read_ram(*this), |
| 64 | | m_write_clear_video_interrupt(*this), |
| 65 | | m_char_rom_tag(""), |
| 66 | | m_palette(*this, "palette") |
| 89 | : device_t(mconfig, VT100_VIDEO, "VT100 Video", tag, owner, clock, "vt100_video", __FILE__), |
| 90 | device_video_interface(mconfig, *this), |
| 91 | m_read_ram(*this), |
| 92 | m_write_clear_video_interrupt(*this), |
| 93 | m_char_rom_tag(""), |
| 94 | m_palette(*this, "palette") |
| 67 | 95 | { |
| 68 | 96 | } |
| 69 | 97 | |
| 70 | 98 | |
| 71 | 99 | rainbow_video_device::rainbow_video_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) |
| 72 | | : vt100_video_device(mconfig, RAINBOW_VIDEO, "Rainbow Video", tag, owner, clock, "rainbow_video", __FILE__) |
| 100 | : vt100_video_device(mconfig, RAINBOW_VIDEO, "Rainbow Video", tag, owner, clock, "rainbow_video", __FILE__) |
| 73 | 101 | { |
| 74 | 102 | } |
| 75 | 103 | |
| r31342 | r31343 | |
| 88 | 116 | assert(m_gfx != NULL); |
| 89 | 117 | |
| 90 | 118 | // LBA7 is scan line frequency update |
| 91 | | machine().scheduler().timer_pulse(attotime::from_nsec(31778), timer_expired_delegate(FUNC(vt100_video_device::lba7_change),this)); |
| 119 | machine().scheduler().timer_pulse(attotime::from_nsec(31778), timer_expired_delegate(FUNC(vt100_video_device::lba7_change), this)); |
| 92 | 120 | |
| 93 | 121 | save_item(NAME(m_lba7)); |
| 94 | 122 | save_item(NAME(m_scroll_latch)); |
| r31342 | r31343 | |
| 98 | 126 | save_item(NAME(m_columns)); |
| 99 | 127 | save_item(NAME(m_height)); |
| 100 | 128 | save_item(NAME(m_height_MAX)); |
| 101 | | save_item(NAME(m_skip_lines)); |
| 129 | save_item(NAME(m_fill_lines)); |
| 102 | 130 | save_item(NAME(m_frequency)); |
| 103 | 131 | save_item(NAME(m_interlaced)); |
| 104 | 132 | } |
| r31342 | r31343 | |
| 127 | 155 | |
| 128 | 156 | m_columns = 80; |
| 129 | 157 | m_frequency = 60; |
| 158 | |
| 130 | 159 | m_interlaced = 1; |
| 131 | | m_skip_lines = 2; // for 60Hz |
| 160 | m_fill_lines = 2; // for 60Hz |
| 161 | recompute_parameters(); |
| 132 | 162 | } |
| 133 | 163 | |
| 134 | 164 | // ****** RAINBOW ****** |
| r31342 | r31343 | |
| 155 | 185 | m_basic_attribute = 0; |
| 156 | 186 | |
| 157 | 187 | m_columns = 80; |
| 158 | | m_frequency = 60; |
| 159 | | m_interlaced = 1; |
| 160 | | m_skip_lines = 2; // for 60Hz |
| 188 | |
| 189 | m_frequency = 60; |
| 190 | |
| 191 | m_interlaced = 1; |
| 192 | m_fill_lines = 2; // for 60Hz (not in use any longer -> detected) |
| 193 | recompute_parameters(); |
| 161 | 194 | } |
| 162 | 195 | |
| 163 | 196 | /*************************************************************************** |
| 164 | | IMPLEMENTATION |
| 197 | IMPLEMENTATION |
| 165 | 198 | ***************************************************************************/ |
| 166 | 199 | |
| 167 | 200 | // Also used by Rainbow-100 ************ |
| 168 | 201 | void vt100_video_device::recompute_parameters() |
| 169 | 202 | { |
| 203 | rectangle visarea; |
| 170 | 204 | int horiz_pix_total = 0; |
| 171 | | int vert_pix_total = 0; |
| 172 | | rectangle visarea; |
| 173 | 205 | |
| 174 | | vert_pix_total = m_height * 10; |
| 206 | // RAINBOW: 240 scan lines in non-interlaced mode (480 interlaced). VT-100 : same (?) |
| 207 | m_linedoubler = false; // 24 "true" lines (240) -OR- 48 lines with NO ghost lines (true 480) |
| 208 | if ((m_interlaced) && (m_height == 24)) |
| 209 | m_linedoubler = true; // 24 lines with 'double scan' (false 480) |
| 175 | 210 | |
| 176 | | if (m_columns == 132) { |
| 177 | | horiz_pix_total = m_columns * 9; // display 1 less filler pixel in 132 char. mode (DEC-100) |
| 178 | | } |
| 179 | | else { |
| 211 | int vert_pix_total = ((m_linedoubler == false) ? m_height : m_height_MAX) * 10; |
| 212 | |
| 213 | if (m_columns == 132) |
| 214 | horiz_pix_total = m_columns * 9; // display 1 less filler pixel in 132 char. mode |
| 215 | else |
| 180 | 216 | horiz_pix_total = m_columns * 10; // normal 80 character mode. |
| 181 | | } |
| 182 | 217 | |
| 183 | 218 | visarea.set(0, horiz_pix_total - 1, 0, vert_pix_total - 1); |
| 219 | machine().first_screen()->configure(horiz_pix_total, vert_pix_total, visarea, HZ_TO_ATTOSECONDS((m_interlaced == 0) ? m_frequency : (m_frequency/2) )); |
| 184 | 220 | |
| 185 | | m_screen->configure(horiz_pix_total, vert_pix_total, visarea, m_screen->frame_period().attoseconds); |
| 221 | if (VERBOSE) |
| 222 | { |
| 223 | printf("\n(RECOMPUTE) HPIX: %d - VPIX: %d", horiz_pix_total, vert_pix_total); |
| 224 | printf("\n(RECOMPUTE) FREQUENCY: %d", (m_interlaced == 0) ? m_frequency : (m_frequency / 2)); |
| 225 | if (m_interlaced) |
| 226 | printf("\n(RECOMPUTE) * INTERLACED *"); |
| 227 | if (m_linedoubler) |
| 228 | printf("\n(RECOMPUTE) * LINEDOUBLER *"); |
| 229 | } |
| 186 | 230 | } |
| 187 | 231 | |
| 188 | 232 | |
| 189 | | READ8_MEMBER( vt100_video_device::lba7_r ) |
| 233 | READ8_MEMBER(vt100_video_device::lba7_r) |
| 190 | 234 | { |
| 191 | 235 | return m_lba7; |
| 192 | 236 | } |
| 193 | 237 | |
| 194 | 238 | |
| 195 | 239 | // Also used by Rainbow-100 ************ |
| 196 | | WRITE8_MEMBER( vt100_video_device::dc012_w ) |
| 240 | WRITE8_MEMBER(vt100_video_device::dc012_w) |
| 197 | 241 | { |
| 242 | |
| 198 | 243 | // TODO: writes to 10C/0C should be treated differently (emulation disables the watchdog too often). |
| 199 | 244 | // - see 3.1.3.9.5 DC012 Programming Information (PC-100 spec) |
| 200 | 245 | if (data == 0) // MHFU is disabled by writing 00 to port 010C. |
| 201 | 246 | { |
| 202 | | //if (MHFU_FLAG == true) |
| 203 | | // printf("MHFU *** DISABLED *** \n"); |
| 204 | 247 | MHFU_FLAG = false; |
| 205 | 248 | MHFU_counter = 0; |
| 249 | |
| 250 | if (VERBOSE) |
| 251 | { |
| 252 | if (MHFU_FLAG == true) |
| 253 | printf("MHFU *** DISABLED *** %ul \n", offset); |
| 206 | 254 | } |
| 255 | } |
| 207 | 256 | else |
| 208 | | { // RESET |
| 209 | | //if (MHFU_FLAG == false) |
| 210 | | // printf("MHFU ___ENABLED___ \n"); |
| 257 | { |
| 258 | // RESET |
| 211 | 259 | MHFU_FLAG = true; |
| 260 | MHFU_counter = 0; |
| 212 | 261 | |
| 213 | | MHFU_counter = 0; |
| 262 | if (VERBOSE) |
| 263 | { |
| 264 | if (MHFU_FLAG == false) |
| 265 | printf("MHFU ___ENABLED___ %ul \n", offset); |
| 214 | 266 | } |
| 267 | } |
| 215 | 268 | |
| 216 | 269 | if (!(data & 0x08)) |
| 217 | 270 | { |
| r31342 | r31343 | |
| 239 | 292 | case 0x09: |
| 240 | 293 | // clear vertical frequency interrupt; |
| 241 | 294 | m_write_clear_video_interrupt(0); |
| 242 | | |
| 243 | 295 | break; |
| 244 | 296 | case 0x0a: |
| 245 | 297 | // set reverse field on |
| r31342 | r31343 | |
| 264 | 316 | |
| 265 | 317 | if (m_height_MAX == 25) break; // Abort on VT-100 for now. |
| 266 | 318 | |
| 267 | | if (m_height != 24) |
| 268 | | { |
| 269 | 319 | m_height = 24; // (DEC Rainbow 100) : 24 line display |
| 270 | 320 | recompute_parameters(); |
| 271 | | } |
| 272 | 321 | break; |
| 273 | 322 | |
| 274 | 323 | case 0x0e: |
| 275 | | m_blink_flip_flop = 0; // 'unsupported' DC012 command. Turn blink flip-flop off. |
| 324 | m_blink_flip_flop = 0; // 'unsupported' DC012 command. Turns blink flip-flop off (11XX). |
| 276 | 325 | break; |
| 277 | 326 | |
| 278 | 327 | case 0x0f: |
| r31342 | r31343 | |
| 284 | 333 | // Abort on VT-100 for now. |
| 285 | 334 | if (m_height_MAX == 25) break; |
| 286 | 335 | |
| 287 | | if (m_height != 48) |
| 288 | | { |
| 289 | 336 | m_height = 48; // (DEC Rainbow 100) : 48 line display |
| 290 | 337 | recompute_parameters(); |
| 291 | | } |
| 292 | 338 | break; |
| 293 | 339 | } |
| 294 | 340 | } |
| 295 | 341 | } |
| 296 | 342 | |
| 297 | | |
| 298 | | WRITE8_MEMBER( vt100_video_device::dc011_w ) |
| 343 | // Writing to DC011 resets internal counters (& disturbs display) on real hardware. |
| 344 | WRITE8_MEMBER(vt100_video_device::dc011_w) |
| 299 | 345 | { |
| 300 | | if (!BIT(data, 5)) |
| 346 | if (!BIT(data, 5)) |
| 301 | 347 | { |
| 302 | | UINT8 col = m_columns; |
| 348 | m_interlaced = 1; |
| 349 | |
| 303 | 350 | if (!BIT(data, 4)) |
| 304 | | { |
| 305 | 351 | m_columns = 80; |
| 306 | | } |
| 307 | 352 | else |
| 308 | | { |
| 309 | 353 | m_columns = 132; |
| 310 | 354 | } |
| 311 | | if (col != m_columns) |
| 312 | | { |
| 313 | | recompute_parameters(); |
| 314 | | } |
| 315 | | m_interlaced = 1; |
| 316 | | } |
| 317 | 355 | else |
| 318 | 356 | { |
| 357 | m_interlaced = 0; |
| 358 | |
| 319 | 359 | if (!BIT(data, 4)) |
| 320 | 360 | { |
| 321 | 361 | m_frequency = 60; |
| 322 | | m_skip_lines = 2; |
| 362 | m_fill_lines = 2; |
| 323 | 363 | } |
| 324 | 364 | else |
| 325 | 365 | { |
| 326 | 366 | m_frequency = 50; |
| 327 | | m_skip_lines = 5; |
| 367 | m_fill_lines = 5; |
| 328 | 368 | } |
| 329 | | m_interlaced = 0; |
| 330 | 369 | } |
| 370 | |
| 371 | recompute_parameters(); |
| 331 | 372 | } |
| 332 | 373 | |
| 333 | | WRITE8_MEMBER( vt100_video_device::brightness_w ) |
| 374 | WRITE8_MEMBER(vt100_video_device::brightness_w) |
| 334 | 375 | { |
| 335 | 376 | //m_palette->set_pen_color(1, data, data, data); |
| 336 | 377 | } |
| r31342 | r31343 | |
| 347 | 388 | { |
| 348 | 389 | switch (display_type) |
| 349 | 390 | { |
| 350 | | case 0 : // bottom half, double height |
| 391 | case 0: // bottom half, double height |
| 351 | 392 | j = (i >> 1) + 5; break; |
| 352 | | case 1 : // top half, double height |
| 393 | case 1: // top half, double height |
| 353 | 394 | j = (i >> 1); break; |
| 354 | | case 2 : // double width |
| 355 | | case 3 : // normal |
| 395 | case 2: // double width |
| 396 | case 3: // normal |
| 356 | 397 | j = i; break; |
| 357 | | default : j = 0; break; |
| 398 | default: j = 0; break; |
| 358 | 399 | } |
| 359 | 400 | // modify line since that is how it is stored in rom |
| 360 | 401 | if (j == 0) j = 15; else j = j - 1; |
| r31342 | r31343 | |
| 415 | 456 | if (m_read_ram(0) != 0x7f) |
| 416 | 457 | return; |
| 417 | 458 | |
| 418 | | while (line < (m_height + m_skip_lines)) |
| 459 | while (line < (m_height + m_fill_lines)) |
| 419 | 460 | { |
| 420 | 461 | code = m_read_ram(addr + xpos); |
| 421 | 462 | if (code == 0x7f) |
| 422 | 463 | { |
| 423 | 464 | // end of line, fill empty till end of line |
| 424 | | if (line >= m_skip_lines) |
| 465 | if (line >= m_fill_lines) |
| 425 | 466 | { |
| 426 | 467 | for (x = xpos; x < ((display_type == 2) ? (m_columns / 2) : m_columns); x++) |
| 427 | 468 | { |
| r31342 | r31343 | |
| 430 | 471 | } |
| 431 | 472 | // move to new data |
| 432 | 473 | temp = m_read_ram(addr + xpos + 1) * 256 + m_read_ram(addr + xpos + 2); |
| 433 | | addr = (temp) & 0x1fff; |
| 474 | addr = (temp)& 0x1fff; |
| 434 | 475 | // if A12 is 1 then it is 0x2000 block, if 0 then 0x4000 (AVO) |
| 435 | 476 | if (addr & 0x1000) addr &= 0xfff; else addr |= 0x2000; |
| 436 | 477 | scroll_region = (temp >> 15) & 1; |
| 437 | 478 | display_type = (temp >> 13) & 3; |
| 438 | | if (line >= m_skip_lines) |
| 479 | if (line >= m_fill_lines) |
| 439 | 480 | { |
| 440 | 481 | ypos++; |
| 441 | 482 | } |
| r31342 | r31343 | |
| 445 | 486 | else |
| 446 | 487 | { |
| 447 | 488 | // display regular char |
| 448 | | if (line >= m_skip_lines) |
| 489 | if (line >= m_fill_lines) |
| 449 | 490 | { |
| 450 | 491 | display_char(bitmap, code, xpos, ypos, scroll_region, display_type); |
| 451 | 492 | } |
| r31342 | r31343 | |
| 461 | 502 | } |
| 462 | 503 | |
| 463 | 504 | // ****** RAINBOW ****** |
| 505 | // Display 10 scan lines (or 20 in interlaced mode) for one character @ position X/Y |
| 506 | // NOTE: X or Y indicate a character position! |
| 507 | |
| 464 | 508 | // 5 possible CHARACTER STATES (normal, reverse, bold, blink, underline) are encoded into display_type. |
| 465 | | |
| 466 | 509 | // From the VT-180 specs, chapter 6-43 (where multiple attributes are described): |
| 467 | 510 | // 1) reverse characters [ XOR of reverse video and reverse screen (A) ] normally have dim backgrounds with black characters (B) |
| 468 | 511 | // 2) bold and reverse together give a background of normal intensity |
| r31342 | r31343 | |
| 474 | 517 | // 5) underline causes the 9.th scan to be forced to |
| 475 | 518 | // A) white of the same intensity as the characters (for nonreversed characters), |
| 476 | 519 | // b) to black (for reverse characters) |
| 520 | // LINE ATTRIBUTE 'double_height' always is interpreted as 'double width + double height' |
| 477 | 521 | |
| 478 | | // LINE ATTRIBUTE 'double_height' always is interpreted as 'double width + double height' |
| 522 | // ATTRIBUTES: No attributes = 0x0E |
| 523 | // 1 = display char. in REVERSE (encoded as 8 in display_type) HIGH ACTIVE |
| 524 | // 0 = display char. in BOLD (encoded as 16 in display_type) LOW ACTIVE |
| 525 | // 0 = display char. w. BLINK (encoded as 32 in display_type) LOW ACTIVE |
| 526 | // 0 = display char. w. UNDERLINE (encoded as 64 in display_type) LOW ACTIVE |
| 479 | 527 | void rainbow_video_device::display_char(bitmap_ind16 &bitmap, UINT8 code, int x, int y, UINT8 scroll_region, UINT8 display_type) |
| 480 | 528 | { |
| 529 | UINT16 x_preset = x << 3; // x_preset = x * 9 (= 132 column mode) |
| 530 | x_preset += x; |
| 531 | |
| 532 | if (m_columns == 80) |
| 533 | x_preset += x; // x_preset = x * 10 (80 column mode) |
| 534 | |
| 481 | 535 | UINT16 y_preset; |
| 482 | | UINT16 x_preset, d_x_preset; |
| 483 | | if (m_columns == 132) |
| 484 | | { |
| 485 | | x_preset = x * 9; |
| 486 | | d_x_preset = x * 18; |
| 487 | | } |
| 488 | | else |
| 489 | | { |
| 490 | | x_preset = x * 10; |
| 491 | | d_x_preset = x * 20; |
| 492 | | } |
| 493 | 536 | |
| 537 | UINT16 CHARPOS_y_preset = y << 3; // CHARPOS_y_preset = y * 10; |
| 538 | CHARPOS_y_preset += y; |
| 539 | CHARPOS_y_preset += y; |
| 540 | |
| 541 | UINT16 DOUBLE_x_preset = x_preset << 1; // 18 for 132 column mode, else 20 (x_preset * 2) |
| 542 | |
| 494 | 543 | UINT8 line = 0; |
| 495 | 544 | int bit = 0, j = 0; |
| 496 | 545 | int fg_intensity; |
| 497 | 546 | int back_intensity, back_default_intensity; |
| 498 | 547 | |
| 499 | | int invert = (display_type & 8) >> 3; // BIT 3 indicates REVERSE |
| 500 | | int bold = (display_type & 16) >> 4; // BIT 4 indicates BOLD |
| 501 | | int blink = display_type & 32; // BIT 5 indicates BLINK |
| 502 | | int underline = display_type & 64; // BIT 6 indicates UNDERLINE |
| 503 | | bool blank = (display_type & 0x80) ? true : false; // BIT 7 indicates BLANK |
| 548 | int invert = (display_type & 8) ? 1 : 0; // REVERSE |
| 549 | int bold = (display_type & 16) ? 0 : 1; // BIT 4 |
| 550 | int blink = (display_type & 32) ? 0 : 1; // BIT 5 |
| 551 | int underline = (display_type & 64) ? 0 : 1; // BIT 6 |
| 552 | bool blank = (display_type & 128) ? true : false; // BIT 7 |
| 504 | 553 | |
| 505 | 554 | display_type = display_type & 3; |
| 506 | 555 | |
| r31342 | r31343 | |
| 508 | 557 | // 'reverse field' = reverse video over entire screen |
| 509 | 558 | |
| 510 | 559 | // For reference: a complete truth table can be taken from TABLE 4-6-4 / VT100 technical manual. |
| 511 | | // Following simple IF statements implement it in full. Code segments should not be shuffled. |
| 560 | // Following IF statements implement it in full. Code segments should not be shuffled. |
| 512 | 561 | invert = invert ^ m_reverse_field ^ m_basic_attribute; |
| 513 | 562 | |
| 514 | 563 | fg_intensity = bold + 2; // FOREGROUND (FG): normal (2) or bright (3) |
| 515 | 564 | |
| 516 | 565 | back_intensity = 0; // DO NOT SHUFFLE CODE AROUND !! |
| 517 | | if ( (blink != 0) && ( m_blink_flip_flop != 0 ) ) |
| 566 | if ((blink != 0) && (m_blink_flip_flop != 0)) |
| 518 | 567 | fg_intensity -= 1; // normal => dim bright => normal (when bold) |
| 519 | 568 | |
| 520 | 569 | // INVERSION: background gets foreground intensity (reduced by 1). |
| r31342 | r31343 | |
| 539 | 588 | if (scroll_region != 0) |
| 540 | 589 | smooth_offset = m_last_scroll; // valid after VBI |
| 541 | 590 | |
| 591 | int i = 0; |
| 542 | 592 | int extra_scan_line = 0; |
| 543 | | for (int scan_line = 0; scan_line < 10; scan_line++) |
| 593 | for (int scan_line = 0; scan_line < (m_linedoubler ? 20 : 10); scan_line++) |
| 544 | 594 | { |
| 545 | | y_preset = y * 10 + scan_line; |
| 595 | y_preset = CHARPOS_y_preset + scan_line; |
| 546 | 596 | |
| 547 | | int i = scan_line + smooth_offset; // offset to bitmap in char-rom (+ scroll) |
| 548 | | if (i > 9) // SMOOTH SCROLL - fill previous scan line: |
| 597 | // 'i' points to char-rom (plus scroll offset; if active) - |
| 598 | // IF INTERLACED: odd lines = even lines |
| 599 | i = (m_linedoubler ? (scan_line >> 1) : scan_line) + smooth_offset; |
| 600 | |
| 601 | if (i > 9) // handle everything in one loop (in case of smooth scroll): |
| 549 | 602 | { |
| 550 | 603 | extra_scan_line += 1; |
| 551 | | i = smooth_offset - extra_scan_line; // correct bitmap |
| 552 | 604 | |
| 553 | | y_preset -= scan_line; |
| 554 | | if (y > 0) |
| 555 | | y_preset -= extra_scan_line; // Y * 10 - extra_scan_line |
| 605 | // Fetch appropriate character bitmap (one scan line) - |
| 606 | // IF INTERLACED: no odd lines |
| 607 | i = smooth_offset - (m_linedoubler ? (extra_scan_line >> 1) : extra_scan_line); |
| 608 | |
| 609 | if (CHARPOS_y_preset >= extra_scan_line) // If result not negative... |
| 610 | y_preset = CHARPOS_y_preset - extra_scan_line; // correct Y pos. |
| 611 | else |
| 612 | { |
| 613 | y_preset = (m_linedoubler ? 480 : 240) - extra_scan_line; |
| 614 | i = 0; // (should be always empty) |
| 556 | 615 | } |
| 616 | } |
| 557 | 617 | |
| 558 | 618 | switch (display_type) |
| 559 | 619 | { |
| 560 | | case 0 : // bottom half of 'double height, double width' char. |
| 620 | case 0: // bottom half of 'double height, double width' char. |
| 561 | 621 | j = (i >> 1) + 5; |
| 562 | 622 | break; |
| 563 | 623 | |
| 564 | | case 2 : // top half of 'double height, double width' char. |
| 624 | case 2: // top half of 'double height, double width' char. |
| 565 | 625 | j = (i >> 1); |
| 566 | 626 | break; |
| 567 | 627 | |
| 568 | | default : // 1: double width |
| 628 | default: // 1: double width |
| 569 | 629 | // 3: normal |
| 570 | 630 | j = i; |
| 571 | 631 | break; |
| r31342 | r31343 | |
| 574 | 634 | // modify line since that is how it is stored in rom |
| 575 | 635 | if (j == 0) j = 15; else j = j - 1; |
| 576 | 636 | |
| 577 | | line = m_gfx[ (code << 4) + j]; // code * 16 |
| 637 | line = m_gfx[(code << 4) + j]; // code * 16 |
| 578 | 638 | |
| 579 | 639 | // UNDERLINED CHARACTERS (CASE 5 - different in 1 line): |
| 580 | 640 | back_intensity = back_default_intensity; // 0, 1, 2 |
| 581 | | if ( underline != 0 ) |
| 641 | if (underline != 0) |
| 582 | 642 | { |
| 583 | | if ( i == 8 ) |
| 643 | if (i == 8) |
| 584 | 644 | { |
| 585 | 645 | if (invert == 0) |
| 586 | 646 | line = 0xff; // CASE 5 A) |
| r31342 | r31343 | |
| 611 | 671 | // Double, 'double_height + double_width', then normal. |
| 612 | 672 | if (double_width) |
| 613 | 673 | { |
| 614 | | bitmap.pix16( y_preset, d_x_preset + (b << 1) + 1) = bit; |
| 615 | | bitmap.pix16( y_preset, d_x_preset + (b << 1) ) = bit; |
| 674 | bitmap.pix16(y_preset, DOUBLE_x_preset + (b << 1) + 1) = bit; |
| 675 | bitmap.pix16(y_preset, DOUBLE_x_preset + (b << 1)) = bit; |
| 616 | 676 | |
| 617 | 677 | if (double_height) |
| 618 | 678 | { |
| 619 | | bitmap.pix16( 1 + y_preset, d_x_preset + (b << 1) + 1) = bit; |
| 620 | | bitmap.pix16( 1 + y_preset, d_x_preset + (b << 1) ) = bit; |
| 679 | bitmap.pix16(1 + y_preset, DOUBLE_x_preset + (b << 1) + 1) = bit; |
| 680 | bitmap.pix16(1 + y_preset, DOUBLE_x_preset + (b << 1)) = bit; |
| 621 | 681 | } |
| 622 | 682 | } |
| 623 | 683 | else |
| r31342 | r31343 | |
| 630 | 690 | if (double_width) |
| 631 | 691 | { |
| 632 | 692 | // double chars: 18 or 20 bits |
| 633 | | bitmap.pix16(y_preset, d_x_preset + 16) = bit; |
| 634 | | bitmap.pix16(y_preset, d_x_preset + 17) = bit; |
| 693 | bitmap.pix16(y_preset, DOUBLE_x_preset + 16) = bit; |
| 694 | bitmap.pix16(y_preset, DOUBLE_x_preset + 17) = bit; |
| 635 | 695 | |
| 636 | 696 | if (m_columns == 80) |
| 637 | 697 | { |
| 638 | | bitmap.pix16(y_preset, d_x_preset + 18) = bit; |
| 639 | | bitmap.pix16(y_preset, d_x_preset + 19) = bit; |
| 698 | bitmap.pix16(y_preset, DOUBLE_x_preset + 18) = bit; |
| 699 | bitmap.pix16(y_preset, DOUBLE_x_preset + 19) = bit; |
| 640 | 700 | } |
| 641 | 701 | } |
| 642 | 702 | else |
| r31342 | r31343 | |
| 660 | 720 | int ypos = 0; |
| 661 | 721 | UINT8 code; |
| 662 | 722 | int x = 0; |
| 663 | | UINT8 scroll_region = 1; // DEFAULT TO 1 = PART OF scroll_region |
| 664 | | UINT8 display_type = 3; // binary 11 |
| 723 | UINT8 scroll_region = 0; // DEFAULT TO 0 = NOT PART OF scroll_region |
| 724 | UINT8 display_type = 3; // NORMAL DISPLAY - binary 11 |
| 665 | 725 | UINT16 temp = 0; |
| 666 | 726 | |
| 667 | | while (line < (m_height + m_skip_lines)) |
| 727 | if (m_read_ram(0) != 0xff) // video uninitialized? |
| 728 | return; |
| 729 | |
| 730 | // Skip fill (0xFF) lines and put result in ADDR. |
| 731 | for (int xp = 1; xp <= 6; xp += 1) // beware of circular references |
| 668 | 732 | { |
| 733 | // Fetch LINE ATTRIBUTE before it is gone |
| 734 | attr_addr = 0x1000 | ((addr + 1) & 0x0fff); |
| 735 | |
| 736 | temp = m_read_ram(addr + 2) * 256 + m_read_ram(addr + 1); |
| 737 | addr = (temp)& 0x0fff; |
| 738 | |
| 739 | temp = m_read_ram(attr_addr); |
| 740 | scroll_region = (temp)& 1; |
| 741 | display_type = (temp >> 1) & 3; |
| 742 | |
| 743 | if (addr >= 0x12) |
| 744 | break; |
| 745 | } |
| 746 | |
| 747 | int vert_charlines_MAX = ((m_linedoubler == false) ? m_height : m_height_MAX); |
| 748 | while (line < vert_charlines_MAX ) |
| 749 | { |
| 669 | 750 | code = m_read_ram(addr + xpos); |
| 670 | 751 | |
| 671 | | if ( code == 0x00 ) // TODO: investigate side effect on regular zero character! |
| 752 | if (code == 0x00) // TODO: investigate side effect on regular zero character! |
| 672 | 753 | display_type |= 0x80; // DEFAULT: filler chars (till end of line) and empty lines (00) will be blanked |
| 673 | 754 | else |
| 674 | 755 | display_type &= 0x7f; // else activate display. |
| 675 | 756 | |
| 676 | | if ( code == 0xff ) |
| 757 | if (code == 0xff) // end of line, fill empty till end of line |
| 677 | 758 | { |
| 678 | | // end of line, fill empty till end of line |
| 679 | | if (line >= m_skip_lines) |
| 759 | // HINT: display_type is already shifted! All except 3 is DOUBLE WIDTH 40 or 66 chars per line |
| 760 | for (x = xpos; x < ((display_type != 3) ? (m_columns / 2) : m_columns); x++) |
| 680 | 761 | { |
| 681 | | // NOTE: display_type is already shifted! All except 3 is DOUBLE WIDTH 40 or 66 chars per line |
| 682 | | for (x = xpos; x < ( (display_type != 3) ? (m_columns / 2) : m_columns ); x++) |
| 683 | | { |
| 684 | 762 | display_char(bitmap, code, x, ypos, scroll_region, display_type | 0x80); |
| 685 | 763 | } |
| 686 | 764 | |
| 687 | | } |
| 688 | | |
| 689 | 765 | // LINE ATTRIBUTE - valid for all chars on next line ** DO NOT SHUFFLE ** |
| 690 | | attr_addr = 0x1000 | ( (addr + xpos + 1) & 0x0fff ); |
| 766 | attr_addr = 0x1000 | ((addr + xpos + 1) & 0x0fff); |
| 691 | 767 | |
| 692 | 768 | // MOVE TO NEW DATA |
| 693 | 769 | temp = m_read_ram(addr + xpos + 2) * 256 + m_read_ram(addr + xpos + 1); |
| 694 | | addr = (temp) & 0x0fff; |
| 770 | addr = (temp)& 0x0fff; |
| 695 | 771 | |
| 696 | 772 | temp = m_read_ram(attr_addr); |
| 697 | | scroll_region = (temp) & 1; |
| 773 | scroll_region = (temp)& 1; |
| 698 | 774 | display_type = (temp >> 1) & 3; |
| 699 | 775 | |
| 700 | | if (line >= m_skip_lines) |
| 701 | | { |
| 702 | | ypos++; |
| 703 | | } |
| 776 | ypos++; // Y + 1 in non-interlaced mode |
| 777 | if (m_linedoubler) |
| 778 | ypos++; // Y + 2 in 'double scan' mode (see -> 'display_char') |
| 779 | |
| 780 | if (ypos > vert_charlines_MAX) // prevent invalid Y pos. |
| 781 | break; |
| 782 | |
| 704 | 783 | xpos = 0; |
| 705 | 784 | line++; |
| 706 | 785 | } |
| 707 | 786 | else |
| 708 | 787 | { |
| 709 | | // display regular char |
| 710 | | if (line >= m_skip_lines) |
| 711 | | { |
| 712 | | attr_addr = 0x1000 | ( (addr + xpos) & 0x0fff ); |
| 713 | | temp = m_read_ram(attr_addr); // get character attribute |
| 788 | attr_addr = 0x1000 | ((addr + xpos) & 0x0fff); |
| 789 | temp = (m_read_ram(attr_addr) & 15) << 3; // get character attributes |
| 714 | 790 | |
| 715 | | // CONFIRMED: Reverse active on 1. No attributes = 0x0E |
| 716 | | // 1 = display char. in REVERSE (encoded as 8) |
| 717 | | // 0 = display char. in BOLD (encoded as 16) |
| 718 | | // 0 = display char. w. BLINK (encoded as 32) |
| 719 | | // 0 = display char. w. UNDERLINE (encoded as 64). |
| 720 | | display_char(bitmap, code, xpos, ypos, scroll_region, display_type | ( ( (temp & 1)) << 3 ) |
| 721 | | | ( (2-(temp & 2)) << 3 ) |
| 722 | | | ( (4-(temp & 4)) << 3 ) |
| 723 | | | ( (8-(temp & 8)) << 3 ) |
| 724 | | ); |
| 791 | // see 'display_char' for an explanation of attribute encoding - |
| 792 | display_char(bitmap, code, xpos, ypos, scroll_region, display_type | temp); |
| 725 | 793 | |
| 726 | | } |
| 727 | 794 | xpos++; |
| 728 | | |
| 729 | | if (xpos > m_columns ) |
| 795 | if (xpos > m_columns) |
| 730 | 796 | { |
| 731 | 797 | xpos = 0; |
| 732 | 798 | line++; |
| r31342 | r31343 | |
| 735 | 801 | |
| 736 | 802 | } // while |
| 737 | 803 | |
| 738 | | |
| 739 | 804 | } |
| 740 | 805 | |
| 741 | 806 | void rainbow_video_device::notify_vblank(bool v) |
| r31342 | r31343 | |
| 753 | 818 | v_last = v; |
| 754 | 819 | } |
| 755 | 820 | |
| 756 | | void rainbow_video_device::palette_select ( int choice ) |
| 821 | void rainbow_video_device::palette_select(int choice) |
| 757 | 822 | { |
| 758 | | switch(choice) |
| 823 | switch (choice) |
| 759 | 824 | { |
| 760 | 825 | default: |
| 761 | 826 | case 0x01: |
| 762 | | m_palette->set_pen_color(1, 0xff-100, 0xff-100, 0xff-100); // WHITE (dim) |
| 763 | | m_palette->set_pen_color(2, 0xff-50, 0xff-50, 0xff-50); // WHITE NORMAL |
| 827 | m_palette->set_pen_color(1, 0xff - 100, 0xff - 100, 0xff - 100); // WHITE (dim) |
| 828 | m_palette->set_pen_color(2, 0xff - 50, 0xff - 50, 0xff - 50); // WHITE NORMAL |
| 764 | 829 | m_palette->set_pen_color(3, 0xff, 0xff, 0xff); // WHITE (brighter) |
| 765 | 830 | break; |
| 766 | 831 | |
| r31342 | r31343 | |
| 772 | 837 | |
| 773 | 838 | case 0x03: |
| 774 | 839 | m_palette->set_pen_color(1, 213 - 47, 146 - 47, 82 - 47); // AMBER (dim) |
| 775 | | m_palette->set_pen_color(2, 213, 146, 82 ); // AMBER (NORMAL) |
| 776 | | m_palette->set_pen_color(3, 255, 193, 129 ); // AMBER (brighter) |
| 840 | m_palette->set_pen_color(2, 213, 146, 82); // AMBER (NORMAL) |
| 841 | m_palette->set_pen_color(3, 255, 193, 129); // AMBER (brighter) |
| 777 | 842 | break; |
| 778 | 843 | } |
| 779 | 844 | } |
| r31342 | r31343 | |
| 783 | 848 | { |
| 784 | 849 | // 'In reverse screen mode, termination forces the beam to the screen background intensity' |
| 785 | 850 | // Background intensity means 'dim' (1) according to one source. |
| 786 | | bitmap.fill( ((m_reverse_field ^ m_basic_attribute) ? 1 : 0) , cliprect); |
| 851 | bitmap.fill(((m_reverse_field ^ m_basic_attribute) ? 1 : 0), cliprect); |
| 787 | 852 | } |
| 788 | 853 | |
| 789 | 854 | |
| r31342 | r31343 | |
| 801 | 866 | return MHFU_counter; |
| 802 | 867 | |
| 803 | 868 | case -100: // -100 : RESET and ENABLE MHFU counter |
| 804 | | //printf("-100 MHFU * reset and ENABLE * \n"); |
| 869 | if (VERBOSE) |
| 870 | printf("-100 MHFU * reset and ENABLE * \n"); |
| 805 | 871 | MHFU_counter = 0; |
| 806 | 872 | |
| 807 | | //if (MHFU_FLAG == false) |
| 808 | | // printf("-100 MHFU ___ENABLED___\n"); |
| 873 | if (VERBOSE) |
| 874 | { |
| 875 | if (MHFU_FLAG == false) |
| 876 | printf("-100 MHFU ___ENABLED___\n"); |
| 877 | } |
| 809 | 878 | MHFU_FLAG = true; |
| 810 | 879 | |
| 811 | 880 | return -100; |
| r31342 | r31343 | |
| 816 | 885 | } // switch |
| 817 | 886 | } |
| 818 | 887 | |
| 819 | | TIMER_CALLBACK_MEMBER( vt100_video_device::lba7_change ) |
| 888 | TIMER_CALLBACK_MEMBER(vt100_video_device::lba7_change) |
| 820 | 889 | { |
| 821 | 890 | m_lba7 = (m_lba7) ? 0 : 1; |
| 822 | 891 | } |
| 823 | 892 | |
| 824 | | static MACHINE_CONFIG_FRAGMENT( vt100_video ) |
| 825 | | MCFG_PALETTE_ADD_MONOCHROME_GREEN("palette") |
| 893 | static MACHINE_CONFIG_FRAGMENT(vt100_video) |
| 894 | MCFG_PALETTE_ADD_MONOCHROME_GREEN("palette") |
| 826 | 895 | MACHINE_CONFIG_END |
| 827 | 896 | |
| 828 | 897 | //------------------------------------------------- |
| r31342 | r31343 | |
| 832 | 901 | |
| 833 | 902 | machine_config_constructor vt100_video_device::device_mconfig_additions() const |
| 834 | 903 | { |
| 835 | | return MACHINE_CONFIG_NAME( vt100_video ); |
| 904 | return MACHINE_CONFIG_NAME(vt100_video); |
| 836 | 905 | } |
| 837 | 906 | |
| 838 | | static MACHINE_CONFIG_FRAGMENT( rainbow_video ) |
| 839 | | MCFG_PALETTE_ADD("palette", 4) |
| 907 | static MACHINE_CONFIG_FRAGMENT(rainbow_video) |
| 908 | MCFG_PALETTE_ADD("palette", 4) |
| 840 | 909 | MACHINE_CONFIG_END |
| 841 | 910 | |
| 842 | 911 | //------------------------------------------------- |
| r31342 | r31343 | |
| 846 | 915 | |
| 847 | 916 | machine_config_constructor rainbow_video_device::device_mconfig_additions() const |
| 848 | 917 | { |
| 849 | | return MACHINE_CONFIG_NAME( rainbow_video ); |
| 918 | return MACHINE_CONFIG_NAME(rainbow_video); |
| 850 | 919 | } |