diff options
Diffstat (limited to 'src/devices/video/upd7220.cpp')
-rw-r--r-- | src/devices/video/upd7220.cpp | 1298 |
1 files changed, 777 insertions, 521 deletions
diff --git a/src/devices/video/upd7220.cpp b/src/devices/video/upd7220.cpp index d8697222485..95fe02f0326 100644 --- a/src/devices/video/upd7220.cpp +++ b/src/devices/video/upd7220.cpp @@ -1,64 +1,74 @@ // license:BSD-3-Clause -// copyright-holders:Angelo Salese, Miodrag Milanovic, Carl -/********************************************************************** +// copyright-holders:Angelo Salese, Miodrag Milanovic, Carl, Brian Johnson +/************************************************************************************************** + +Intel 82720 Graphics Display Controller emulation +a.k.a. NEC uPD7220 + +TODO: +- implement FIFO as ring buffer; +- incomplete / unimplemented FIGD / GCHRD draw modes; +\- FIGD character +\- slanted character +- read-modify-write cycle (fixed by now?); +- a5105: has a FIFO bug with the RDAT, should be a lot larger when it scrolls up. + Can be fixed with a DRDY mechanism for RDAT/WDAT (fixed by now?); +- later pc9801/pc9821 machines: throws "Invalid command byte 05" (zettmj on Epson logo), + actual undocumented command to reset something? +- wide mode (32-bit access); +- microbx2: sets mode=0x1f (interlace), oddly cuts off area scroll on prompt (double height chars?) + while DEMO.1 don't. +- repeat fields interlace mode; +- support non-draw on retrace mode (pc98 bitmap layer -> potential raster effects), better + honoring of visible area (bail out thru m_al, cutting off unnecessary comparisons); +- light pen; + +**************************************************************************************************/ - Intel 82720 Graphics Display Controller emulation - -**********************************************************************/ - -/* - - TODO: - - - implement FIFO as ring buffer - - commands - - DMAR - - DMAW - - incomplete / unimplemented FIGD / GCHRD draw modes - - FIGD character - - slanted character - - read-modify-write cycle - - read data - - modify data - - write data - - QX-10 diagnostic test has positioning bugs with the bitmap display test; - - QX-10 diagnostic test misses the zooming factor (external pin); - - compis2 SAD address for bitmap is 0x20000 for whatever reason (presumably missing banking); - - A5105 has a FIFO bug with the RDAT, should be a lot larger when it scrolls up. - The problem is that DMA-ing with RDAT/WDAT shouldn't be instant; - - - honor visible area - - wide mode (32-bit access) - - light pen - - dad and mask are the same, in figd dad is shifted every step and when msb or lsb are 1 ead is advanced in x dir - -*/ #include "emu.h" #include "upd7220.h" #include "screen.h" -//#define VERBOSE 1 -#include "logmacro.h" +#define LOG_CRTC (1U << 1) +#define LOG_DRAW (1U << 2) +#define LOG_AREA (1U << 3) // printout works best with -oslog +#define LOG_CMD (1U << 4) +#define LOG_CMD2 (1U << 5) // DE related commands +#define LOG_CMD3 (1U << 6) // memory interface (RDAT/WDAT) +#define LOG_CMD4 (1U << 7) // DMA +#define VERBOSE (LOG_GENERAL) +//#define LOG_OUTPUT_FUNC osd_printf_info +#include "logmacro.h" +#define LOGCRTC(...) LOGMASKED(LOG_CRTC, __VA_ARGS__) +#define LOGDRAW(...) LOGMASKED(LOG_DRAW, __VA_ARGS__) +#define LOGAREA(...) LOGMASKED(LOG_AREA, __VA_ARGS__) +#define LOGCMD(...) LOGMASKED(LOG_CMD, __VA_ARGS__) +#define LOGCMD2(...) LOGMASKED(LOG_CMD2, __VA_ARGS__) +#define LOGCMD3(...) LOGMASKED(LOG_CMD3, __VA_ARGS__) +#define LOGCMD4(...) LOGMASKED(LOG_CMD4, __VA_ARGS__) //************************************************************************** // MACROS / CONSTANTS //************************************************************************** -// todo typedef +// TODO: typedef enum { COMMAND_INVALID = -1, COMMAND_RESET, + COMMAND_RESET2, + COMMAND_RESET3, COMMAND_SYNC, COMMAND_VSYNC, COMMAND_CCHAR, COMMAND_START, - COMMAND_BCTRL, + COMMAND_BLANK, + COMMAND_BLANK2, COMMAND_ZOOM, COMMAND_CURS, COMMAND_PRAM, @@ -73,8 +83,7 @@ enum COMMAND_LPRD, COMMAND_DMAR, COMMAND_DMAW, - COMMAND_5A, - COMMAND_05 + COMMAND_5A }; enum @@ -91,12 +100,14 @@ enum }; #define UPD7220_COMMAND_RESET 0x00 +#define UPD7220_COMMAND_RESET2 0x01 +#define UPD7220_COMMAND_RESET3 0x09 #define UPD7220_COMMAND_SYNC 0x0e // & 0xfe #define UPD7220_COMMAND_VSYNC 0x6e // & 0xfe #define UPD7220_COMMAND_CCHAR 0x4b #define UPD7220_COMMAND_START 0x6b -#define UPD7220_COMMAND_BCTRL 0x0c // & 0xfe -#define UPD7220_COMMAND_05 0x05 +#define UPD7220_COMMAND_BLANK 0x0c // & 0xfe +#define UPD7220_COMMAND_BLANK2 0x05 #define UPD7220_COMMAND_ZOOM 0x46 #define UPD7220_COMMAND_CURS 0x49 #define UPD7220_COMMAND_PRAM 0x70 // & 0xf0 @@ -146,6 +157,7 @@ static constexpr int y_dir[8] = { 1, 1, 0,-1,-1,-1, 0, 1}; // devices DEFINE_DEVICE_TYPE(UPD7220, upd7220_device, "upd7220", "NEC uPD7220") +DEFINE_DEVICE_TYPE(UPD7220A, upd7220a_device, "upd7220a", "NEC uPD7220A") // default address map @@ -163,6 +175,11 @@ ROM_START( upd7220 ) ROM_LOAD( "upd7220.bin", 0x000, 0x100, CRC(3c92b218) SHA1(e154b3106a80c9c98d9f2ee18efcd7f4b4aa7d49) ) ROM_END +ROM_START( upd7220a ) + ROM_REGION( 0x100, "upd7220", 0 ) + ROM_LOAD( "upd7220a.bin", 0x000, 0x100, NO_DUMP ) +ROM_END + //------------------------------------------------- // memory_space_config - return a description of @@ -186,31 +203,16 @@ const tiny_rom_entry *upd7220_device::device_rom_region() const return ROM_NAME( upd7220 ); } +const tiny_rom_entry *upd7220a_device::device_rom_region() const +{ + return ROM_NAME( upd7220a ); +} //************************************************************************** // INLINE HELPERS //************************************************************************** -//------------------------------------------------- -// readbyte - read a byte at the given address -//------------------------------------------------- - -inline uint8_t upd7220_device::readbyte(offs_t address) -{ - return space().read_byte(address); -} - - -//------------------------------------------------- -// writebyte - write a byte at the given address -//------------------------------------------------- - -inline void upd7220_device::writebyte(offs_t address, uint8_t data) -{ - space().write_byte(address, data); -} - inline uint16_t upd7220_device::readword(offs_t address) { return space().read_word(address); @@ -329,6 +331,12 @@ inline void upd7220_device::dequeue(uint8_t *data, int *flag) if (m_fifo_ptr == -1) m_sr &= ~UPD7220_SR_DATA_READY; } + else + { + // TODO: underflow details + // pc9821:skinpan does SR checks over the wrong port during intro ... + *data = 0xff; + } } @@ -338,7 +346,12 @@ inline void upd7220_device::dequeue(uint8_t *data, int *flag) inline void upd7220_device::update_vsync_timer(int state) { - int next_y = state ? m_vs : 0; + const int vert_mult = (m_mode & UPD7220_MODE_INTERLACE_MASK) == UPD7220_MODE_INTERLACE_ON ? 2 : 1; + + // page 6-68, route vsync so that top is start of back porch, end is at sync time + // (at bottom of MAME vpos() mechanism) + // - pc9801:lemmings and pc9801:spindiz2 cares + int next_y = state ? 0 : (m_vbp + m_al + m_vfp) * vert_mult; attotime duration = screen().time_until_pos(next_y, 0); @@ -352,10 +365,11 @@ inline void upd7220_device::update_vsync_timer(int state) inline void upd7220_device::update_hsync_timer(int state) { + const int horiz_mult = (m_mode & UPD7220_MODE_DISPLAY_MASK) == UPD7220_MODE_DISPLAY_GRAPHICS ? 16 : 8; int y = screen().vpos(); - int next_x = state ? m_hs : 0; - int next_y = state ? y : ((y + 1) % m_al); + int next_x = state ? m_hs * horiz_mult : 0; + int next_y = state ? y : ((y + 1) % (m_vs + m_vbp + m_al + m_vfp - 1)); attotime duration = screen().time_until_pos(next_y, next_x); @@ -384,19 +398,18 @@ inline void upd7220_device::update_blank_timer(int state) // recompute_parameters - //------------------------------------------------- +void upd7220_device::device_clock_changed() +{ + recompute_parameters(); +} + inline void upd7220_device::recompute_parameters() { - int horiz_mult = 16, vert_mult = 1; - /* TODO: assume that the pitch also controls number of horizontal pixels in a single cell */ - // horiz_mult = 4 if both mixed and interlace? - if((m_mode & UPD7220_MODE_DISPLAY_MASK) == UPD7220_MODE_DISPLAY_MIXED) - horiz_mult = 8; - else if((m_mode & UPD7220_MODE_INTERLACE_MASK) == UPD7220_MODE_INTERLACE_ON) - { - // in interlaced mode every line contains both fields - horiz_mult = 8; // TODO this breaks compis uhrg video, characters are 16 pixels wide in interlaced mode too - vert_mult = 2; - } + // microbx2 wants horizontal multiplier of x16 + // pc9801:diremono sets up m_mode to be specifically in character mode, wanting x8 here + // TODO: verify compis uhrg video & high reso Hyper 98 + const int horiz_mult = (m_mode & UPD7220_MODE_DISPLAY_MASK) == UPD7220_MODE_DISPLAY_GRAPHICS ? 16 : 8; + const int vert_mult = (m_mode & UPD7220_MODE_INTERLACE_MASK) == UPD7220_MODE_INTERLACE_ON ? 2 : 1; int horiz_pix_total = (m_hs + m_hbp + m_hfp + m_aw) * horiz_mult; int vert_pix_total = (m_vs + m_vbp + m_al + m_vfp) * vert_mult; @@ -415,14 +428,15 @@ inline void upd7220_device::recompute_parameters() m_vbp, //m_vs + m_vbp; m_al * vert_mult + m_vbp - 1);//vert_pix_total - m_vfp - 1; - LOG("uPD7220 Screen: %u x %u @ %f Hz\n", horiz_pix_total, vert_pix_total, 1 / ATTOSECONDS_TO_DOUBLE(refresh)); - LOG("Visible Area: (%u, %u) - (%u, %u)\n", visarea.left(), visarea.top(), visarea.right(), visarea.bottom()); - LOG("%d %d %d %d %d\n",m_hs,m_hbp,m_aw,m_hfp,m_pitch); - LOG("%d %d %d %d\n",m_vs,m_vbp,m_al,m_vfp); + LOGCRTC("New Screen setup: %u x %u @ %f Hz\n", horiz_pix_total, vert_pix_total, 1 / ATTOSECONDS_TO_DOUBLE(refresh)); + LOGCRTC("Visible Area: (%u, %u) - (%u, %u)\n", visarea.left(), visarea.top(), visarea.right(), visarea.bottom()); + //LOGCRTC("%d %d %d %d %d\n",m_hs,m_hbp,m_aw,m_hfp,m_pitch); + //LOGCRTC("%d %d %d %d\n",m_vs,m_vbp,m_al,m_vfp); if (m_m) { screen().configure(horiz_pix_total, vert_pix_total, visarea, refresh); + screen().reset_origin(); update_hsync_timer(0); update_vsync_timer(0); @@ -445,84 +459,129 @@ inline void upd7220_device::reset_figs_param() { m_figs.m_dc = 0x0000; m_figs.m_d = 0x0008; - m_figs.m_d1 = 0x0008; - m_figs.m_d2 = 0x0000; - m_figs.m_dm = 0x0000; + m_figs.m_d1 = 0xffff; + m_figs.m_d2 = 0x0008; + m_figs.m_dm = 0xffff; m_figs.m_gd = 0; + m_figs.m_figure_type = 0; + m_pattern = (m_ra[8]) | (m_ra[9] << 8); } //------------------------------------------------- // read_vram - //------------------------------------------------- +inline uint16_t upd7220_device::read_vram() +{ + uint16_t const data = readword(m_ead); + m_ead += x_dir[m_figs.m_dir] + (y_dir[m_figs.m_dir] * get_pitch()); + m_ead &= 0x3ffff; -inline void upd7220_device::read_vram(uint8_t type, uint8_t mod) + return data; +} + +inline void upd7220_device::rdat(uint8_t type, uint8_t mod) { if (type == 1) { - LOG("uPD7220 invalid type 1 RDAT parameter\n"); + LOG("invalid type 1 RDAT parameter\n"); return; } if (mod) - LOG("uPD7220 RDAT used with mod = %02x?\n",mod); + LOG("RDAT used with mod = %02x?\n",mod); while (m_figs.m_dc && m_fifo_ptr < (type ? 15 : 14)) { + uint16_t const data = read_vram(); switch(type) { - case 0: - queue(readbyte(m_ead*2), 0); - queue(readbyte(m_ead*2+1), 0); - break; - case 2: - queue(readbyte(m_ead*2), 0); - break; - case 3: - queue(readbyte(m_ead*2+1), 0); - break; + case 0: + queue(data & 0xff, 0); + queue((data >> 8) & 0xff, 0); + break; + case 2: + queue(data & 0xff, 0); + break; + case 3: + queue((data >> 8) & 0xff, 0); + break; } m_figs.m_dc--; - m_ead += x_dir[m_figs.m_dir] + (y_dir[m_figs.m_dir] * m_pitch); - m_ead &= 0x3ffff; } if (m_figs.m_dc == 0) reset_figs_param(); } - //------------------------------------------------- // write_vram - //------------------------------------------------- - -inline void upd7220_device::write_vram(uint8_t type, uint8_t mod) +inline void upd7220_device::write_vram(uint8_t type, uint8_t mod, uint16_t data, uint16_t mask) { - uint16_t result; + uint16_t current = readword(m_ead); - if (type == 1) + switch(mod & 3) + { + case 0x00: //replace + switch (type) + { + case 0: current = (current & (~mask)) | (data & mask); break; + case 2: current = (current & ~(mask & 0xff)) | (data & (mask & 0xff)); break; + case 3: current = (current & ~(mask & 0xff00)) | (data & (mask & 0xff00)); break; + } + break; + case 0x01: //complement + switch (type) + { + case 0: current = current ^ (data & mask); break; + case 2: current = current ^ (data & (mask & 0xff)); break; + case 3: current = current ^ (data & (mask & 0xff00)); break; + } + break; + case 0x02: //reset to zero + switch (type) + { + case 0: current = current & ~(data & mask); break; + case 2: current = current & ~(data & (mask & 0xff)); break; + case 3: current = current & ~(data & (mask & 0xff00)); break; + } + break; + case 0x03: //set to one + switch (type) + { + case 0: current = current | (data & mask); break; + case 2: current = current | (data & (mask & 0xff)); break; + case 3: current = current | (data & (mask & 0xff00)); break; + } + break; + } + writeword(m_ead, current); +} + +inline void upd7220_device::wdat(uint8_t type, uint8_t mod) +{ + if(type == 1) { logerror("uPD7220 invalid type 1 WDAT parameter\n"); return; } - result = 0; - - result = m_pr[1] | (m_pr[2] << 8); + uint16_t result = m_pr[1] | (m_pr[2] << 8); switch(type) { - case 0: - result &= m_mask; - break; - case 2: - result &= (m_mask & 0xff); - break; - case 3: - result <<= 8; - result &= (m_mask & 0xff00); - break; + case 0: + result &= m_mask; + break; + case 2: + result &= (m_mask & 0xff); + break; + case 3: + result <<= 8; + result &= (m_mask & 0xff00); + break; } //if(result) @@ -534,43 +593,8 @@ inline void upd7220_device::write_vram(uint8_t type, uint8_t mod) for(int i = 0; i < m_figs.m_dc + 1; i++) { - switch(mod & 3) - { - case 0x00: //replace - if(type == 0) - writeword(m_ead*2+0, result); - if(type == 2) - writebyte(m_ead*2+0, result & 0xff); - if(type == 3) - writebyte(m_ead*2+1, result >> 8); - break; - case 0x01: //complement - if(type == 0) - writeword(m_ead*2+0, readword(m_ead*2+0) ^ result); - if(type == 2) - writebyte(m_ead*2+0, readbyte(m_ead*2+0) ^ (result & 0xff)); - if(type == 3) - writebyte(m_ead*2+1, readbyte(m_ead*2+1) ^ (result >> 8)); - break; - case 0x02: //reset to zero - if(type == 0) - writeword(m_ead*2+0, readword(m_ead*2+0) & ~result); - if(type == 2) - writebyte(m_ead*2+0, readbyte(m_ead*2+0) & ~(result & 0xff)); - if(type == 3) - writebyte(m_ead*2+1, readbyte(m_ead*2+1) & ~(result >> 8)); - break; - case 0x03: //set to one - if(type == 0) - writeword(m_ead*2+0, readword(m_ead*2+0) | result); - if(type == 2) - writebyte(m_ead*2+0, readbyte(m_ead*2+0) | (result & 0xff)); - if(type == 3) - writebyte(m_ead*2+1, readbyte(m_ead*2+1) | (result >> 8)); - break; - } - - m_ead += x_dir[m_figs.m_dir] + (y_dir[m_figs.m_dir] * m_pitch); + write_vram(type, mod, result); + m_ead += x_dir[m_figs.m_dir] + (y_dir[m_figs.m_dir] * get_pitch()); m_ead &= 0x3ffff; } } @@ -601,6 +625,19 @@ inline void upd7220_device::get_graphics_partition(int index, uint32_t *sad, uin *wd = BIT(m_ra[(index * 4) + 3], 7); } +/* + * experimental, for non-canonical stuff such as PC9821 PEGC + */ +std::tuple<u32, u16, u8> upd7220_device::get_area_partition_props(int line) +{ + uint32_t sad; + uint16_t len; + int im, wd; + // TODO: multiareas (pc9821:skinpan title) + get_graphics_partition(0, &sad, &len, &im, &wd); + + return std::make_tuple(sad, m_pitch, im); +} //************************************************************************** @@ -611,18 +648,20 @@ inline void upd7220_device::get_graphics_partition(int index, uint32_t *sad, uin // upd7220_device - constructor //------------------------------------------------- -upd7220_device::upd7220_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) : - device_t(mconfig, UPD7220, tag, owner, clock), +upd7220_device::upd7220_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, uint32_t clock) : + device_t(mconfig, type, tag, owner, clock), device_memory_interface(mconfig, *this), device_video_interface(mconfig, *this), + m_display_cb(*this), + m_draw_text_cb(*this), m_write_drq(*this), m_write_hsync(*this), m_write_vsync(*this), m_write_blank(*this), + m_pattern(0), m_mask(0), m_pitch(0), m_ead(0), - m_dad(0), m_lad(0), m_ra_addr(0), m_sr(UPD7220_SR_FIFO_EMPTY), @@ -650,7 +689,7 @@ upd7220_device::upd7220_device(const machine_config &mconfig, const char *tag, d m_disp(0), m_gchr(0), m_bitmap_mod(0), - m_space_config("videoram", ENDIANNESS_LITTLE, 16, 18, 0, address_map_constructor(FUNC(upd7220_device::upd7220_vram), this)) + m_space_config("videoram", ENDIANNESS_LITTLE, 16, 18, -1, address_map_constructor(FUNC(upd7220_device::upd7220_vram), this)) { for (int i = 0; i < 16; i++) { @@ -668,6 +707,16 @@ upd7220_device::upd7220_device(const machine_config &mconfig, const char *tag, d memset(&m_figs, 0x00, sizeof(m_figs)); } +upd7220_device::upd7220_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) + : upd7220_device(mconfig, UPD7220, tag, owner, clock) +{ +} + +upd7220a_device::upd7220a_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) + : upd7220_device(mconfig, UPD7220A, tag, owner, clock) +{ +} + //------------------------------------------------- // device_start - device-specific startup @@ -676,18 +725,13 @@ upd7220_device::upd7220_device(const machine_config &mconfig, const char *tag, d void upd7220_device::device_start() { // resolve callbacks - m_display_cb.bind_relative_to(*owner()); - m_draw_text_cb.bind_relative_to(*owner()); - - m_write_drq.resolve_safe(); - m_write_hsync.resolve_safe(); - m_write_vsync.resolve_safe(); - m_write_blank.resolve_safe(); + m_display_cb.resolve_safe(); + m_draw_text_cb.resolve_safe(); // allocate timers - m_vsync_timer = timer_alloc(TIMER_VSYNC); - m_hsync_timer = timer_alloc(TIMER_HSYNC); - m_blank_timer = timer_alloc(TIMER_BLANK); + m_vsync_timer = timer_alloc(FUNC(upd7220_device::vsync_update), this); + m_hsync_timer = timer_alloc(FUNC(upd7220_device::hsync_update), this); + m_blank_timer = timer_alloc(FUNC(upd7220_device::blank_update), this); // register for state saving save_item(NAME(m_ra)); @@ -710,12 +754,29 @@ void upd7220_device::device_start() save_item(NAME(m_ctop)); save_item(NAME(m_cbot)); save_item(NAME(m_ead)); - save_item(NAME(m_dad)); save_item(NAME(m_lad)); save_item(NAME(m_disp)); save_item(NAME(m_gchr)); + save_item(NAME(m_pattern)); save_item(NAME(m_mask)); save_item(NAME(m_pitch)); + save_item(NAME(m_ra_addr)); + save_item(NAME(m_cr)); + save_item(NAME(m_pr)); + save_item(NAME(m_param_ptr)); + save_item(NAME(m_fifo)); + save_item(NAME(m_fifo_flag)); + save_item(NAME(m_fifo_ptr)); + save_item(NAME(m_fifo_dir)); + save_item(NAME(m_bitmap_mod)); + save_item(NAME(m_figs.m_dir)); + save_item(NAME(m_figs.m_figure_type)); + save_item(NAME(m_figs.m_dc)); + save_item(NAME(m_figs.m_gd)); + save_item(NAME(m_figs.m_d)); + save_item(NAME(m_figs.m_d1)); + save_item(NAME(m_figs.m_d2)); + save_item(NAME(m_figs.m_dm)); } @@ -730,58 +791,55 @@ void upd7220_device::device_reset() //------------------------------------------------- -// device_timer - handler timer events +// timer events //------------------------------------------------- -void upd7220_device::device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr) +TIMER_CALLBACK_MEMBER(upd7220_device::hsync_update) { - switch (id) + if (param) { - case TIMER_HSYNC: - if (param) - { - m_sr |= UPD7220_SR_HBLANK_ACTIVE; - } - else - { - m_sr &= ~UPD7220_SR_HBLANK_ACTIVE; - } + m_sr |= UPD7220_SR_HBLANK_ACTIVE; + } + else + { + m_sr &= ~UPD7220_SR_HBLANK_ACTIVE; + } - m_write_hsync(param); + m_write_hsync(param); - update_hsync_timer(param); - break; + update_hsync_timer(param); +} - case TIMER_VSYNC: - if (param) - { - m_sr |= UPD7220_SR_VSYNC_ACTIVE; - } - else - { - m_sr &= ~UPD7220_SR_VSYNC_ACTIVE; - } +TIMER_CALLBACK_MEMBER(upd7220_device::vsync_update) +{ + if (param) + { + m_sr |= UPD7220_SR_VSYNC_ACTIVE; + } + else + { + m_sr &= ~UPD7220_SR_VSYNC_ACTIVE; + } - m_write_vsync(param); + m_write_vsync(param); - update_vsync_timer(param); - break; + update_vsync_timer(param); +} - case TIMER_BLANK: - if (param) - { - m_sr |= UPD7220_SR_HBLANK_ACTIVE; - } - else - { - m_sr &= ~UPD7220_SR_HBLANK_ACTIVE; - } +TIMER_CALLBACK_MEMBER(upd7220_device::blank_update) +{ + if (param) + { + m_sr |= UPD7220_SR_HBLANK_ACTIVE; + } + else + { + m_sr &= ~UPD7220_SR_HBLANK_ACTIVE; + } - m_write_blank(param); + m_write_blank(param); - update_blank_timer(param); - break; - } + update_blank_timer(param); } @@ -789,26 +847,15 @@ void upd7220_device::device_timer(emu_timer &timer, device_timer_id id, int para // draw_pixel - //------------------------------------------------- -void upd7220_device::draw_pixel(int x, int y, int xi, uint16_t tile_data) +void upd7220_device::draw_pixel() { - uint32_t addr = ((y * (m_pitch << (m_figs.m_gd ? 0 : 1))) + (x >> 3)) & 0x3ffff; - uint16_t data = readword(addr); - uint16_t new_pixel = (tile_data & (1 << (xi & 0xf))) ? (1 << (x & 0xf)) : 0; + LOGDRAW("draw_pixel(): EAD=%08x MASK=%04x MOD=%02x - FIGS DIR=%02x DC=%d\n", m_ead, m_mask, m_bitmap_mod, m_figs.m_dir, m_figs.m_dc); - switch(m_bitmap_mod) + for(int i = 0; i <= m_figs.m_dc; ++i) { - case 0: //replace - writeword(addr, (data & ~(1 << (x & 0xf))) | new_pixel); - break; - case 1: //complement - writeword(addr, data ^ new_pixel); - break; - case 2: //reset - writeword(addr, data & ~new_pixel); - break; - case 3: //set - writeword(addr, data | new_pixel); - break; + const uint16_t pattern = get_pattern(i & 0xf); + write_vram(0, m_bitmap_mod, pattern, m_mask); + next_pixel(m_figs.m_dir); } } @@ -817,219 +864,239 @@ void upd7220_device::draw_pixel(int x, int y, int xi, uint16_t tile_data) // draw_line - //------------------------------------------------- -void upd7220_device::draw_line(int x, int y) +void upd7220_device::draw_line() { - int xi, yi; - int d = (m_figs.m_d & 0x2000) ? (int16_t)(m_figs.m_d | 0xe000) : m_figs.m_d; - int d1 = (m_figs.m_d1 & 0x2000) ? (int16_t)(m_figs.m_d1 | 0xe000) : m_figs.m_d1; - int d2 = (m_figs.m_d2 & 0x2000) ? (int16_t)(m_figs.m_d2 | 0xe000) : m_figs.m_d2; - uint16_t pattern = (m_ra[8]) | (m_ra[9]<<8); - const int dot_dir[4] = {1, -1, -1, 1}; + int d = util::sext(m_figs.m_d, 14); + int d1 = util::sext(m_figs.m_d1, 14); + int d2 = util::sext(m_figs.m_d2, 14); + const uint8_t octant = m_figs.m_dir; - LOG("uPD7220 line check: %d %d %02x %08x %d %d %d\n",x,y,m_figs.m_dir,m_ead,d1,m_figs.m_dc,m_bitmap_mod); + LOGDRAW("draw_line(): EAD=%08x MASK=%04x MOD=%02x - FIGS DIR=%02x DC=%d D=%d D1=%d D2=%d\n", m_ead, m_mask, m_bitmap_mod, m_figs.m_dir, m_figs.m_dc, d, d1, d2); - for(yi = xi = 0; yi <= m_figs.m_dc; yi++) + for(int i = 0; i <= m_figs.m_dc; ++i) { - switch(m_figs.m_dir & 3) + const uint16_t pattern = get_pattern(i & 0xf); + write_vram(0, m_bitmap_mod, pattern, m_mask); + if (octant & 1) { - case 1: - case 2: - draw_pixel(yi * dot_dir[((m_figs.m_dir >> 1) + 3) & 3] + x, xi * dot_dir[m_figs.m_dir >> 1] + y, yi, pattern); - break; - default: - draw_pixel(xi * dot_dir[((m_figs.m_dir >> 1) + 3) & 3] + x, yi * dot_dir[m_figs.m_dir >> 1] + y, yi, pattern); - break; + m_figs.m_dir = (d < 0) ? (octant + 1) & 7 : octant; } - if(d > 0) + else { - xi++; - d += d2; + m_figs.m_dir = (d < 0) ? octant : (octant + 1) & 7; } - else - d += d1; - } - - switch(m_figs.m_dir & 3) - { - case 1: - case 2: - x += yi * dot_dir[((m_figs.m_dir >> 1) + 3) & 3]; - y += xi * dot_dir[m_figs.m_dir >> 1]; - break; - default: - x += xi * dot_dir[((m_figs.m_dir >> 1) + 3) & 3]; - y += yi * dot_dir[m_figs.m_dir >> 1]; - break; + d += ((d < 0) ? d1 : d2); + next_pixel(m_figs.m_dir); } - - m_ead = (x >> 4) + (y * (m_pitch >> m_figs.m_gd)); - m_dad = x & 0x0f; } + //------------------------------------------------- // draw_arc - //------------------------------------------------- -void upd7220_device::draw_arc(int x, int y) +void upd7220_device::draw_arc() { - int xi = 0, err = -m_figs.m_d, d = m_figs.m_d + 1; - uint16_t pattern = (m_ra[8]) | (m_ra[9]<<8); - const int dot_dir[4] = {1, -1, -1, 1}; + int err = -m_figs.m_d, d = m_figs.m_d + 1; + + const uint16_t octant = m_figs.m_dir; - LOG("uPD7220 arc check: %d %d %02x %08x %d %d %d\n",x,y,m_figs.m_dir,m_ead,m_figs.m_dm,m_figs.m_dc,m_figs.m_d); + LOGDRAW("draw_arc(): EAD=%08x MASK=%04x MOD=%02x - FIGS DIR=%02x DC=%d D=%d D2=%d DM=%d\n", m_ead, m_mask, m_bitmap_mod, m_figs.m_dir, m_figs.m_dc, m_figs.m_d, m_figs.m_d2, m_figs.m_dm); - for(int yi = 0; yi <= m_figs.m_dc; yi++) + for (int i = 0; i <= m_figs.m_dc; ++i) { - if(yi >= m_figs.m_dm) + const uint16_t pattern = get_pattern(i % 0xf); + if (i >= m_figs.m_dm) { - switch(m_figs.m_dir & 3) - { - case 1: - case 2: - draw_pixel(yi * dot_dir[((m_figs.m_dir >> 1) + 3) & 3] + x, xi * dot_dir[m_figs.m_dir >> 1] + y, yi, pattern); - break; - default: - draw_pixel(xi * dot_dir[((m_figs.m_dir >> 1) + 3) & 3] + x, yi * dot_dir[m_figs.m_dir >> 1] + y, yi, pattern); - break; - } + write_vram(0, m_bitmap_mod, pattern, m_mask); + } + if (err < 0) + { + m_figs.m_dir = (octant & 1) ? (octant + 1) & 7 : octant; } - if(err < 0) - err += (yi + 1) << 1; else { - xi++; - d--; - err += (yi - d + 1) << 1; + m_figs.m_dir = (octant & 1) ? octant : (octant + 1) & 7; } + err += (err < 0) ? ((i + 1) << 1) : ((i - --d + 1) << 1); + next_pixel(m_figs.m_dir); } - switch(m_figs.m_dir & 3) - { - case 1: - case 2: - x += (m_figs.m_dc + 1) * dot_dir[((m_figs.m_dir >> 1) + 3) & 3]; - break; - default: - y += (m_figs.m_dc + 1) * dot_dir[m_figs.m_dir >> 1]; - break; - } - - m_ead = (x >> 4) + (y * (m_pitch >> m_figs.m_gd)); - m_dad = x & 0x0f; } + //------------------------------------------------- // draw_rectangle - //------------------------------------------------- -void upd7220_device::draw_rectangle(int x, int y) +void upd7220_device::draw_rectangle() { - int i; - const int rect_x_dir[8] = { 0, 1, 0,-1, 1, 1,-1,-1 }; - const int rect_y_dir[8] = { 1, 0,-1, 0, 1,-1,-1, 1 }; - uint8_t rect_type,rect_dir; - uint16_t pattern = (m_ra[8]) | (m_ra[9]<<8); + assert(m_figs.m_dc == 3); - LOG("uPD7220 rectangle check: %d %d %02x %08x\n",x,y,m_figs.m_dir,m_ead); + LOGDRAW("draw_rectangle(): EAD=%08x MASK=%04x MOD=%02x - FIGS DIR=%02x DC=%d D=%d D2=%d\n",m_ead, m_mask, m_bitmap_mod, m_figs.m_dir, m_figs.m_dc, m_figs.m_d, m_figs.m_d2); - rect_type = (m_figs.m_dir & 1) << 2; - rect_dir = rect_type | (((m_figs.m_dir >> 1) + 0) & 3); - - for(i = 0;i < m_figs.m_d;i++) + for (int i = 0; i <= m_figs.m_dc; ++i) { - draw_pixel(x,y,i,pattern); - x+=rect_x_dir[rect_dir]; - y+=rect_y_dir[rect_dir]; + const uint16_t dist = (i & 1 ? m_figs.m_d2 : m_figs.m_d); + for (int j = 0; j < dist; ++j) + { + const uint16_t pattern = get_pattern(j & 0xf); + write_vram(0, m_bitmap_mod, pattern, m_mask); + if (i > 0 && j == 0) + m_figs.m_dir = (m_figs.m_dir + 2) & 7; + next_pixel(m_figs.m_dir); + } + } +} - rect_dir = rect_type | (((m_figs.m_dir >> 1) + 1) & 3); - for(i = 0;i < m_figs.m_d2;i++) - { - draw_pixel(x,y,i,pattern); - x+=rect_x_dir[rect_dir]; - y+=rect_y_dir[rect_dir]; - } +//------------------------------------------------- +// draw_char - +//------------------------------------------------- - rect_dir = rect_type | (((m_figs.m_dir >> 1) + 2) & 3); +void upd7220_device::draw_char() +{ + const int8_t dir_change[2][4] = { + {2, 2, -2, -2}, + {1, 3, -3, -1} + }; + const uint8_t type = (m_figs.m_figure_type & 0x10) >> 4; - for(i = 0;i < m_figs.m_d;i++) + LOGDRAW("draw_char(): EAD=%08x MASK=%04x MOD=%02x DIR=%02x D=%d DC=%d FIGURE=%02x\n",m_ead,m_mask,m_bitmap_mod,m_figs.m_dir,m_figs.m_d,m_figs.m_dc,m_figs.m_figure_type); + + for (int i = 0, di = 0; i < (m_figs.m_dc + 1); ++i) { - draw_pixel(x,y,i,pattern); - x+=rect_x_dir[rect_dir]; - y+=rect_y_dir[rect_dir]; + m_pattern = (m_ra[15 - (i & 7)] << 8) | m_ra[15 - (i & 7)]; + for (int zdc = 0; zdc <= m_gchr; ++zdc, ++di) + { + for (int j = 0; j < m_figs.m_d; ++j) + { + const uint16_t pattern = (di % 2) ? get_pattern(15-(j & 0xf)) : get_pattern((j & 0xf)); + for (int zd = 0; zd <= m_gchr; ++zd) + { + write_vram(0, m_bitmap_mod, pattern, m_mask); + + if (j != (m_figs.m_d - 1) || zd != m_gchr) + next_pixel(m_figs.m_dir); + } + } + m_figs.m_dir = (((uint16_t)m_figs.m_dir + dir_change[type][(di % 2) << 1]) & 7); + next_pixel(m_figs.m_dir); + m_figs.m_dir = (((uint16_t)m_figs.m_dir + dir_change[type][((di % 2) << 1)+1]) & 7); + } } +} - rect_dir = rect_type | (((m_figs.m_dir >> 1) + 3) & 3); - for(i = 0;i < m_figs.m_d2;i++) - { - draw_pixel(x,y,i,pattern); - x+=rect_x_dir[rect_dir]; - y+=rect_y_dir[rect_dir]; - } +//------------------------------------------------- +// helper functions used to rotate a uint16_t +//------------------------------------------------- - m_ead = (x >> 4) + (y * (m_pitch >> m_figs.m_gd)); - m_dad = x & 0x0f; +static constexpr uint16_t rotate_right(uint16_t value) +{ + return (value>>1) | (value<<( (-1) & 0x0f )); +} +static constexpr uint16_t rotate_left(uint16_t value) +{ + return (value<<1) | (value>>( (-1) & 0x0f )); } //------------------------------------------------- -// draw_char - +// get_pitch - //------------------------------------------------- -void upd7220_device::draw_char(int x, int y) +uint16_t upd7220_device::get_pitch() { - int isize,psize; - uint16_t tile_data; + bool mixed = ((m_mode & UPD7220_MODE_DISPLAY_MASK) == UPD7220_MODE_DISPLAY_MIXED); + + if (mixed) + return m_pitch >> m_figs.m_gd; + + return m_pitch; +} + - LOG("uPD7220 char check: %d %d %02x %08x %d %d %02x\n",x,y,m_figs.m_dir,m_ead,m_figs.m_d,m_figs.m_dc,m_figs.m_figure_type); +//------------------------------------------------- +// get_pattern - +//------------------------------------------------- - /* QX10 may require upper bits for psize, VT240 requires the opposite */ - isize = m_figs.m_d; - psize = m_figs.m_dc + 1; +uint16_t upd7220_device::get_pattern(uint8_t cycle) +{ + bool mixed = ((m_mode & UPD7220_MODE_DISPLAY_MASK) == UPD7220_MODE_DISPLAY_MIXED); + bool graphics = ((m_mode & UPD7220_MODE_DISPLAY_MASK) == UPD7220_MODE_DISPLAY_GRAPHICS); - for(int pi = 0; pi < psize; pi++) + if ((mixed && m_figs.m_gd) || graphics) { - tile_data = (m_ra[((psize-1-pi) & 7) | 8] << 8) | m_ra[((psize-1-pi) & 7) | 8]; - for(int pz = 0; pz <= m_gchr; pz++) + return ((m_pattern >> (cycle & 0xf)) & 1) * 0xffff; + } + return m_pattern; +} + + +//------------------------------------------------- +// next_pixel - +//------------------------------------------------- + +void upd7220_device::next_pixel(int direction) +{ + switch(direction & 7) + { + case 0: + m_ead += get_pitch(); + break; + case 1: + m_ead += get_pitch(); + if (m_mask & 0x8000) { - int ii = 0, curpixel = 0; - if(pi & 1) - { - ii = isize - 1; - curpixel = (isize * (m_gchr + 1)) - 1; - } - while(pi & 1 ? ii >= 0 : ii < isize) - { - for(int iz = 0; iz <= m_gchr; iz++) - { - draw_pixel(x + (curpixel * x_dir[m_figs.m_dir]), y + (curpixel * y_dir[m_figs.m_dir]), ii, tile_data); - if(pi & 1) - curpixel--; - else - curpixel++; - } - if(pi & 1) - ii--; - else - ii++; - } - if(m_figs.m_figure_type == 2) - { - x += x_dir[(m_figs.m_dir + 2) & 7]; - y += y_dir[(m_figs.m_dir + 2) & 7]; - } - else - { - x += x_dir[(m_figs.m_dir + 1) & 7]; - y += y_dir[(m_figs.m_dir + 1) & 7]; - } + m_ead += 1; + } + m_mask = rotate_left(m_mask); + break; + case 2: + if (m_mask & 0x8000) + { + m_ead += 1; + } + m_mask = rotate_left(m_mask); + break; + case 3: + m_ead -= get_pitch(); + if (m_mask & 0x8000) + { + m_ead += 1; } + m_mask = rotate_left(m_mask); + break; + case 4: + m_ead -= get_pitch(); + break; + case 5: + m_ead -= get_pitch(); + if (m_mask & 0x1) + { + m_ead -= 1; + } + m_mask = rotate_right(m_mask); + break; + case 6: + if (m_mask & 0x1) + { + m_ead -= 1; + } + m_mask = rotate_right(m_mask); + break; + case 7: + m_ead += get_pitch(); + if (m_mask & 0x1) + { + m_ead -= 1; + } + m_mask = rotate_right(m_mask); + break; } - - m_ead = (x >> 4) + (y * (m_pitch >> m_figs.m_gd)); - m_dad = (x & 0xf); + m_ead &= 0x3ffff; } @@ -1056,13 +1123,12 @@ int upd7220_device::translate_command(uint8_t data) case UPD7220_COMMAND_CURD: command = COMMAND_CURD; break; case UPD7220_COMMAND_LPRD: command = COMMAND_LPRD; break; case UPD7220_COMMAND_5A: command = COMMAND_5A; break; - case UPD7220_COMMAND_05: command = COMMAND_05; break; default: switch (data & 0xfe) { case UPD7220_COMMAND_SYNC: command = COMMAND_SYNC; break; case UPD7220_COMMAND_VSYNC: command = COMMAND_VSYNC; break; - case UPD7220_COMMAND_BCTRL: command = COMMAND_BCTRL; break; + case UPD7220_COMMAND_BLANK: command = COMMAND_BLANK; break; default: switch (data & 0xf0) { @@ -1082,6 +1148,20 @@ int upd7220_device::translate_command(uint8_t data) return command; } +int upd7220a_device::translate_command(uint8_t data) +{ + int command = upd7220_device::translate_command(data); + + switch (data) + { + case UPD7220_COMMAND_RESET2: command = COMMAND_RESET2; break; + case UPD7220_COMMAND_RESET3: command = COMMAND_RESET3; break; + case UPD7220_COMMAND_BLANK2: command = COMMAND_BLANK2; break; + } + + return command; +} + //------------------------------------------------- // process_fifo - @@ -1091,22 +1171,27 @@ void upd7220_device::process_fifo() { uint8_t data; int flag; - uint16_t eff_pitch = m_pitch >> m_figs.m_gd; + int cr; dequeue(&data, &flag); if (flag == FIFO_COMMAND) { - m_cr = data; - m_param_ptr = 1; + cr = translate_command(data); + if (cr != COMMAND_BLANK) // workaround for Rainbow 100 Windows 1.03, needs verification + { + m_cr = data; + m_param_ptr = 1; + } } else { + cr = translate_command(m_cr); m_pr[m_param_ptr] = data; m_param_ptr++; } - switch (translate_command(m_cr)) + switch (cr) { case COMMAND_INVALID: logerror("uPD7220 Invalid Command Byte %02x\n", m_cr); @@ -1118,17 +1203,29 @@ void upd7220_device::process_fifo() break; case COMMAND_RESET: /* reset */ + case COMMAND_RESET2: + case COMMAND_RESET3: switch (m_param_ptr) { - case 0: - LOG("uPD7220 RESET\n"); - - m_de = 0; - m_ra[0] = m_ra[1] = m_ra[2] = 0; - m_ra[3] = 0x19; + case 1: + LOGCMD("RESET %02x\n", cr); + + if (cr != COMMAND_RESET3) + m_de = 0; + // NOTE: a reset doesn't touch anything belonging to parameters, as stated in docs. + // pc9801:mightyhd won't program sad/len but just issue a reset after BIOS + // (which originally sets len = 0x1ff) + // with this on, it will len = 400 lines, repeating the bottommost two rows in a 432 setup. + //m_ra[0] = m_ra[1] = m_ra[2] = 0; + //m_ra[3] = 0x19; m_ead = 0; - m_dad = 0; + // TODO: very unlikely, should be 0xffff assuming it's even touched ... m_mask = 0; + // FIFO, Command Processor and internal counters are cleared by this + // - pc9801rs BIOS starts up a DMAW command that spindiz2 will dislike during its boot sequences + m_sr &= ~UPD7220_SR_DRAWING_IN_PROGRESS; + fifo_clear(); + stop_dma(); break; case 9: @@ -1144,16 +1241,15 @@ void upd7220_device::process_fifo() m_pitch = m_aw; - LOG("uPD7220 Mode: %02x\n", m_mode); - LOG("uPD7220 AW: %u\n", m_aw); - LOG("uPD7220 HS: %u\n", m_hs); - LOG("uPD7220 VS: %u\n", m_vs); - LOG("uPD7220 HFP: %u\n", m_hfp); - LOG("uPD7220 HBP: %u\n", m_hbp); - LOG("uPD7220 VFP: %u\n", m_vfp); - LOG("uPD7220 AL: %u\n", m_al); - LOG("uPD7220 VBP: %u\n", m_vbp); - LOG("uPD7220 PITCH: %u\n", m_pitch); + if (type() == UPD7220A) + { + m_pitch = ((m_pr[5] & 0x40) << 2) | (m_pitch & 0xff); + m_al += (m_pr[6] >> 6) & 1; + } + + LOGCMD("- Mode: %02x\n", m_mode); + LOGCMD("- HS=%u HBP=%u AW=%u HFP=%u PITCH=%u\n", m_hs, m_hbp, m_aw, m_hfp, m_pitch); + LOGCMD("- VS=%u VBP=%u AL=%u VFP=%u\n", m_vs, m_vbp, m_al, m_vfp); recompute_parameters(); break; @@ -1161,6 +1257,13 @@ void upd7220_device::process_fifo() break; case COMMAND_SYNC: /* sync format specify */ + if (flag == FIFO_COMMAND) + { + m_de = m_cr & 1; + LOGCMD2("SYNC %u\n", m_de); + } + + // TODO: merge implementation with above if (m_param_ptr == 9) { m_mode = m_pr[1]; @@ -1175,16 +1278,15 @@ void upd7220_device::process_fifo() m_pitch = m_aw; - LOG("uPD7220 Mode: %02x\n", m_mode); - LOG("uPD7220 AW: %u\n", m_aw); - LOG("uPD7220 HS: %u\n", m_hs); - LOG("uPD7220 VS: %u\n", m_vs); - LOG("uPD7220 HFP: %u\n", m_hfp); - LOG("uPD7220 HBP: %u\n", m_hbp); - LOG("uPD7220 VFP: %u\n", m_vfp); - LOG("uPD7220 AL: %u\n", m_al); - LOG("uPD7220 VBP: %u\n", m_vbp); - LOG("uPD7220 PITCH: %u\n", m_pitch); + if (type() == UPD7220A) + { + m_pitch = ((m_pr[5] & 0x40) << 2) | (m_pitch & 0xff); + m_al += (m_pr[6] >> 6) & 1; + } + + LOGCMD("- Mode: %02x\n", m_mode); + LOGCMD("- HS=%u HBP=%u AW=%u HFP=%u PITCH=%u\n", m_hs, m_hbp, m_aw, m_hfp, m_pitch); + LOGCMD("- VS=%u VBP=%u AL=%u VFP=%u\n", m_vs, m_vbp, m_al, m_vfp); recompute_parameters(); } @@ -1193,55 +1295,54 @@ void upd7220_device::process_fifo() case COMMAND_VSYNC: /* vertical sync mode */ m_m = m_cr & 0x01; - LOG("uPD7220 M: %u\n", m_m); + LOGCMD("VSYNC M=%u\n", m_m); recompute_parameters(); break; case COMMAND_CCHAR: /* cursor & character characteristics */ - if(m_param_ptr == 2) + switch (m_param_ptr) { + case 2: m_lr = (m_pr[1] & 0x1f) + 1; m_dc = BIT(m_pr[1], 7); - LOG("uPD7220 LR: %u\n", m_lr); - LOG("uPD7220 DC: %u\n", m_dc); - } + LOGCMD("CCHAR LR=%u DC=%u\n", m_lr, m_dc); + break; - if(m_param_ptr == 3) - { + case 3: m_ctop = m_pr[2] & 0x1f; m_sc = BIT(m_pr[2], 5); m_br = (m_pr[2] >> 6); /* guess, assume that blink rate clears upper bits (if any) */ - LOG("uPD7220 CTOP: %u\n", m_ctop); - LOG("uPD7220 SC: %u\n", m_sc); - } + LOGCMD("- CTOP=%u SC=%u\n", m_ctop, m_sc); + break; - if(m_param_ptr == 4) - { + case 4: m_br = ((m_pr[3] & 0x07) << 2) | (m_pr[2] >> 6); m_cbot = m_pr[3] >> 3; - LOG("uPD7220 BR: %u\n", m_br); - LOG("uPD7220 CBOT: %u\n", m_cbot); + LOGCMD("- BR=%u CBOT=%u\n", m_br, m_cbot); + break; } break; case COMMAND_START: /* start display & end idle mode */ m_de = 1; - //LOG("uPD7220 '%s' DE: 1\n"); + LOGCMD2("START\n"); break; - case COMMAND_05: + case COMMAND_BLANK2: m_de = 0; + LOGCMD2("BLANK2\n"); + break; - case COMMAND_BCTRL: /* display blanking control */ - m_de = m_cr & 0x01; + case COMMAND_BLANK: /* display blanking control */ + m_de = data & 0x01; - //LOG("uPD7220 DE: %u\n", m_de); + LOGCMD2("BLANK %u\n", m_de); break; case COMMAND_ZOOM: /* zoom factors specify */ @@ -1250,8 +1351,7 @@ void upd7220_device::process_fifo() m_gchr = m_pr[1] & 0x0f; m_disp = m_pr[1] >> 4; - LOG("uPD7220 GCHR: %01x\n", m_gchr); - LOG("uPD7220 DISP: %01x\n", m_disp); + LOGCMD("ZOOM GCHR=%01x DISP=%01x\n", m_gchr, m_disp); } break; @@ -1262,12 +1362,12 @@ void upd7220_device::process_fifo() m_ead = (upper_addr << 16) | (m_pr[2] << 8) | m_pr[1]; - LOG("uPD7220 EAD: %06x\n", m_ead); + LOGCMD("CURS EAD: %06x\n", m_ead); if(m_param_ptr == 4) { - m_dad = m_pr[3] >> 4; - LOG("uPD7220 DAD: %01x\n", m_dad); + m_mask = 1 << ((m_pr[3] >> 4) & 0x0f); + LOGCMD("- MASK: %04x\n", m_mask); } } break; @@ -1281,8 +1381,13 @@ void upd7220_device::process_fifo() { if (m_ra_addr < 16) { - LOG("uPD7220 RA%u: %02x\n", m_ra_addr, data); + LOGCMD("PRAM RA%u: %02x\n", m_ra_addr, data); + switch (m_ra_addr) + { + case 8: m_pattern = (m_pattern & 0xff00) | data; break; + case 9: m_pattern = (m_pattern & 0x00ff) | (data << 8); break; + } m_ra[m_ra_addr] = data; m_ra_addr++; } @@ -1292,11 +1397,21 @@ void upd7220_device::process_fifo() break; case COMMAND_PITCH: /* pitch specification */ - if (flag == FIFO_PARAMETER) + // pc9801:burai writes a spurious extra value during intro, effectively ignored + // (only first value matters) + if (flag == FIFO_PARAMETER && m_param_ptr == 2) { - m_pitch = data; + m_pitch = (m_pitch & 0x100) | data; + + if (m_pitch < 2) + { + // TODO: a pitch of zero will lead to a MAME crash in draw_graphics_line + // Coerce a fail-safe minimum, what should really happen is to be verified ... + popmessage("%s pitch == 0!", this->tag()); + m_pitch = 2; + } - LOG("uPD7220 PITCH: %u\n", m_pitch); + LOGCMD("PITCH %u\n", m_pitch); } break; @@ -1305,10 +1420,23 @@ void upd7220_device::process_fifo() if (m_param_ptr == 3 || (m_param_ptr == 2 && m_cr & 0x10)) { - LOG("%02x = %02x %02x (%c) %06x %04x\n",m_cr,m_pr[2],m_pr[1],m_pr[1]?m_pr[1]:' ',m_ead,m_figs.m_dc); + m_pattern = (m_pattern & 0xff00) | m_pr[1]; + if (m_param_ptr == 3) + m_pattern = (m_pattern & 0xff) | (m_pr[2] << 8); + LOGCMD3("WDAT PATTERN=%04x\n", m_pattern); + if (m_figs.m_figure_type) + break; + LOGCMD3("- CR=%02x (%02x %02x) (%c) EAD=%06x - FIGS DC=%04x\n" + , m_cr + , m_pr[2] + , m_pr[1] + , m_pr[1] ? m_pr[1]:' ' + , m_ead + , m_figs.m_dc + ); fifo_set_direction(FIFO_WRITE); - write_vram((m_cr & 0x18) >> 3,m_cr & 3); + wdat((m_cr & 0x18) >> 3,m_cr & 3); reset_figs_param(); m_param_ptr = 1; } @@ -1319,95 +1447,113 @@ void upd7220_device::process_fifo() { m_mask = (m_pr[2] << 8) | m_pr[1]; - LOG("uPD7220 MASK: %04x\n", m_mask); + LOGCMD("MASK %04x\n", m_mask); } break; case COMMAND_FIGS: /* figure drawing parameters specify */ - if (m_param_ptr == 2) + switch (m_param_ptr) { + case 2: m_figs.m_dir = m_pr[1] & 0x7; m_figs.m_figure_type = (m_pr[1] & 0xf8) >> 3; + LOGCMD("FIGS DIR=%02x FIGURE=%02x\n", m_figs.m_dir, m_figs.m_figure_type); //if(m_figs.m_dir != 2) // printf("DIR %02x\n",m_pr[1]); - } + break; - // the Decision Mate V during start-up test upload only 2 params before execute the - // RDAT command, so I assume this is the expected behaviour, but this needs to be verified. - if (m_param_ptr == 3) + // the Decision Mate V during start-up test upload only 2 params before execute the + // RDAT command, so I assume this is the expected behaviour, but this needs to be verified. + case 3: m_figs.m_dc = (m_pr[2]) | (m_figs.m_dc & 0x3f00); + LOGCMD("- DC=%04x (*)\n", m_figs.m_dc); + break; - if (m_param_ptr == 4) - { + case 4: m_figs.m_dc = (m_pr[2]) | ((m_pr[3] & 0x3f) << 8); m_figs.m_gd = (m_pr[3] & 0x40) && ((m_mode & UPD7220_MODE_DISPLAY_MASK) == UPD7220_MODE_DISPLAY_MIXED); - } + LOGCMD("- DC=%04x\n", m_figs.m_dc); + LOGCMD("- GD=%02x\n", m_figs.m_gd); + break; - if (m_param_ptr == 6) + case 6: m_figs.m_d = (m_pr[4]) | ((m_pr[5] & 0x3f) << 8); + LOGCMD("- D=%04x\n", m_figs.m_d); + break; - if (m_param_ptr == 8) + case 8: m_figs.m_d2 = (m_pr[6]) | ((m_pr[7] & 0x3f) << 8); + LOGCMD("- D2=%04x\n", m_figs.m_d2); + break; - if (m_param_ptr == 10) + case 10: m_figs.m_d1 = (m_pr[8]) | ((m_pr[9] & 0x3f) << 8); + LOGCMD("- D1=%04x\n", m_figs.m_d1); + break; - if (m_param_ptr == 12) + case 12: m_figs.m_dm = (m_pr[10]) | ((m_pr[11] & 0x3f) << 8); - + LOGCMD("- DM=%04x\n", m_figs.m_dm); + break; + } break; case COMMAND_FIGD: /* figure draw start */ - if(m_figs.m_figure_type == 0) - draw_pixel(((m_ead % eff_pitch) << 4) | (m_dad & 0xf),(m_ead / eff_pitch),m_dad,(m_ra[8]) | (m_ra[9]<<8)); - else if(m_figs.m_figure_type == 1) - draw_line(((m_ead % eff_pitch) << 4) | (m_dad & 0xf),(m_ead / eff_pitch)); - else if(m_figs.m_figure_type == 4) - draw_arc(((m_ead % eff_pitch) << 4) | (m_dad & 0xf),(m_ead / eff_pitch)); - else if(m_figs.m_figure_type == 8) - draw_rectangle(((m_ead % eff_pitch) << 4) | (m_dad & 0xf),(m_ead / eff_pitch)); - else - logerror("uPD7220 Unimplemented command FIGD %02x\n", m_figs.m_figure_type); - + LOGCMD("FIGD %u\n", m_figs.m_figure_type); + switch (m_figs.m_figure_type) + { + case 0: draw_pixel(); break; + case 1: draw_line(); break; + case 4: draw_arc(); break; + case 8: draw_rectangle(); break; + default: + popmessage("%s Unimplemented command FIGD %02x\n", this->tag(), m_figs.m_figure_type); + break; + } reset_figs_param(); m_sr |= UPD7220_SR_DRAWING_IN_PROGRESS; break; case COMMAND_GCHRD: /* graphics character draw and area filling start */ + LOGCMD("GCHRD %u\n", m_figs.m_figure_type); + if((m_figs.m_figure_type & 0xf) == 2) - draw_char(((m_ead % eff_pitch) << 4) | (m_dad & 0xf),(m_ead / eff_pitch)); + draw_char(); else - logerror("uPD7220 Unimplemented command GCHRD %02x\n", m_figs.m_figure_type); + popmessage("%s Unimplemented command GCHRD %02x\n", this->tag(), m_figs.m_figure_type); reset_figs_param(); m_sr |= UPD7220_SR_DRAWING_IN_PROGRESS; break; case COMMAND_RDAT: /* read data from display memory */ + LOGCMD3("RDAT %06x %02x\n", m_ead, m_cr); + fifo_set_direction(FIFO_READ); - read_vram((m_cr & 0x18) >> 3,m_cr & 3); + rdat((m_cr & 0x18) >> 3,m_cr & 3); m_sr |= UPD7220_SR_DATA_READY; break; case COMMAND_CURD: /* cursor address read */ - { - uint16_t dad = 1 << m_dad; + LOGCMD3("CURD %06x\n", m_ead, m_mask); + fifo_set_direction(FIFO_READ); queue(m_ead & 0xff, 0); queue((m_ead >> 8) & 0xff, 0); queue(m_ead >> 16, 0); - queue(dad & 0xff, 0); - queue(dad >> 8, 0); + queue(m_mask & 0xff, 0); + queue(m_mask >> 8, 0); m_sr |= UPD7220_SR_DATA_READY; break; - } case COMMAND_LPRD: /* light pen address read */ + LOGCMD3("LPRD %06x\n", m_lad); + fifo_set_direction(FIFO_READ); queue(m_lad & 0xff, 0); @@ -1419,11 +1565,19 @@ void upd7220_device::process_fifo() break; case COMMAND_DMAR: /* DMA read request */ - logerror("uPD7220 Unimplemented command DMAR\n"); + LOGCMD4("DMAR CR=%02x DC=%d D=%d\n", m_cr, m_figs.m_dc, m_figs.m_d); + m_dma_type = (m_cr >> 3) & 3; + m_dma_mod = m_cr & 3; + m_dma_transfer_length = (m_figs.m_dc + 1) * (m_figs.m_d + 2); + start_dma(); break; case COMMAND_DMAW: /* DMA write request */ - logerror("uPD7220 Unimplemented command DMAW\n"); + LOGCMD4("DMAW CR=%02x DC=%d D=%d\n", m_cr, m_figs.m_dc, m_figs.m_d); + m_dma_type = (m_cr >> 3) & 3; + m_dma_mod = m_cr & 3; + m_dma_transfer_length = (m_figs.m_dc + 1) * (m_figs.m_d + 1); + start_dma(); break; } } @@ -1438,7 +1592,7 @@ void upd7220_device::continue_command() // continue RDAT command when data to read are larger than the FIFO (a5105 and dmv text scrolling) if (m_figs.m_dc && translate_command(m_cr) == COMMAND_RDAT) { - read_vram((m_cr & 0x18) >> 3, m_cr & 3); + rdat((m_cr & 0x18) >> 3, m_cr & 3); m_sr |= UPD7220_SR_DATA_READY; } } @@ -1468,7 +1622,6 @@ uint8_t upd7220_device::read(offs_t offset) /* TODO: timing of these */ m_sr &= ~UPD7220_SR_DRAWING_IN_PROGRESS; - m_sr &= ~UPD7220_SR_DMA_EXECUTE; } return data; @@ -1504,7 +1657,36 @@ void upd7220_device::write(offs_t offset, uint8_t data) uint8_t upd7220_device::dack_r() { - return 0; + uint8_t result = 0; + switch(m_dma_type) + { + case 0: + if (m_dma_transfer_length % 2 == 0) + { + m_dma_data = read_vram(); + result = m_dma_data & 0xff; + } + else + { + result = (m_dma_data >> 8) & 0xff; + } + break; + case 2: + m_dma_data = read_vram(); + result = m_dma_data & 0xff; + break; + case 3: + m_dma_data = read_vram(); + result = (m_dma_data >> 8) & 0xff; + break; + default: + logerror("uPD7220 Invalid DMA Transfer Type\n"); + } + if (--m_dma_transfer_length == 0) + { + stop_dma(); + } + return result; } @@ -1514,6 +1696,59 @@ uint8_t upd7220_device::dack_r() void upd7220_device::dack_w(uint8_t data) { + switch(m_dma_type) + { + case 0: + if (m_dma_transfer_length % 2) + { + m_dma_data = ((m_dma_data & 0xff) | data << 8) & m_mask; + write_vram(m_dma_type, m_dma_mod, m_dma_data); + m_ead += x_dir[m_figs.m_dir] + (y_dir[m_figs.m_dir] * get_pitch()); + m_ead &= 0x3ffff; + } + else + { + m_dma_data = (m_dma_data & 0xff00) | data; + } + break; + case 2: + m_dma_data = data & (m_mask & 0xff); + write_vram(m_dma_type, m_dma_mod, m_dma_data); + m_ead += x_dir[m_figs.m_dir] + (y_dir[m_figs.m_dir] * get_pitch()); + m_ead &= 0x3ffff; + break; + case 3: + m_dma_data = (data << 8) & (m_mask & 0xff00); + write_vram(m_dma_type, m_dma_mod, m_dma_data); + m_ead += x_dir[m_figs.m_dir] + (y_dir[m_figs.m_dir] * get_pitch()); + m_ead &= 0x3ffff; + break; + default: + logerror("uPD7220 Invalid DMA Transfer Type\n"); + } + if (--m_dma_transfer_length == 0) + { + stop_dma(); + } +} + +void upd7220_device::start_dma() +{ + if ((m_sr & UPD7220_SR_DMA_EXECUTE) == 0) + { + m_write_drq(ASSERT_LINE); + m_sr |= UPD7220_SR_DMA_EXECUTE; + } +} + +void upd7220_device::stop_dma() +{ + if ((m_sr & UPD7220_SR_DMA_EXECUTE) == UPD7220_SR_DMA_EXECUTE) + { + m_write_drq(CLEAR_LINE); + m_sr &= ~UPD7220_SR_DMA_EXECUTE; + reset_figs_param(); + } } @@ -1521,7 +1756,7 @@ void upd7220_device::dack_w(uint8_t data) // ext_sync_w - //------------------------------------------------- -WRITE_LINE_MEMBER( upd7220_device::ext_sync_w ) +void upd7220_device::ext_sync_w(int state) { //LOG("uPD7220 External Synchronization: %u\n", state); @@ -1540,7 +1775,7 @@ WRITE_LINE_MEMBER( upd7220_device::ext_sync_w ) // ext_sync_w - //------------------------------------------------- -WRITE_LINE_MEMBER( upd7220_device::lpen_w ) +void upd7220_device::lpen_w(int state) { /* only if 2 rising edges on the lpen input occur at the same point during successive video fields are the pulses accepted */ @@ -1562,21 +1797,20 @@ WRITE_LINE_MEMBER( upd7220_device::lpen_w ) void upd7220_device::update_text(bitmap_rgb32 &bitmap, const rectangle &cliprect) { - uint32_t addr, sad; + uint32_t sad; uint16_t len; int im, wd; - int y, sy = 0; + int sy = 0; for (int area = 0; area < 4; area++) { get_text_partition(area, &sad, &len, &im, &wd); + int y; for (y = sy; y < sy + len; y++) { - addr = sad + (y * m_pitch); - - if (!m_draw_text_cb.isnull()) - m_draw_text_cb(bitmap, addr, (y * m_lr) + m_vbp, wd, m_pitch, m_lr, m_dc, m_ead); + uint32_t const addr = sad + (y * m_pitch); + m_draw_text_cb(bitmap, addr, (y * m_lr) + m_vbp, wd, m_pitch, m_lr, m_dc, m_ead, m_ctop, m_cbot); } sy = y + 1; @@ -1588,16 +1822,17 @@ void upd7220_device::update_text(bitmap_rgb32 &bitmap, const rectangle &cliprect // draw_graphics_line - //------------------------------------------------- -void upd7220_device::draw_graphics_line(bitmap_rgb32 &bitmap, uint32_t addr, int y, int wd, int pitch) +// TODO: merge with update_graphics +void upd7220_device::draw_graphics_line(bitmap_rgb32 &bitmap, uint32_t addr, int y, int wd, int mixed) { - int sx, al = bitmap.cliprect().height(); + int al = bitmap.cliprect().height(); + int aw = m_aw >> mixed; + int pitch = m_pitch >> mixed; - for (sx = 0; sx < pitch; sx++) + for (int sx = 0; sx < aw; sx++) { - if((sx << 4) < m_aw * 16 && y < al) - m_display_cb(bitmap, y, sx << 4, addr); - - addr+= (wd + 1) * 2; + if(sx < m_aw && y < al) + m_display_cb(bitmap, y, sx << 4, addr + (wd + 1) * (sx % pitch)); } } @@ -1608,58 +1843,79 @@ void upd7220_device::draw_graphics_line(bitmap_rgb32 &bitmap, uint32_t addr, int void upd7220_device::update_graphics(bitmap_rgb32 &bitmap, const rectangle &cliprect, int force_bitmap) { - uint32_t addr, sad; + uint32_t sad; uint16_t len; - int im, wd, area; + int im, wd; int y = 0, tsy = 0, bsy = 0; - bool mixed = ((m_mode & UPD7220_MODE_DISPLAY_MASK) == UPD7220_MODE_DISPLAY_MIXED); - uint8_t interlace = ((m_mode & UPD7220_MODE_INTERLACE_MASK) == UPD7220_MODE_INTERLACE_ON) ? 0 : 1; + int mixed = ((m_mode & UPD7220_MODE_DISPLAY_MASK) == UPD7220_MODE_DISPLAY_MIXED) ? 1 : 0; + uint8_t interlace = ((m_mode & UPD7220_MODE_INTERLACE_MASK) == UPD7220_MODE_INTERLACE_ON) ? 1 : 0; + uint8_t zoom = m_disp + 1; + + LOGAREA("FRAME=%d MODE=%02x FORCE BITMAP=%d ZOOM=%02x PITCH=%d\n", screen().frame_number(), m_mode, force_bitmap, zoom, m_pitch); - for (area = 0; area < 4; area++) + for(int area = 0; area < 4; area++) { get_graphics_partition(area, &sad, &len, &im, &wd); - if (im || force_bitmap) - { - //get_graphics_partition(area, &sad, &len, &im, &wd); + LOGAREA("%s: AREA=%d BSY=%4d SAD=%06x len=%04x im=%d wd=%d\n", this->tag(), area, bsy, sad, len, im, wd); + + // pc9821:aitd (256 color mode) and pc9821:os2warp3 (16, installation screens) wants this shift + const u8 pitch_shift = force_bitmap ? im : mixed; - if(area >= 3) // TODO: most likely to be correct, Quarth (PC-98xx) definitely draws with area 2. We might see an area 3 someday ... + if(im || force_bitmap) + { + // according to documentation only areas 0-1-2 can be drawn in bitmap mode + // - pc98:quarth definitely needs area 2 for player section. + // - pc98:steamhea wants area 3 for scrolling and dialogue screens to work together, + // contradicting the doc. Fixed in 7220A or applies just for mixed mode? + // TODO: what happens if combined area size is smaller than display height? + // documentation suggests that it should repeat from area 0, needs real HW verification (no known SW do it). + if (area >= 3 && !force_bitmap) break; - if(!interlace) + // pc98:madoum1-3 sets up ALL areas to a length of 0 after initial intro screen. + // madoum1: area 0 sad==0 on gameplay (PC=0x955e7), sad==0xaa0 on second intro screen (tower) then intentionally scrolls up and back to initial position. + // Suggests that length should be treated as max size if this occurs, this is also proven to be correct via real HW verification. + // TODO: check if text partition do the same. + if (len == 0) + len = 0x400; + + if (interlace) len <<= 1; - for (y = 0; y < len; y++) + for(y = 0; y < len; y++) { - /* TODO: again correct? - Quarth (PC-98xx) doesn't seem to use pitch here and it definitely wants bsy to be /2 to make scrolling to work. - Xevious (PC-98xx) wants the pitch to be fixed at 80, and wants bsy to be /1 - Dragon Buster (PC-98xx) contradicts with Xevious with regards of the pitch tho ... */ - addr = ((sad << 1) & 0x3ffff) + ((y / (mixed ? 1 : m_lr)) * (m_pitch << (im ? 0 : 1))); - - if (!m_display_cb.isnull()) - draw_graphics_line(bitmap, addr, y + bsy + m_vbp, wd, (m_pitch << interlace)); + // TODO: not completely correct, all is drawn half size with real HW tests on pc98 msdos by just changing PRAM values. + // pc98 quarth doesn't seem to use pitch here and it definitely wants bsy to be /2 to make scrolling to work. + // pc98 xevious wants the pitch to be fixed at 80, and wants bsy to be /1 + // pc98 dbuster contradicts with Xevious with regards of the pitch tho ... + uint32_t const addr = (sad & 0x3ffff) + ((y / (mixed ? 1 : m_lr)) * (m_pitch >> pitch_shift)); + for(int z = 0; z <= m_disp; ++z) + { + int yval = (y*zoom)+z + (bsy + m_vbp); + // pc9801:duel sets up bitmap layer with height 384 vs. 400 of text layer + // so we scissor here, interlace wants it bumped x2 (microbx2) + if(yval >= cliprect.top() && yval <= cliprect.bottom() && (yval - m_vbp) < m_al << interlace) + draw_graphics_line(bitmap, addr, yval, wd, pitch_shift); + } } } else { - get_text_partition(area, &sad, &len, &im, &wd); - if(m_lr) { - for (y = 0; y < len; y+=m_lr) + for(y = 0; y < len; y += m_lr) { - addr = (sad & 0x3ffff) + ((y / m_lr) * m_pitch); - - if (!m_draw_text_cb.isnull()) - m_draw_text_cb(bitmap, addr, y + tsy + m_vbp, wd, m_pitch, m_lr, m_dc, m_ead); + uint32_t const addr = (sad & 0x3ffff) + ((y / m_lr) * m_pitch); + int yval = y * zoom + (tsy + m_vbp); + m_draw_text_cb(bitmap, addr, yval, wd, m_pitch, m_lr, m_dc, m_ead, m_ctop, m_cbot); } } } if (m_lr) - tsy += y; - bsy += y; + tsy += y * zoom; + bsy += y * zoom; } } @@ -1687,7 +1943,7 @@ uint32_t upd7220_device::screen_update(screen_device &screen, bitmap_rgb32 &bitm break; case UPD7220_MODE_DISPLAY_INVALID: - LOG("uPD7220 Invalid Display Mode!\n"); + popmessage("%s Invalid Display Mode!", this->tag()); } } return 0; |