diff --git a/platform/x86real/pc98/egc.cpp b/platform/x86real/pc98/egc.cpp new file mode 100644 index 00000000..8a7e5312 --- /dev/null +++ b/platform/x86real/pc98/egc.cpp @@ -0,0 +1,185 @@ +#include "platform.h" +#include "x86real.h" +#include "pc98.h" +#include "planar.h" +#include "platform/x86real/pc98/egc.hpp" +#include "platform/x86real/pc98/graph.hpp" +#include "platform/x86real/pc98/grcg.hpp" +#include "platform/x86real/pc98/page.hpp" +#include "platform/x86real/pc98/palette.hpp" +#include "platform/x86real/pc98/vsync.hpp" + +enum egc_register_t { + EGC_ACTIVEPLANEREG = 0x04A0, + EGC_READPLANEREG = 0x04A2, + EGC_MODE_ROP_REG = 0x04A4, + EGC_FGCOLORREG = 0x04A6, + EGC_MASKREG = 0x04A8, + EGC_BGCOLORREG = 0x04AA, + EGC_ADDRRESSREG = 0x04AC, + EGC_BITLENGTHREG = 0x04AE, +}; + +EGCCopy::EGCCopy() +{ + // The EGC does in fact require an active GRCG. + // (See PC-9801 Programmers' Bible, p. 456) + _outportb_(0x7C, GC_TDW); + + graph_egc_on(); + outport(EGC_ACTIVEPLANEREG, 0xFFF0); + outport(EGC_READPLANEREG, 0x00FF); + outport(EGC_MASKREG, 0xFFFF); + outport(EGC_ADDRRESSREG, 0); + outport(EGC_MODE_ROP_REG, 0x29F0); +} + +EGCCopy::~EGCCopy() +{ + graph_egc_off(); + _outportb_(0x7C, GC_OFF); +} + +// Rectangle blitting +// ------------------ +// The EGC automatically reinitializes its internal shifter after copying the +// number of dots indicated in the bit length register. This makes it ideal to +// be used in rectangular loops, as nothing needs to be reinitialized for +// successive row loops. + +inline uvram_offset_t vram_offset_even( + const uvram_word_amount_t& x, const uvram_y_t& y +) { + return (vram_offset_shift(0, y) + (x * EGC_REGISTER_SIZE)); +} + +inline vram_byte_amount_t copy_width( + const pixel_t& w, const upixel_t& dst_first +) { + return (( + (dst_first + w + EGC_REGISTER_MASK) / EGC_REGISTER_DOTS + ) * EGC_REGISTER_SIZE); +} + +void EGCCopy::rect( + screen_x_t src_left, + vram_y_t src_top, + screen_x_t dst_left, + vram_y_t dst_top, + pixel_t w, + pixel_t h +) +{ + if(w <= 0) { + return; + } + _SI = (vram_offset_shift(src_left, src_top) & ~1); + _DI = (vram_offset_shift(dst_left, dst_top) & ~1); + const upixel_t src_first = (src_left & EGC_REGISTER_MASK); + const upixel_t dst_first = (dst_left & EGC_REGISTER_MASK); + const register vram_byte_amount_t vram_w = copy_width(w, dst_first); + outport(EGC_ADDRRESSREG, (src_first | (dst_first << 4))); + outport(EGC_BITLENGTHREG, (w - 1)); + _ES = SEG_PLANE_B; + if(src_first > dst_first) { + for(pixel_t y = 0; y < h; y++) { + // Need to read one additional word to prefill the tile register. + _AX = *reinterpret_cast(_SI); + _SI += EGC_REGISTER_SIZE; + + for(_BX = 0; _BX < vram_w; _BX += EGC_REGISTER_SIZE) { + _AX = *reinterpret_cast(_SI + _BX); + *reinterpret_cast(_DI + _BX) = _AX; + } + _SI += (ROW_SIZE - EGC_REGISTER_SIZE); + _DI += ROW_SIZE; + } + } else { + for(pixel_t y = 0; y < h; y++) { + for(_BX = 0; _BX < vram_w; _BX += EGC_REGISTER_SIZE) { + _AX = *reinterpret_cast(_SI + _BX); + *reinterpret_cast(_DI + _BX) = _AX; + } + _SI += ROW_SIZE; + _DI += ROW_SIZE; + } + } +} + +void EGCCopy::rect_interpage( + screen_x_t src_left, + vram_y_t src_top, + screen_x_t dst_left, + vram_y_t dst_top, + pixel_t w, + pixel_t h, + page_t src_page +) +{ + if(w <= 0) { + return; + } + _SI = (vram_offset_shift(src_left, src_top) & ~1); + _DI = (vram_offset_shift(dst_left, dst_top) & ~1); + const upixel_t src_first = (src_left & EGC_REGISTER_MASK); + const upixel_t dst_first = (dst_left & EGC_REGISTER_MASK); + const register vram_byte_amount_t vram_w = copy_width(w, dst_first); + const bool dst_page = (src_page ^ 1); + outport(EGC_BITLENGTHREG, (w - 1)); + outport(EGC_ADDRRESSREG, (src_first | (dst_first << 4))); + _ES = SEG_PLANE_B; + if(src_first > dst_first) { + for(pixel_t y = 0; y < h; y++) { + // Need to read one additional word to prefill the tile register. + page_access(src_page); + _AX = *reinterpret_cast(_SI); + _SI += EGC_REGISTER_SIZE; + + for(_BX = 0; _BX < vram_w; _BX += EGC_REGISTER_SIZE) { + page_access(src_page); + _AX = *reinterpret_cast(_SI + _BX); + page_access(dst_page); + *reinterpret_cast(_DI + _BX) = _AX; + } + _SI += (ROW_SIZE - EGC_REGISTER_SIZE); + _DI += ROW_SIZE; + } + } else { + for(pixel_t y = 0; y < h; y++) { + for(_BX = 0; _BX < vram_w; _BX += EGC_REGISTER_SIZE) { + page_access(src_page); + _AX = *reinterpret_cast(_SI + _BX); + page_access(dst_page); + *reinterpret_cast(_DI + _BX) = _AX; + } + _SI += ROW_SIZE; + _DI += ROW_SIZE; + } + } +} + +void EGCCopy::rect_interpage( + screen_x_t left, vram_y_t top, pixel_t w, pixel_t h, page_t src_page +) +{ + if(w <= 0) { + return; + } + uvram_offset_t vo = (vram_offset_shift(left, top) & ~1); + const upixel_t first = (left & EGC_REGISTER_MASK); + const register vram_byte_amount_t vram_w = copy_width(w, first); + const bool dst_page = (src_page ^ 1); + outport(EGC_ADDRRESSREG, (first | (first << 4))); + outport(EGC_BITLENGTHREG, (w - 1)); + _ES = SEG_PLANE_B; + for(pixel_t y = 0; y < h; y++) { + for(_BX = 0; _BX < vram_w; _BX += EGC_REGISTER_SIZE) { + page_access(src_page); + _AX = *reinterpret_cast(vo + _BX); + page_access(dst_page); + *reinterpret_cast(vo + _BX) = _AX; + } + vo += ROW_SIZE; + } +} +// ------------------ diff --git a/platform/x86real/pc98/egc.hpp b/platform/x86real/pc98/egc.hpp new file mode 100644 index 00000000..ea5999af --- /dev/null +++ b/platform/x86real/pc98/egc.hpp @@ -0,0 +1,72 @@ +// C++ RAII EGC implementation +// --------------------------- +// EGC wrapper that automatically disables the EGC if the object goes out of +// scope. + +struct EGCCopy { + EGCCopy(); + ~EGCCopy(); + + // Rectangular blitting + // -------------------- + // All of these assume that the given rectangles lie fully within VRAM. + // The inter-page functions return with ![src_page] as the accessed page. + + // Blits the ([w]×[h}) rectangle starting at ([src_left], [src_top]) to + // ([dst_left], [dst_top]) on the current VRAM page. + void rect( + screen_x_t src_left, + vram_y_t src_top, + screen_x_t dst_left, + vram_y_t dst_top, + pixel_t w, + pixel_t h + ); + + // Blits the ([w]×[h}) rectangle starting at ([src_left], [src_top]) on + // VRAM page [src_page] to ([dst_left], [dst_top]) on the other VRAM page. + void rect_interpage( + screen_x_t src_left, + vram_y_t src_top, + screen_x_t dst_left, + vram_y_t dst_top, + pixel_t w, + pixel_t h, + page_t src_page + ); + + // Blits the ([w]×[h}) rectangle starting at ([left], [top]) on VRAM page + // [src_page] to the same position on the other VRAM page. + void rect_interpage( + screen_x_t left, vram_y_t top, pixel_t w, pixel_t h, page_t src_page + ); + // ------------------ +}; + +// Internal EGC implementation macros +// ---------------------------------- + +inline void graph_mode_change(bool enable_or_disable) { + _outportb_(0x6A, (0x06 + enable_or_disable)); +} + +// Requires graphics mode changing to be enabled via +// graph_mode_change(true). +inline void graph_mode_egc(bool enable_or_disable) { + _outportb_(0x6A, (0x04 + enable_or_disable)); +} + +inline void graph_egc(bool enable_or_disable) { + graph_mode_change(true); + graph_mode_egc(enable_or_disable); + graph_mode_change(false); +} + +inline void graph_egc_on(void) { + graph_egc(true); +} + +inline void graph_egc_off(void) { + graph_egc(false); +} +// ---------------------------------- diff --git a/th01/hardware/egc.h b/th01/hardware/egc.h index 47f54475..4f0247c6 100644 --- a/th01/hardware/egc.h +++ b/th01/hardware/egc.h @@ -1,35 +1,5 @@ #include "defconv.h" -/// Enabling and disabling -/// ---------------------- - -#ifdef X86REAL_H - inline void graph_mode_change(bool enable_or_disable) { - _outportb_(0x6A, (0x06 + enable_or_disable)); - } - - // Requires graphics mode changing to be enabled via - // graph_mode_change(true). - inline void graph_mode_egc(bool enable_or_disable) { - _outportb_(0x6A, (0x04 + enable_or_disable)); - } - - inline void graph_egc(bool enable_or_disable) { - graph_mode_change(true); - graph_mode_egc(enable_or_disable); - graph_mode_change(false); - } - - inline void graph_egc_on(void) { - graph_egc(true); - } - - inline void graph_egc_off(void) { - graph_egc(false); - } -#endif -/// ---------------------- - // Requires the EGC to have been activated before. #define egc_setup_copy() \ outport2(EGC_ACTIVEPLANEREG, 0xFFF0); \ diff --git a/th01/zunsoft.cpp b/th01/zunsoft.cpp index 02c714dd..d53e15cc 100644 --- a/th01/zunsoft.cpp +++ b/th01/zunsoft.cpp @@ -7,6 +7,7 @@ #include "x86real.h" #include "pc98.h" #include "master.hpp" +#include "platform/x86real/pc98/egc.hpp" #include "platform/x86real/pc98/page.hpp" #include "th01/hardware/egc.h" #include "th01/math/polar.hpp" diff --git a/th04/hardware/egcrect.cpp b/th04/hardware/egcrect.cpp index a43cb429..778f1345 100644 --- a/th04/hardware/egcrect.cpp +++ b/th04/hardware/egcrect.cpp @@ -6,6 +6,7 @@ #include "planar.h" #include "master.hpp" #include "platform/x86real/flags.hpp" +#include "platform/x86real/pc98/egc.hpp" extern "C" { #include "th01/hardware/egc.h"