[Decompilation] [th01] master.lib resident palette function reimplementations
Which store colors as GRB, as suggested by the structure's ID string.
Even master.lib's own functions add an additional XCHG AH, AL
instruction to get colors into and out of this format. MASTER.MAN
suggests that it's some sort of standard on PC-98. It does match the
order of ths hardware's palette register ports, after all.
(0AAh = green, 0ACh = red, 0AEh = blue)
Now we also know why __seg* wasn't used more commonly, as lamented in
c8e8e98. Turbo C++ simply doesn't support a lot of arithmetic on
segment pointers.
And then that undecompilable far call to a function within the same
segment, but inside a different translation unit…
Also, thanks again to Egor for the SCOPY@ hack that debuted in 0460072.
Would have probably struggled with this a lot more without that.
And *then* you realize that TH01 effectively doesn't even use the
resident palette. 😐
And yes, we're procrastinating the whole issue of potentially using
a single translation unit for all three binaries by using a common
segment name, because it *really* isn't that easy.
Completes P0066, funded by Keyblade Wiedling Neko and Splashman.
2020-01-05 15:10:00 +00:00
|
|
|
#include <dos.h>
|
2021-01-26 15:33:06 +00:00
|
|
|
#include <mem.h>
|
2020-01-11 20:06:26 +00:00
|
|
|
#include <mbctype.h>
|
|
|
|
#include <mbstring.h>
|
2022-03-12 23:14:12 +00:00
|
|
|
#include "platform.h"
|
2022-08-09 01:24:33 +00:00
|
|
|
#include "decomp.hpp"
|
2022-03-12 23:14:12 +00:00
|
|
|
#include "pc98.h"
|
|
|
|
#include "planar.h"
|
2021-01-26 15:33:06 +00:00
|
|
|
#include "master.hpp"
|
2022-08-09 01:24:33 +00:00
|
|
|
#include "shiftjis.hpp"
|
2021-09-24 09:16:45 +00:00
|
|
|
#include "th01/v_colors.hpp"
|
2021-09-19 18:51:21 +00:00
|
|
|
#include "th01/math/clamp.hpp"
|
2020-01-12 18:13:26 +00:00
|
|
|
#include "th01/hardware/egc.h"
|
2022-08-08 23:38:13 +00:00
|
|
|
#include "th01/hardware/vsync.hpp"
|
2020-01-10 20:25:29 +00:00
|
|
|
#include "th01/hardware/graph.h"
|
2022-08-09 00:32:20 +00:00
|
|
|
#include "th01/hardware/grppsafx.h"
|
2020-03-02 19:22:10 +00:00
|
|
|
#include "th01/hardware/palette.h"
|
[Decompilation] [th01] master.lib resident palette function reimplementations
Which store colors as GRB, as suggested by the structure's ID string.
Even master.lib's own functions add an additional XCHG AH, AL
instruction to get colors into and out of this format. MASTER.MAN
suggests that it's some sort of standard on PC-98. It does match the
order of ths hardware's palette register ports, after all.
(0AAh = green, 0ACh = red, 0AEh = blue)
Now we also know why __seg* wasn't used more commonly, as lamented in
c8e8e98. Turbo C++ simply doesn't support a lot of arithmetic on
segment pointers.
And then that undecompilable far call to a function within the same
segment, but inside a different translation unit…
Also, thanks again to Egor for the SCOPY@ hack that debuted in 0460072.
Would have probably struggled with this a lot more without that.
And *then* you realize that TH01 effectively doesn't even use the
resident palette. 😐
And yes, we're procrastinating the whole issue of potentially using
a single translation unit for all three binaries by using a common
segment name, because it *really* isn't that easy.
Completes P0066, funded by Keyblade Wiedling Neko and Splashman.
2020-01-05 15:10:00 +00:00
|
|
|
|
2020-03-01 17:48:44 +00:00
|
|
|
#undef grcg_off
|
|
|
|
#define grcg_off() outportb(0x7C, 0);
|
|
|
|
|
2022-08-10 20:20:27 +00:00
|
|
|
// Never read from, so it's supposedly only there for debugging purposes?
|
|
|
|
static screen_point_t graph_r_last_line_end;
|
|
|
|
|
|
|
|
static int8_t unused; // ZUN bloat
|
|
|
|
static page_t page_accessed;
|
2020-03-01 16:15:24 +00:00
|
|
|
|
2020-01-10 20:25:29 +00:00
|
|
|
/// VRAM plane "structures"
|
|
|
|
/// -----------------------
|
|
|
|
#define Planes_declare(var) \
|
2021-02-03 17:12:06 +00:00
|
|
|
dots8_t far *var##_B = reinterpret_cast<dots8_t __seg *>(SEG_PLANE_B); \
|
|
|
|
dots8_t far *var##_R = reinterpret_cast<dots8_t __seg *>(SEG_PLANE_R); \
|
|
|
|
dots8_t far *var##_G = reinterpret_cast<dots8_t __seg *>(SEG_PLANE_G); \
|
|
|
|
dots8_t far *var##_E = reinterpret_cast<dots8_t __seg *>(SEG_PLANE_E);
|
2020-01-10 20:25:29 +00:00
|
|
|
|
|
|
|
#define Planes_next_row(var) \
|
|
|
|
var##_B += ROW_SIZE; \
|
|
|
|
var##_R += ROW_SIZE; \
|
|
|
|
var##_G += ROW_SIZE; \
|
|
|
|
var##_E += ROW_SIZE;
|
|
|
|
|
|
|
|
#define Planes_offset(var, x, y) \
|
2020-06-16 10:43:39 +00:00
|
|
|
var##_B += vram_offset_divmul(x, y); \
|
|
|
|
var##_R += vram_offset_divmul(x, y); \
|
|
|
|
var##_G += vram_offset_divmul(x, y); \
|
|
|
|
var##_E += vram_offset_divmul(x, y);
|
2020-01-10 20:25:29 +00:00
|
|
|
|
|
|
|
#define PlanarRow_declare(var) \
|
2020-03-06 23:25:03 +00:00
|
|
|
dots8_t var##_B[ROW_SIZE]; \
|
|
|
|
dots8_t var##_R[ROW_SIZE]; \
|
|
|
|
dots8_t var##_G[ROW_SIZE]; \
|
|
|
|
dots8_t var##_E[ROW_SIZE]; \
|
2020-01-10 20:25:29 +00:00
|
|
|
|
|
|
|
#define PlanarRow_blit(dst, src, bytes) \
|
|
|
|
memcpy(dst##_B, src##_B, bytes); \
|
|
|
|
memcpy(dst##_R, src##_R, bytes); \
|
|
|
|
memcpy(dst##_G, src##_G, bytes); \
|
|
|
|
memcpy(dst##_E, src##_E, bytes);
|
|
|
|
/// -----------------------
|
|
|
|
|
2020-01-11 21:50:33 +00:00
|
|
|
/// Clipping
|
|
|
|
/// --------
|
2022-08-04 13:29:34 +00:00
|
|
|
|
2022-03-11 23:48:15 +00:00
|
|
|
#define fix_order(tmp, low, high) \
|
2020-01-11 21:50:33 +00:00
|
|
|
if(low > high) { \
|
2022-03-11 23:48:15 +00:00
|
|
|
tmp = low; \
|
2020-01-11 21:50:33 +00:00
|
|
|
low = high; \
|
2022-03-11 23:48:15 +00:00
|
|
|
high = tmp; \
|
2020-01-11 21:50:33 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#define clip_min(low, high, minimum) \
|
|
|
|
if(low < minimum) { \
|
|
|
|
if(high < minimum) { \
|
|
|
|
return; \
|
|
|
|
} \
|
|
|
|
low = minimum; \
|
|
|
|
}
|
|
|
|
|
|
|
|
#define clip_max(low, high, maximum) \
|
|
|
|
if(high > maximum) { \
|
|
|
|
if(low > maximum) { \
|
|
|
|
return; \
|
|
|
|
} \
|
|
|
|
high = maximum; \
|
|
|
|
}
|
|
|
|
|
|
|
|
#define clip_x(left, right) \
|
|
|
|
clip_min(left, right, 0); \
|
|
|
|
clip_max(left, right, (RES_X - 1));
|
|
|
|
|
|
|
|
#define clip_y(top, bottom) \
|
|
|
|
clip_min(top, bottom, 0); \
|
|
|
|
clip_max(top, bottom, (RES_Y - 1));
|
|
|
|
/// --------
|
|
|
|
|
2020-03-01 21:47:46 +00:00
|
|
|
/// BIOS
|
|
|
|
/// ----
|
2022-08-04 13:29:34 +00:00
|
|
|
|
2022-07-07 12:36:07 +00:00
|
|
|
inline void graph_access_and_show_0() {
|
2020-03-01 21:47:46 +00:00
|
|
|
graph_accesspage_func(0);
|
|
|
|
graph_showpage_func(0);
|
|
|
|
}
|
|
|
|
|
2022-07-07 12:36:07 +00:00
|
|
|
inline void cgrom_code_and_grcg_off() {
|
2020-03-01 21:47:46 +00:00
|
|
|
outportb(0x68, 0xA); // CG ROM code access
|
|
|
|
grcg_off();
|
|
|
|
}
|
|
|
|
|
2022-07-07 12:36:07 +00:00
|
|
|
inline void z_graph_400line() {
|
2020-03-01 21:47:46 +00:00
|
|
|
REGS regs;
|
|
|
|
|
|
|
|
z_graph_hide();
|
|
|
|
|
|
|
|
// 640x400
|
|
|
|
regs.h.ah = 0x42;
|
|
|
|
regs.h.ch = 0xC0;
|
|
|
|
int86(0x18, ®s, ®s);
|
|
|
|
|
|
|
|
// 16-color, analog mode
|
|
|
|
outportb(0x6A, 1);
|
|
|
|
}
|
|
|
|
|
2022-07-07 12:36:07 +00:00
|
|
|
inline void z_graph_access_and_show_0() {
|
2020-03-01 21:47:46 +00:00
|
|
|
graph_access_and_show_0();
|
|
|
|
cgrom_code_and_grcg_off();
|
|
|
|
z_graph_show();
|
|
|
|
}
|
|
|
|
|
|
|
|
void z_graph_init()
|
|
|
|
{
|
|
|
|
z_graph_400line();
|
|
|
|
z_palette_set_all_show(z_Palettes);
|
|
|
|
graph_access_and_show_0();
|
|
|
|
z_graph_clear_0();
|
|
|
|
cgrom_code_and_grcg_off();
|
|
|
|
z_graph_show();
|
|
|
|
}
|
|
|
|
|
|
|
|
void graph_400line_access_and_show_0()
|
|
|
|
{
|
|
|
|
z_graph_400line();
|
|
|
|
z_graph_access_and_show_0();
|
|
|
|
}
|
|
|
|
|
|
|
|
void z_graph_exit()
|
|
|
|
{
|
|
|
|
z_palette_black();
|
|
|
|
z_graph_clear_0();
|
|
|
|
graph_access_and_show_0();
|
|
|
|
z_graph_show();
|
|
|
|
cgrom_code_and_grcg_off();
|
|
|
|
}
|
|
|
|
|
|
|
|
void z_graph_show()
|
|
|
|
{
|
|
|
|
REGS regs;
|
|
|
|
regs.h.ah = 0x40;
|
|
|
|
int86(0x18, ®s, ®s);
|
|
|
|
}
|
|
|
|
|
|
|
|
void z_graph_hide()
|
|
|
|
{
|
|
|
|
REGS regs;
|
|
|
|
regs.h.ah = 0x41;
|
|
|
|
int86(0x18, ®s, ®s);
|
|
|
|
}
|
|
|
|
/// ----
|
|
|
|
|
2020-03-01 19:48:24 +00:00
|
|
|
/// Page flipping
|
|
|
|
/// -------------
|
2022-08-04 13:29:34 +00:00
|
|
|
|
2020-03-01 19:48:24 +00:00
|
|
|
void graph_showpage_func(page_t page)
|
|
|
|
{
|
|
|
|
outportb(0xA4, page);
|
|
|
|
}
|
|
|
|
|
|
|
|
void graph_accesspage_func(int page)
|
|
|
|
{
|
2021-11-07 17:26:03 +00:00
|
|
|
page_accessed = page;
|
2020-03-01 19:48:24 +00:00
|
|
|
outportb(0xA6, page);
|
|
|
|
}
|
|
|
|
/// -------------
|
2020-01-11 20:06:26 +00:00
|
|
|
|
2020-03-01 17:48:44 +00:00
|
|
|
/// Hardware
|
|
|
|
/// --------
|
2022-08-04 13:29:34 +00:00
|
|
|
|
2020-03-01 17:48:44 +00:00
|
|
|
void z_palette_show_single(int col, int r, int g, int b)
|
|
|
|
{
|
|
|
|
outportb(0xA8, col);
|
|
|
|
outportb(0xAA, g);
|
|
|
|
outportb(0xAC, r);
|
|
|
|
outportb(0xAE, b);
|
|
|
|
}
|
|
|
|
|
|
|
|
#define grcg_setcolor(mode, col) \
|
|
|
|
outportb(0x7C, mode); \
|
|
|
|
outportb(0x7E, (col & 1) ? 0xFF : 0x00); \
|
|
|
|
outportb(0x7E, (col & 2) ? 0xFF : 0x00); \
|
|
|
|
outportb(0x7E, (col & 4) ? 0xFF : 0x00); \
|
|
|
|
outportb(0x7E, (col & 8) ? 0xFF : 0x00);
|
|
|
|
|
|
|
|
void grcg_setcolor_rmw(int col)
|
|
|
|
{
|
|
|
|
grcg_setcolor(0xC0, col);
|
|
|
|
}
|
|
|
|
|
2022-01-01 19:07:25 +00:00
|
|
|
void grcg_setcolor_tcr(int col)
|
2020-03-01 17:48:44 +00:00
|
|
|
{
|
|
|
|
grcg_setcolor(0x80, col);
|
|
|
|
}
|
|
|
|
|
|
|
|
void grcg_off_func(void)
|
|
|
|
{
|
|
|
|
grcg_off();
|
|
|
|
}
|
|
|
|
/// --------
|
|
|
|
|
|
|
|
/// Palette
|
|
|
|
/// -------
|
2022-08-10 20:20:27 +00:00
|
|
|
|
|
|
|
Palette4 z_Palettes = {
|
|
|
|
// These match the Z_ATRB_* bits, interestingly?
|
|
|
|
0x0, 0x0, 0x0, // 0: Black
|
|
|
|
0x0, 0x0, 0xF, // 1: Blue
|
|
|
|
0x0, 0xF, 0x0, // 2: Green
|
|
|
|
0x0, 0xF, 0xF, // 3: Cyan
|
|
|
|
0xF, 0x0, 0x0, // 4: Red
|
|
|
|
0xF, 0x0, 0xF, // 5: Magenta
|
|
|
|
0xF, 0xF, 0x0, // 6: Yellow
|
|
|
|
0xF, 0xF, 0xF, // 7: White
|
|
|
|
|
|
|
|
0x8, 0x8, 0x8, // 8: Gray
|
|
|
|
0x0, 0x0, 0xA, // 9: Dark blue
|
|
|
|
0x0, 0xA, 0x0, // 10: Dark green
|
|
|
|
0x0, 0xA, 0xA, // 11: Dark cyan
|
|
|
|
0xA, 0x0, 0x0, // 12: Dark red
|
|
|
|
0xA, 0x0, 0xA, // 13: Purple
|
|
|
|
0xA, 0xA, 0x0, // 14: Gold
|
|
|
|
0xC, 0xC, 0xC, // 15: Silver
|
|
|
|
};
|
|
|
|
|
2020-03-01 17:48:44 +00:00
|
|
|
void z_palette_set_all_show(const Palette4& pal)
|
|
|
|
{
|
|
|
|
for(int i = 0; i < COLOR_COUNT; i++) {
|
|
|
|
z_palette_set_show(i, pal[i].c.r, pal[i].c.g, pal[i].c.b);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void z_palette_set_show(int col, int r, int g, int b)
|
|
|
|
{
|
2021-09-19 18:51:21 +00:00
|
|
|
r = clamp_min(clamp_max(r, RGB4::max()), 0);
|
|
|
|
g = clamp_min(clamp_max(g, RGB4::max()), 0);
|
|
|
|
b = clamp_min(clamp_max(b, RGB4::max()), 0);
|
2020-03-01 17:48:44 +00:00
|
|
|
|
|
|
|
z_Palettes[col].c.r = r;
|
|
|
|
z_Palettes[col].c.g = g;
|
|
|
|
z_Palettes[col].c.b = b;
|
|
|
|
z_palette_show_single(col, r, g, b);
|
|
|
|
}
|
|
|
|
/// -------
|
|
|
|
|
2020-03-01 16:15:24 +00:00
|
|
|
/// Whole-page functions
|
|
|
|
/// --------------------
|
|
|
|
void z_graph_clear()
|
|
|
|
{
|
2021-02-03 17:12:06 +00:00
|
|
|
dots8_t far *plane = reinterpret_cast<dots8_t __seg *>(SEG_PLANE_B);
|
2020-03-01 16:15:24 +00:00
|
|
|
|
2020-03-01 17:48:44 +00:00
|
|
|
grcg_setcolor_rmw(0);
|
|
|
|
memset(plane, 0xFF, PLANE_SIZE);
|
|
|
|
grcg_off_func();
|
2020-03-01 16:15:24 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void z_graph_clear_0(void)
|
|
|
|
{
|
|
|
|
// Yes, page 2, twice. Which effectively is the same as page 0... at least
|
|
|
|
// according to any real hardware and emulator tests I could come up with.
|
2020-03-01 19:48:24 +00:00
|
|
|
graph_accesspage_func(2); z_graph_clear();
|
|
|
|
graph_accesspage_func(2); z_graph_clear();
|
2020-03-01 16:15:24 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void z_graph_clear_col(uint4_t col)
|
|
|
|
{
|
2021-02-03 17:12:06 +00:00
|
|
|
dots8_t far *plane = reinterpret_cast<dots8_t __seg *>(SEG_PLANE_B);
|
2020-03-01 16:15:24 +00:00
|
|
|
|
2020-03-01 17:48:44 +00:00
|
|
|
grcg_setcolor_rmw(col);
|
|
|
|
memset(plane, 0xFF, PLANE_SIZE);
|
|
|
|
grcg_off_func();
|
2020-03-01 16:15:24 +00:00
|
|
|
}
|
|
|
|
|
2021-11-07 17:26:03 +00:00
|
|
|
void graph_copy_accessed_page_to_other(void)
|
2020-03-01 16:15:24 +00:00
|
|
|
{
|
|
|
|
PlanarRow_declare(tmp);
|
|
|
|
Planes_declare(p);
|
2021-11-07 17:26:03 +00:00
|
|
|
page_t page_front = (page_accessed ^ 1);
|
2020-03-01 16:15:24 +00:00
|
|
|
|
2020-08-20 19:59:45 +00:00
|
|
|
for(screen_y_t y = 0; y < RES_Y; y++) {
|
2020-03-01 16:15:24 +00:00
|
|
|
PlanarRow_blit(tmp, p, ROW_SIZE);
|
|
|
|
graph_accesspage(page_front);
|
|
|
|
PlanarRow_blit(p, tmp, ROW_SIZE);
|
2021-11-07 17:26:03 +00:00
|
|
|
graph_accesspage(page_accessed);
|
2020-03-01 16:15:24 +00:00
|
|
|
Planes_next_row(p);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
/// --------------------
|
2020-01-06 13:40:42 +00:00
|
|
|
|
2020-03-01 11:24:01 +00:00
|
|
|
/// Palette fades
|
|
|
|
/// -------------
|
2022-08-10 20:20:27 +00:00
|
|
|
|
2020-03-01 11:24:01 +00:00
|
|
|
#define FADE_DELAY 10
|
|
|
|
#define fade_loop(pal, per_comp) \
|
|
|
|
for(int i = 0; i < pal.range(); i++) { \
|
|
|
|
z_vsync_wait(); \
|
|
|
|
for(int col = 0; col < COLOR_COUNT; col++) { \
|
2021-05-16 20:40:36 +00:00
|
|
|
for(int comp = 0; comp < COMPONENT_COUNT; comp++) { \
|
2020-03-01 11:24:01 +00:00
|
|
|
per_comp; \
|
|
|
|
} \
|
2020-03-01 17:48:44 +00:00
|
|
|
z_palette_show_single_col(col, pal[col]); \
|
2020-03-01 11:24:01 +00:00
|
|
|
} \
|
|
|
|
delay(FADE_DELAY); \
|
|
|
|
}
|
|
|
|
|
|
|
|
void z_palette_black(void)
|
|
|
|
{
|
|
|
|
for(int col = 0; col < COLOR_COUNT; col++) {
|
2020-03-01 17:48:44 +00:00
|
|
|
z_palette_show_single(col, 0, 0, 0);
|
2020-03-01 11:24:01 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void z_palette_black_in(void)
|
|
|
|
{
|
|
|
|
Palette4 fadepal;
|
|
|
|
memset(&fadepal, 0, sizeof(Palette4));
|
|
|
|
fade_loop(fadepal,
|
|
|
|
if(fadepal[col].v[comp] < z_Palettes[col].v[comp]) {
|
|
|
|
fadepal[col].v[comp]++;
|
|
|
|
}
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
void z_palette_black_out(void)
|
|
|
|
{
|
|
|
|
Palette4 fadepal;
|
|
|
|
memcpy(&fadepal, &z_Palettes, sizeof(Palette4));
|
|
|
|
|
|
|
|
fade_loop(fadepal,
|
|
|
|
if(fadepal[col].v[comp] > fadepal[0].min()) {
|
|
|
|
fadepal[col].v[comp]--;
|
|
|
|
}
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
void z_palette_white(void)
|
|
|
|
{
|
|
|
|
for(int col = 0; col < COLOR_COUNT; col++) {
|
2020-03-01 17:48:44 +00:00
|
|
|
z_palette_show_single(col, RGB4::max(), RGB4::max(), RGB4::max());
|
2020-03-01 11:24:01 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void z_palette_white_in(void)
|
|
|
|
{
|
|
|
|
Palette4 fadepal;
|
|
|
|
memset(&fadepal, fadepal[0].max(), sizeof(Palette4));
|
|
|
|
|
|
|
|
fade_loop(fadepal,
|
|
|
|
if(fadepal[col].v[comp] > z_Palettes[col].v[comp]) {
|
|
|
|
fadepal[col].v[comp]--;
|
|
|
|
}
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
void z_palette_white_out(void)
|
|
|
|
{
|
|
|
|
Palette4 fadepal;
|
|
|
|
memcpy(&fadepal, &z_Palettes, sizeof(Palette4));
|
|
|
|
|
|
|
|
fade_loop(fadepal,
|
|
|
|
if(fadepal[col].v[comp] < fadepal[0].max()) {
|
|
|
|
fadepal[col].v[comp]++;
|
|
|
|
}
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
void z_palette_show(void)
|
|
|
|
{
|
|
|
|
for(int i = 0; i < COLOR_COUNT; i++) {
|
2020-03-01 17:48:44 +00:00
|
|
|
z_palette_show_single_col(i, z_Palettes[i]);
|
2020-03-01 11:24:01 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
/// -------------
|
|
|
|
|
2020-01-12 21:28:38 +00:00
|
|
|
/// Points
|
|
|
|
/// ------
|
|
|
|
#define VRAM_SBYTE(plane, offset) \
|
2020-03-06 23:25:03 +00:00
|
|
|
*reinterpret_cast<sdots8_t *>(MK_FP(SEG_PLANE_##plane, offset))
|
2020-01-12 21:28:38 +00:00
|
|
|
|
2020-08-20 19:59:45 +00:00
|
|
|
void z_grcg_pset(screen_x_t x, vram_y_t y, int col)
|
2020-01-12 21:28:38 +00:00
|
|
|
{
|
2020-03-01 17:48:44 +00:00
|
|
|
grcg_setcolor_rmw(col);
|
2022-06-12 14:14:24 +00:00
|
|
|
VRAM_SBYTE(B, vram_offset_mulshift(x, y)) = (0x80 >> (x & BYTE_MASK));
|
2020-03-01 17:48:44 +00:00
|
|
|
grcg_off_func();
|
2020-01-12 21:28:38 +00:00
|
|
|
}
|
|
|
|
|
2020-08-20 19:59:45 +00:00
|
|
|
int z_graph_readdot(screen_x_t x, vram_y_t y)
|
2020-01-12 21:28:38 +00:00
|
|
|
{
|
|
|
|
int ret;
|
2020-08-21 17:18:28 +00:00
|
|
|
vram_offset_t vram_offset = vram_offset_mulshift(x, y);
|
2022-06-12 14:14:24 +00:00
|
|
|
sdots16_t mask = (0x80 >> (x & BYTE_MASK));
|
2020-01-12 21:28:38 +00:00
|
|
|
|
|
|
|
#define test(plane, vram_offset, mask, bit) \
|
|
|
|
if(VRAM_SBYTE(plane, vram_offset) & mask) { \
|
|
|
|
ret |= bit; \
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = 0;
|
|
|
|
test(B, vram_offset, mask, 1);
|
|
|
|
test(R, vram_offset, mask, 2);
|
|
|
|
test(G, vram_offset, mask, 4);
|
|
|
|
test(E, vram_offset, mask, 8);
|
|
|
|
return ret;
|
|
|
|
|
2021-05-07 19:21:15 +00:00
|
|
|
#undef test
|
2020-01-12 21:28:38 +00:00
|
|
|
}
|
|
|
|
/// ------
|
|
|
|
|
2020-01-11 14:12:23 +00:00
|
|
|
/// Restorable line drawing
|
|
|
|
/// -----------------------
|
2022-08-04 13:29:34 +00:00
|
|
|
|
2020-01-11 14:12:23 +00:00
|
|
|
// `true` copies the pixels to be drawn from the same position on page 1, thus
|
|
|
|
// restoring them with the background image. `false` (the default) draws them
|
2020-06-18 18:01:29 +00:00
|
|
|
// regularly in the given [col].
|
2022-08-10 20:20:27 +00:00
|
|
|
static bool graph_r_unput = false;
|
|
|
|
|
2020-01-11 14:12:23 +00:00
|
|
|
// Not used for purely horizontal lines.
|
2022-08-10 20:20:27 +00:00
|
|
|
static dots16_t graph_r_pattern = 0x80; // 1 pixel (* )
|
2020-01-12 18:13:26 +00:00
|
|
|
|
2020-08-20 19:59:45 +00:00
|
|
|
void graph_r_hline(screen_x_t left, screen_x_t right, vram_y_t y, int col)
|
2020-01-12 18:13:26 +00:00
|
|
|
{
|
2020-08-21 18:13:08 +00:00
|
|
|
vram_byte_amount_t x;
|
|
|
|
vram_byte_amount_t full_bytes_to_put;
|
2020-01-12 18:13:26 +00:00
|
|
|
int order_tmp;
|
2020-03-06 23:25:03 +00:00
|
|
|
dots8_t left_pixels;
|
|
|
|
dots8_t right_pixels;
|
|
|
|
dots8_t *vram_row;
|
2020-01-12 18:13:26 +00:00
|
|
|
|
2022-03-11 23:48:15 +00:00
|
|
|
fix_order(order_tmp, left, right);
|
2020-01-12 18:13:26 +00:00
|
|
|
clip_x(left, right);
|
|
|
|
|
|
|
|
graph_r_last_line_end.x = right;
|
|
|
|
graph_r_last_line_end.y = y;
|
|
|
|
|
2021-01-26 15:33:06 +00:00
|
|
|
vram_row = (dots8_t *)(MK_FP(SEG_PLANE_B, vram_offset_muldiv(left, y)));
|
2020-08-05 21:04:47 +00:00
|
|
|
full_bytes_to_put = (right / BYTE_DOTS) - (left / BYTE_DOTS);
|
2022-06-12 14:14:24 +00:00
|
|
|
left_pixels = 0xFF >> (left & BYTE_MASK);
|
|
|
|
right_pixels = 0xFF << (BYTE_MASK - (right & BYTE_MASK));
|
2020-01-12 18:13:26 +00:00
|
|
|
|
2020-06-06 16:39:55 +00:00
|
|
|
if(!graph_r_unput) {
|
2020-03-01 17:48:44 +00:00
|
|
|
grcg_setcolor_rmw(col);
|
2020-01-12 18:13:26 +00:00
|
|
|
}
|
2020-06-06 16:39:55 +00:00
|
|
|
if(graph_r_unput) {
|
2020-07-04 18:20:51 +00:00
|
|
|
egc_copy_rect_1_to_0_16(left, y, RES_X - left, 1);
|
2020-01-12 18:13:26 +00:00
|
|
|
} else {
|
|
|
|
if(full_bytes_to_put == 0) {
|
|
|
|
vram_row[0] = (left_pixels & right_pixels);
|
|
|
|
} else {
|
|
|
|
vram_row[0] = left_pixels;
|
2020-03-01 17:48:44 +00:00
|
|
|
for(x = 1; x < full_bytes_to_put; x++) {
|
2020-01-12 18:13:26 +00:00
|
|
|
vram_row[x] = 0xFF;
|
|
|
|
}
|
|
|
|
vram_row[full_bytes_to_put] = right_pixels;
|
|
|
|
}
|
|
|
|
}
|
2020-06-06 16:39:55 +00:00
|
|
|
if(!graph_r_unput) {
|
2020-03-01 17:48:44 +00:00
|
|
|
grcg_off_func();
|
2020-01-12 18:13:26 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-08-20 19:59:45 +00:00
|
|
|
void graph_r_vline(screen_x_t x, vram_y_t top, vram_y_t bottom, int col)
|
2020-01-12 18:13:26 +00:00
|
|
|
{
|
2020-08-20 19:59:45 +00:00
|
|
|
vram_y_t y;
|
2020-01-12 18:13:26 +00:00
|
|
|
int order_tmp;
|
2020-03-06 23:25:03 +00:00
|
|
|
dots16_t pattern;
|
2020-08-21 17:18:28 +00:00
|
|
|
vram_offset_t vram_row_offset;
|
2020-01-12 18:13:26 +00:00
|
|
|
|
2022-03-11 23:48:15 +00:00
|
|
|
fix_order(order_tmp, top, bottom);
|
2020-01-12 18:13:26 +00:00
|
|
|
clip_y(top, bottom);
|
|
|
|
|
|
|
|
graph_r_last_line_end.x = x;
|
|
|
|
graph_r_last_line_end.y = bottom;
|
|
|
|
|
2020-06-06 16:39:55 +00:00
|
|
|
if(graph_r_unput) {
|
2020-08-05 21:04:47 +00:00
|
|
|
egc_copy_rect_1_to_0_16(
|
|
|
|
x, top, (sizeof(pattern) * BYTE_DOTS), (bottom - top)
|
|
|
|
);
|
2020-01-12 18:13:26 +00:00
|
|
|
return;
|
|
|
|
}
|
2020-03-15 19:11:55 +00:00
|
|
|
vram_row_offset = vram_offset_shift(x, top);
|
2022-06-12 14:14:24 +00:00
|
|
|
pattern = graph_r_pattern >> (x & BYTE_MASK);
|
|
|
|
pattern |= graph_r_pattern << (16 - (x & BYTE_MASK));
|
2020-01-12 18:13:26 +00:00
|
|
|
|
2020-03-01 17:48:44 +00:00
|
|
|
grcg_setcolor_rmw(col);
|
2020-01-12 18:13:26 +00:00
|
|
|
for(y = top; y <= bottom; y++) {
|
2020-09-25 19:21:56 +00:00
|
|
|
grcg_put(vram_row_offset, pattern, 16);
|
2020-01-12 18:13:26 +00:00
|
|
|
vram_row_offset += ROW_SIZE;
|
|
|
|
}
|
2020-03-01 17:48:44 +00:00
|
|
|
grcg_off_func();
|
2020-01-12 18:13:26 +00:00
|
|
|
}
|
|
|
|
|
2020-08-20 19:59:45 +00:00
|
|
|
void graph_r_line_unput(
|
|
|
|
screen_x_t left, vram_y_t top, screen_x_t right, vram_y_t bottom
|
|
|
|
)
|
2020-01-12 18:13:26 +00:00
|
|
|
{
|
2020-06-06 16:39:55 +00:00
|
|
|
graph_r_unput = true;
|
2021-09-24 09:16:45 +00:00
|
|
|
graph_r_line(left, top, right, bottom, V_WHITE);
|
2020-06-06 16:39:55 +00:00
|
|
|
graph_r_unput = false;
|
2020-01-12 18:13:26 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void graph_r_line_patterned(
|
2020-08-20 19:59:45 +00:00
|
|
|
screen_x_t left,
|
|
|
|
vram_y_t top,
|
|
|
|
screen_x_t right,
|
|
|
|
vram_y_t bottom,
|
|
|
|
int col,
|
|
|
|
dots16_t pattern
|
2020-01-12 18:13:26 +00:00
|
|
|
)
|
|
|
|
{
|
|
|
|
graph_r_pattern = pattern;
|
|
|
|
graph_r_line(left, top, right, bottom, col);
|
|
|
|
graph_r_pattern = 0x80;
|
|
|
|
}
|
|
|
|
|
2020-08-20 19:59:45 +00:00
|
|
|
void graph_r_line(
|
|
|
|
screen_x_t left,
|
|
|
|
vram_y_t top,
|
|
|
|
screen_x_t right,
|
|
|
|
vram_y_t bottom,
|
|
|
|
int col
|
|
|
|
)
|
2020-01-12 18:13:26 +00:00
|
|
|
{
|
2020-08-21 17:18:28 +00:00
|
|
|
register vram_offset_t vram_offset;
|
2020-01-12 18:13:26 +00:00
|
|
|
int i;
|
2020-08-20 19:59:45 +00:00
|
|
|
screen_x_t x_cur;
|
|
|
|
vram_y_t y_cur;
|
2020-08-21 18:13:08 +00:00
|
|
|
pixel_t w, h;
|
2020-01-12 18:13:26 +00:00
|
|
|
int error;
|
|
|
|
int y_direction;
|
|
|
|
int order_tmp;
|
2020-08-20 19:59:45 +00:00
|
|
|
vram_x_t x_vram;
|
|
|
|
vram_y_t y_vram;
|
2020-03-06 23:25:03 +00:00
|
|
|
dots16_t pixels;
|
2020-01-12 18:13:26 +00:00
|
|
|
|
|
|
|
#define lerp(m, x) static_cast<int>(m * static_cast<float>(x))
|
|
|
|
|
2022-05-31 07:05:32 +00:00
|
|
|
#define clip_lerp_min(low, high, lerp_point, slope_dividend, minimum) \
|
2020-01-12 18:13:26 +00:00
|
|
|
if(low < minimum) { \
|
|
|
|
if(high < minimum) { \
|
|
|
|
return; \
|
|
|
|
} \
|
2022-05-31 07:05:32 +00:00
|
|
|
lerp_point += lerp((slope_dividend / (high - low)), (minimum - low)); \
|
2020-01-12 18:13:26 +00:00
|
|
|
low = minimum; \
|
|
|
|
}
|
2022-05-31 07:05:32 +00:00
|
|
|
#define clip_lerp_max(low, high, lerp_point, slope_dividend, maximum) \
|
2020-01-12 18:13:26 +00:00
|
|
|
if(high > maximum) { \
|
|
|
|
if(low > maximum) { \
|
|
|
|
return; \
|
|
|
|
} \
|
2022-05-31 07:05:32 +00:00
|
|
|
lerp_point -= lerp((slope_dividend / (high - low)), (high - maximum)); \
|
2020-01-12 18:13:26 +00:00
|
|
|
high = maximum; \
|
|
|
|
}
|
|
|
|
|
2022-03-11 23:48:15 +00:00
|
|
|
#define unput32_at(vram_offset) { \
|
|
|
|
Planar<dots32_t> page1; \
|
2020-03-06 23:25:03 +00:00
|
|
|
graph_accesspage_func(1); VRAM_SNAP_PLANAR(page1, vram_offset, 32); \
|
2022-03-11 23:48:15 +00:00
|
|
|
graph_accesspage_func(0); VRAM_PUT_PLANAR(vram_offset, page1, 32); \
|
|
|
|
}
|
2020-01-12 18:13:26 +00:00
|
|
|
|
|
|
|
#define plot_loop(\
|
|
|
|
step_var, step_len, step_increment, \
|
|
|
|
plotted_var, plotted_len, plotted_increment \
|
|
|
|
) \
|
|
|
|
error = (step_len >> 1); \
|
|
|
|
for(i = 0; i <= step_len; i++) { \
|
|
|
|
/* Advanced past the VRAM cursor? */ \
|
|
|
|
if((x_cur >> 3) != x_vram || (y_vram != y_cur)) { \
|
|
|
|
vram_offset = (y_vram * ROW_SIZE) + x_vram; \
|
2020-06-06 16:39:55 +00:00
|
|
|
if(!graph_r_unput) { \
|
2020-09-25 19:21:56 +00:00
|
|
|
grcg_put(vram_offset, pixels, 16); \
|
2020-01-12 18:13:26 +00:00
|
|
|
pixels = 0; \
|
|
|
|
} else { \
|
2022-05-31 05:05:43 +00:00
|
|
|
/* ZUN bug: Getting here with a [vram_offset] of 0x0000 will
|
|
|
|
* cause a 4-byte write starting at 0xFFFF. On the 80286 and
|
|
|
|
* later CPUs, offset overflows within an instruction are
|
|
|
|
* illegal even in Real Mode, and will raise a General
|
|
|
|
* Protection Fault.
|
|
|
|
* As of May 2022, Anex86 is the only PC-98 emulator to
|
|
|
|
* correctly replicate this behavior of real hardware,
|
|
|
|
* though. */ \
|
2020-01-12 18:13:26 +00:00
|
|
|
vram_offset--; \
|
2022-03-11 23:48:15 +00:00
|
|
|
unput32_at(vram_offset); \
|
2020-01-12 18:13:26 +00:00
|
|
|
} \
|
|
|
|
y_vram = y_cur; \
|
|
|
|
x_vram = (x_cur >> 3); \
|
|
|
|
} \
|
2022-06-12 14:14:24 +00:00
|
|
|
pixels |= (graph_r_pattern >> (x_cur & BYTE_MASK)); \
|
|
|
|
pixels |= (graph_r_pattern << (16 - (x_cur & BYTE_MASK))); \
|
2020-01-12 18:13:26 +00:00
|
|
|
error -= plotted_len; \
|
|
|
|
step_var += step_increment; \
|
|
|
|
if(error < 0) { \
|
|
|
|
error += step_len; \
|
|
|
|
plotted_var += plotted_increment; \
|
|
|
|
} \
|
|
|
|
} \
|
2020-06-06 16:39:55 +00:00
|
|
|
if(graph_r_unput) { \
|
2020-01-12 18:13:26 +00:00
|
|
|
goto restore_last; \
|
|
|
|
} \
|
|
|
|
goto end;
|
|
|
|
|
2020-06-06 16:39:55 +00:00
|
|
|
if(!graph_r_unput && (left == right)) {
|
2020-01-12 18:13:26 +00:00
|
|
|
graph_r_vline(left, top, bottom, col);
|
|
|
|
return;
|
|
|
|
}
|
2020-06-06 16:39:55 +00:00
|
|
|
if(!graph_r_unput && (top == bottom)) {
|
2020-01-12 18:13:26 +00:00
|
|
|
graph_r_hline(left, right, top, col);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if(left > right) {
|
|
|
|
order_tmp = left;
|
|
|
|
left = right;
|
|
|
|
right = order_tmp;
|
|
|
|
order_tmp = top;
|
|
|
|
top = bottom;
|
|
|
|
bottom = order_tmp;
|
|
|
|
}
|
|
|
|
|
2022-05-31 07:05:32 +00:00
|
|
|
clip_lerp_min(left, right, top, (bottom - top), 0);
|
|
|
|
clip_lerp_max(left, right, bottom, (bottom - top), (RES_X - 1));
|
|
|
|
clip_lerp_min(top, bottom, left, (right - left), 0);
|
|
|
|
clip_lerp_max(top, bottom, right, (right - left), (RES_Y - 1));
|
|
|
|
|
|
|
|
// This division is safe at this point.
|
|
|
|
#define slope_y ((right - left) / (bottom - top))
|
|
|
|
|
2020-01-12 18:13:26 +00:00
|
|
|
if(bottom < 0) {
|
|
|
|
right += lerp(slope_y, 0 - bottom);
|
|
|
|
bottom = 0;
|
|
|
|
}
|
|
|
|
if(top > (RES_Y - 1)) {
|
|
|
|
left -= lerp(slope_y, top - (RES_Y - 1));
|
|
|
|
top = (RES_Y - 1);
|
|
|
|
}
|
|
|
|
graph_r_last_line_end.x = right;
|
|
|
|
graph_r_last_line_end.y = bottom;
|
|
|
|
x_cur = left;
|
|
|
|
y_cur = top;
|
|
|
|
y_direction = (top < bottom) ? 1 : -1;
|
|
|
|
w = right - left;
|
|
|
|
h = (bottom - top) * y_direction;
|
|
|
|
pixels = 0;
|
|
|
|
x_vram = (x_cur >> 3);
|
|
|
|
y_vram = y_cur;
|
|
|
|
|
2020-06-06 16:39:55 +00:00
|
|
|
if(!graph_r_unput) {
|
2020-03-01 17:48:44 +00:00
|
|
|
grcg_setcolor_rmw(col);
|
2020-01-12 18:13:26 +00:00
|
|
|
}
|
|
|
|
if(w > h) {
|
|
|
|
plot_loop(x_cur, w, 1, y_cur, h, y_direction);
|
|
|
|
} else {
|
|
|
|
plot_loop(y_cur, h, y_direction, x_cur, w, 1);
|
|
|
|
}
|
|
|
|
restore_last:
|
2022-05-31 05:05:43 +00:00
|
|
|
// ZUN bug: Off-by-one error, as [x_cur] and [y_cur] are one past the
|
|
|
|
// intended right / bottom coordinates after the plot_loop. Should have
|
|
|
|
// calculated [vram_offset] from [x_vram] and [y_vram] just like the
|
|
|
|
// plot_loop, since those values are directly updated for the next VRAM
|
|
|
|
// byte after a blit, and would thus be correct here as well.
|
|
|
|
//
|
|
|
|
// This way, the offset could potentially end up at [right = 640] or
|
|
|
|
// [bottom = -1]. Both together are not only the same as (0, 0) and thus
|
|
|
|
// wrap from the right edge of VRAM back to the left one, but also trigger
|
|
|
|
// the same General Protection Fault seen in the plot_loop itself.
|
2020-03-15 19:11:55 +00:00
|
|
|
vram_offset = vram_offset_shift(x_cur, y_cur) - 1;
|
2022-03-11 23:48:15 +00:00
|
|
|
unput32_at(vram_offset);
|
2020-01-12 18:13:26 +00:00
|
|
|
end:
|
2020-06-06 16:39:55 +00:00
|
|
|
if(!graph_r_unput) {
|
2020-03-01 17:48:44 +00:00
|
|
|
grcg_off_func();
|
2020-01-12 18:13:26 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#undef plot_loop
|
2022-03-11 23:48:15 +00:00
|
|
|
#undef unput32_at
|
2020-01-12 18:13:26 +00:00
|
|
|
#undef clip_lerp_min
|
|
|
|
#undef clip_lerp_max
|
2022-05-31 07:05:32 +00:00
|
|
|
#undef slope_x
|
2020-01-12 18:13:26 +00:00
|
|
|
}
|
2020-01-11 14:12:23 +00:00
|
|
|
/// -----------------------
|
|
|
|
|
2020-08-20 19:59:45 +00:00
|
|
|
void z_grcg_boxfill(
|
|
|
|
screen_x_t left,
|
|
|
|
vram_y_t top,
|
|
|
|
screen_x_t right,
|
|
|
|
vram_y_t bottom,
|
|
|
|
int col
|
|
|
|
)
|
2020-01-11 21:50:33 +00:00
|
|
|
{
|
2020-08-21 18:13:08 +00:00
|
|
|
vram_byte_amount_t x;
|
2020-08-20 19:59:45 +00:00
|
|
|
vram_y_t y;
|
2020-08-21 18:13:08 +00:00
|
|
|
vram_byte_amount_t full_bytes_to_put;
|
2020-01-11 21:50:33 +00:00
|
|
|
int order_tmp;
|
2020-03-06 23:25:03 +00:00
|
|
|
dots8_t left_pixels;
|
|
|
|
dots8_t right_pixels;
|
|
|
|
dots8_t *vram_row;
|
2020-01-11 21:50:33 +00:00
|
|
|
|
2022-03-11 23:48:15 +00:00
|
|
|
fix_order(order_tmp, left, right);
|
|
|
|
fix_order(order_tmp, top, bottom);
|
2020-01-11 21:50:33 +00:00
|
|
|
clip_x(left, right);
|
|
|
|
clip_y(top, bottom);
|
|
|
|
|
2020-03-01 17:48:44 +00:00
|
|
|
grcg_setcolor_rmw(col);
|
2021-01-26 15:33:06 +00:00
|
|
|
vram_row = (dots8_t *)(MK_FP(SEG_PLANE_B, vram_offset_mulshift(left, top)));
|
2020-01-11 21:50:33 +00:00
|
|
|
for(y = top; y <= bottom; y++) {
|
|
|
|
full_bytes_to_put = (right >> 3) - (left >> 3);
|
2022-06-12 14:14:24 +00:00
|
|
|
left_pixels = 0xFF >> (left & BYTE_MASK);
|
|
|
|
right_pixels = 0xFF << (BYTE_MASK - (right & BYTE_MASK));
|
2020-01-11 21:50:33 +00:00
|
|
|
|
|
|
|
if(full_bytes_to_put == 0) {
|
|
|
|
vram_row[0] = (left_pixels & right_pixels);
|
|
|
|
} else {
|
|
|
|
vram_row[0] = left_pixels;
|
|
|
|
for(x = 1; x < full_bytes_to_put; x++) {
|
|
|
|
vram_row[x] = 0xFF;
|
|
|
|
}
|
|
|
|
vram_row[full_bytes_to_put] = right_pixels;
|
|
|
|
}
|
|
|
|
vram_row += ROW_SIZE;
|
|
|
|
}
|
2020-03-01 17:48:44 +00:00
|
|
|
grcg_off_func();
|
2020-01-11 21:50:33 +00:00
|
|
|
}
|
|
|
|
|
2020-08-20 19:59:45 +00:00
|
|
|
void graph_r_box(
|
|
|
|
screen_x_t left,
|
|
|
|
vram_y_t top,
|
|
|
|
screen_x_t right,
|
|
|
|
vram_y_t bottom,
|
|
|
|
int col
|
|
|
|
)
|
2020-01-11 21:50:33 +00:00
|
|
|
{
|
2020-01-12 18:13:26 +00:00
|
|
|
graph_r_hline(left, right, top, col);
|
|
|
|
graph_r_vline(left, top, bottom, col);
|
|
|
|
graph_r_vline(right, top, bottom, col);
|
|
|
|
graph_r_hline(left, right, bottom, col);
|
2020-01-11 21:50:33 +00:00
|
|
|
}
|
|
|
|
|
2020-10-26 13:30:35 +00:00
|
|
|
inline int fx_weight_from(int col_and_fx) {
|
|
|
|
return ((col_and_fx / 0x10) % 4);
|
|
|
|
}
|
|
|
|
|
|
|
|
inline pixel_t fx_spacing_from(int col_and_fx) {
|
|
|
|
return ((col_and_fx / 0x40) % 8);
|
|
|
|
}
|
|
|
|
|
2022-08-09 01:24:33 +00:00
|
|
|
pixel_t text_extent_fx(int col_and_fx, const shiftjis_t *str)
|
2020-01-11 20:06:26 +00:00
|
|
|
{
|
2020-08-21 18:13:08 +00:00
|
|
|
register pixel_t ret = 0;
|
2020-10-26 13:30:35 +00:00
|
|
|
register pixel_t spacing = fx_spacing_from(col_and_fx);
|
2020-01-11 20:06:26 +00:00
|
|
|
while(*str) {
|
|
|
|
if(_ismbblead(str[0])) {
|
2022-08-09 01:24:33 +00:00
|
|
|
shiftjis_kanji_t codepoint = ((char)str[0] << 8) + str[0];
|
2020-01-11 20:06:26 +00:00
|
|
|
str++;
|
|
|
|
str++;
|
|
|
|
if(codepoint < 0x8540) {
|
|
|
|
ret += GLYPH_FULL_W;
|
|
|
|
} else if(codepoint > 0x869E) {
|
|
|
|
ret += GLYPH_FULL_W;
|
|
|
|
} else {
|
|
|
|
ret += GLYPH_HALF_W;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
ret += GLYPH_HALF_W;
|
|
|
|
str++;
|
|
|
|
}
|
|
|
|
ret += spacing;
|
|
|
|
}
|
|
|
|
return ret - spacing;
|
|
|
|
}
|
|
|
|
|
2020-06-16 10:43:39 +00:00
|
|
|
#include "th01/hardware/grppsafx.cpp"
|
2020-01-11 20:06:26 +00:00
|
|
|
|
2020-08-20 19:59:45 +00:00
|
|
|
void graph_putsa_fx(
|
2022-08-09 01:24:33 +00:00
|
|
|
screen_x_t left, vram_y_t top, int16_t col_and_fx, const shiftjis_t *str
|
2020-08-20 19:59:45 +00:00
|
|
|
)
|
2020-01-11 20:06:26 +00:00
|
|
|
{
|
2020-08-20 19:59:45 +00:00
|
|
|
register screen_x_t x = left;
|
2022-08-09 01:24:33 +00:00
|
|
|
jis_t codepoint;
|
2021-04-17 18:08:30 +00:00
|
|
|
dots_t(GLYPH_FULL_W) glyph_row;
|
2021-02-04 19:16:03 +00:00
|
|
|
dots8_t far *vram;
|
2020-01-11 20:06:26 +00:00
|
|
|
int fullwidth;
|
|
|
|
int first_bit;
|
2020-10-26 13:30:35 +00:00
|
|
|
int weight = fx_weight_from(col_and_fx);
|
|
|
|
pixel_t spacing = fx_spacing_from(col_and_fx);
|
|
|
|
int clear_bg = (col_and_fx & FX_CLEAR_BG);
|
|
|
|
int underline = (col_and_fx & FX_UNDERLINE);
|
|
|
|
int reverse = (col_and_fx & FX_REVERSE);
|
2020-08-21 18:13:08 +00:00
|
|
|
pixel_t w;
|
|
|
|
pixel_t line;
|
2021-04-17 18:08:30 +00:00
|
|
|
dot_rect_t(GLYPH_FULL_W, GLYPH_H) glyph;
|
|
|
|
register dots_t(GLYPH_FULL_W) glyph_row_tmp;
|
2020-01-11 20:06:26 +00:00
|
|
|
|
|
|
|
if(clear_bg) {
|
2020-10-26 13:30:35 +00:00
|
|
|
w = text_extent_fx(col_and_fx, str);
|
2020-01-11 20:06:26 +00:00
|
|
|
if(underline) {
|
2020-03-01 17:48:44 +00:00
|
|
|
z_grcg_boxfill(x, top, (x + w - 1), (top + GLYPH_H + 1), 0);
|
2021-09-24 09:16:45 +00:00
|
|
|
graph_r_hline(x, (x + w - 1), (top + GLYPH_H + 1), V_WHITE);
|
2020-01-11 20:06:26 +00:00
|
|
|
} else {
|
2020-03-01 17:48:44 +00:00
|
|
|
z_grcg_boxfill(x, top, (x + w - 1), (top + GLYPH_H - 1), 0);
|
2020-01-11 20:06:26 +00:00
|
|
|
}
|
|
|
|
} else if(underline) {
|
2020-10-26 13:30:35 +00:00
|
|
|
w = text_extent_fx(col_and_fx, str);
|
2021-09-24 09:16:45 +00:00
|
|
|
graph_r_hline(x, (x + w - 1), (top + GLYPH_H + 1), V_WHITE);
|
2020-01-11 20:06:26 +00:00
|
|
|
}
|
|
|
|
|
2020-10-26 13:30:35 +00:00
|
|
|
grcg_setcolor_rmw(col_and_fx);
|
2020-11-06 21:24:00 +00:00
|
|
|
outportb(0x68, 0xB); // CG ROM dot access
|
2020-01-11 20:06:26 +00:00
|
|
|
|
|
|
|
while(str[0]) {
|
2020-03-01 17:48:44 +00:00
|
|
|
set_vram_ptr(vram, first_bit, x, top);
|
|
|
|
get_glyph(glyph, codepoint, fullwidth, str, x, line);
|
2020-01-11 20:06:26 +00:00
|
|
|
|
|
|
|
for(line = 0; line < GLYPH_H; line++) {
|
|
|
|
apply_weight(glyph_row, glyph[line], glyph_row_tmp, weight);
|
|
|
|
if(reverse) {
|
|
|
|
if(fullwidth) {
|
|
|
|
glyph_row ^= 0xFFFF;
|
|
|
|
} else {
|
|
|
|
glyph_row ^= 0xFF00;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
put_row_and_advance(vram, glyph_row, first_bit);
|
|
|
|
}
|
2020-03-01 17:48:44 +00:00
|
|
|
advance_left(x, fullwidth, spacing);
|
2020-01-11 20:06:26 +00:00
|
|
|
}
|
|
|
|
|
2020-11-06 21:24:00 +00:00
|
|
|
outportb(0x68, 0xA); // CG ROM code access
|
2020-03-01 17:48:44 +00:00
|
|
|
grcg_off_func();
|
2020-01-11 20:06:26 +00:00
|
|
|
}
|
|
|
|
|
2021-11-07 17:26:03 +00:00
|
|
|
void graph_copy_byterect_from_accessed_page_to_other(
|
2020-08-20 19:59:45 +00:00
|
|
|
screen_x_t left, vram_y_t top, screen_x_t right, vram_y_t bottom
|
2020-01-10 20:25:29 +00:00
|
|
|
)
|
|
|
|
{
|
2020-08-21 18:13:08 +00:00
|
|
|
pixel_t w = (right - left) / BYTE_DOTS;
|
|
|
|
pixel_t h = (bottom - top);
|
2020-01-10 20:25:29 +00:00
|
|
|
Planes_declare(p);
|
2021-11-07 17:26:03 +00:00
|
|
|
page_t page_front = page_accessed ^ 1;
|
2020-08-21 18:13:08 +00:00
|
|
|
pixel_t row;
|
2020-01-10 20:25:29 +00:00
|
|
|
PlanarRow_declare(tmp);
|
|
|
|
|
|
|
|
Planes_offset(p, left, top);
|
|
|
|
for(row = 0; row < h; row++) {
|
|
|
|
PlanarRow_blit(tmp, p, w);
|
|
|
|
graph_accesspage(page_front);
|
|
|
|
PlanarRow_blit(p, tmp, w);
|
2021-11-07 17:26:03 +00:00
|
|
|
graph_accesspage(page_accessed);
|
2020-01-10 20:25:29 +00:00
|
|
|
Planes_next_row(p);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void graph_move_byterect_interpage(
|
2020-08-20 19:59:45 +00:00
|
|
|
screen_x_t src_left,
|
|
|
|
vram_y_t src_top,
|
|
|
|
screen_x_t src_right,
|
|
|
|
vram_y_t src_bottom,
|
|
|
|
screen_x_t dst_left,
|
|
|
|
vram_y_t dst_top,
|
2020-05-21 11:06:41 +00:00
|
|
|
int src, int dst
|
2020-01-10 20:25:29 +00:00
|
|
|
)
|
|
|
|
{
|
2020-08-21 18:13:08 +00:00
|
|
|
pixel_t w = (src_right - src_left) / BYTE_DOTS;
|
|
|
|
pixel_t h = (src_bottom - src_top);
|
2020-01-10 20:25:29 +00:00
|
|
|
Planes_declare(src);
|
|
|
|
Planes_declare(dst);
|
2020-08-21 18:13:08 +00:00
|
|
|
pixel_t row;
|
2020-01-10 20:25:29 +00:00
|
|
|
PlanarRow_declare(tmp);
|
|
|
|
|
|
|
|
Planes_offset(src, src_left, src_top);
|
|
|
|
Planes_offset(dst, dst_left, dst_top);
|
|
|
|
for(row = 0; row < h; row++) {
|
|
|
|
PlanarRow_blit(tmp, src, w);
|
|
|
|
graph_accesspage(dst);
|
|
|
|
PlanarRow_blit(dst, tmp, w);
|
|
|
|
graph_accesspage(src);
|
|
|
|
Planes_next_row(src);
|
|
|
|
Planes_next_row(dst);
|
|
|
|
}
|
2021-11-07 17:26:03 +00:00
|
|
|
graph_accesspage(page_accessed);
|
2020-01-10 20:25:29 +00:00
|
|
|
}
|
|
|
|
|
2020-01-06 11:07:53 +00:00
|
|
|
void z_palette_fade_from(
|
|
|
|
uint4_t from_r, uint4_t from_g, uint4_t from_b,
|
|
|
|
int keep[COLOR_COUNT],
|
|
|
|
unsigned int step_ms
|
|
|
|
)
|
|
|
|
{
|
2020-03-01 11:24:18 +00:00
|
|
|
Palette4 fadepal;
|
2020-01-06 11:07:53 +00:00
|
|
|
int i;
|
|
|
|
int col;
|
|
|
|
int comp;
|
|
|
|
|
|
|
|
memset(&fadepal, 0x0, sizeof(fadepal));
|
|
|
|
for(i = 0; i < COLOR_COUNT; i++) {
|
|
|
|
if(!keep[i]) {
|
|
|
|
fadepal[i].c.r = from_r;
|
|
|
|
fadepal[i].c.g = from_g;
|
|
|
|
fadepal[i].c.b = from_b;
|
|
|
|
} else {
|
2020-03-03 10:45:03 +00:00
|
|
|
fadepal[i].c.r = z_Palettes[i].c.r;
|
|
|
|
fadepal[i].c.g = z_Palettes[i].c.g;
|
|
|
|
fadepal[i].c.b = z_Palettes[i].c.b;
|
2020-01-06 11:07:53 +00:00
|
|
|
}
|
|
|
|
}
|
2020-03-01 11:24:18 +00:00
|
|
|
for(i = 0; i < fadepal.range(); i++) {
|
2020-01-06 11:07:53 +00:00
|
|
|
z_vsync_wait();
|
|
|
|
for(col = 0; col < COLOR_COUNT; col++) {
|
2021-05-16 20:40:36 +00:00
|
|
|
for(comp = 0; comp < COMPONENT_COUNT; comp++) {
|
2020-03-03 10:45:03 +00:00
|
|
|
if(fadepal[col].v[comp] != z_Palettes[col].v[comp]) {
|
2020-03-01 11:24:18 +00:00
|
|
|
fadepal.colors[col].v[comp] +=
|
2020-03-03 10:45:03 +00:00
|
|
|
(fadepal[col].v[comp] < z_Palettes[col].v[comp])
|
2020-01-06 11:07:53 +00:00
|
|
|
? 1
|
|
|
|
: -1;
|
|
|
|
}
|
|
|
|
}
|
2020-03-01 17:48:44 +00:00
|
|
|
z_palette_show_single_col(col, fadepal[col]);
|
2020-01-06 11:07:53 +00:00
|
|
|
}
|
|
|
|
delay(step_ms);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
[Decompilation] [th01] master.lib resident palette function reimplementations
Which store colors as GRB, as suggested by the structure's ID string.
Even master.lib's own functions add an additional XCHG AH, AL
instruction to get colors into and out of this format. MASTER.MAN
suggests that it's some sort of standard on PC-98. It does match the
order of ths hardware's palette register ports, after all.
(0AAh = green, 0ACh = red, 0AEh = blue)
Now we also know why __seg* wasn't used more commonly, as lamented in
c8e8e98. Turbo C++ simply doesn't support a lot of arithmetic on
segment pointers.
And then that undecompilable far call to a function within the same
segment, but inside a different translation unit…
Also, thanks again to Egor for the SCOPY@ hack that debuted in 0460072.
Would have probably struggled with this a lot more without that.
And *then* you realize that TH01 effectively doesn't even use the
resident palette. 😐
And yes, we're procrastinating the whole issue of potentially using
a single translation unit for all three binaries by using a common
segment name, because it *really* isn't that easy.
Completes P0066, funded by Keyblade Wiedling Neko and Splashman.
2020-01-05 15:10:00 +00:00
|
|
|
// Resident palette
|
|
|
|
// ----------------
|
2022-08-10 20:20:27 +00:00
|
|
|
|
[Decompilation] [th01] master.lib resident palette function reimplementations
Which store colors as GRB, as suggested by the structure's ID string.
Even master.lib's own functions add an additional XCHG AH, AL
instruction to get colors into and out of this format. MASTER.MAN
suggests that it's some sort of standard on PC-98. It does match the
order of ths hardware's palette register ports, after all.
(0AAh = green, 0ACh = red, 0AEh = blue)
Now we also know why __seg* wasn't used more commonly, as lamented in
c8e8e98. Turbo C++ simply doesn't support a lot of arithmetic on
segment pointers.
And then that undecompilable far call to a function within the same
segment, but inside a different translation unit…
Also, thanks again to Egor for the SCOPY@ hack that debuted in 0460072.
Would have probably struggled with this a lot more without that.
And *then* you realize that TH01 effectively doesn't even use the
resident palette. 😐
And yes, we're procrastinating the whole issue of potentially using
a single translation unit for all three binaries by using a common
segment name, because it *really* isn't that easy.
Completes P0066, funded by Keyblade Wiedling Neko and Splashman.
2020-01-05 15:10:00 +00:00
|
|
|
#define RESPAL_ID "pal98 grb"
|
|
|
|
|
|
|
|
// MASTER.MAN suggests that GBR ordering is some sort of standard on PC-98.
|
|
|
|
// It does match the order of the hardware's palette register ports, after
|
|
|
|
// all. (0AAh = green, 0ACh = red, 0AEh = blue)
|
|
|
|
struct grb_t {
|
|
|
|
uint4_t g, r, b;
|
|
|
|
};
|
|
|
|
|
|
|
|
struct respal_t {
|
|
|
|
char id[sizeof(RESPAL_ID)];
|
|
|
|
unsigned char tone;
|
|
|
|
int8_t padding[5];
|
|
|
|
grb_t pal[COLOR_COUNT];
|
|
|
|
};
|
|
|
|
// ----------------
|
|
|
|
|
|
|
|
// Memory Control Block
|
|
|
|
// Adapted from FreeDOS' kernel/hdr/mcb.h
|
|
|
|
// --------------------
|
2022-08-04 13:29:34 +00:00
|
|
|
|
[Decompilation] [th01] master.lib resident palette function reimplementations
Which store colors as GRB, as suggested by the structure's ID string.
Even master.lib's own functions add an additional XCHG AH, AL
instruction to get colors into and out of this format. MASTER.MAN
suggests that it's some sort of standard on PC-98. It does match the
order of ths hardware's palette register ports, after all.
(0AAh = green, 0ACh = red, 0AEh = blue)
Now we also know why __seg* wasn't used more commonly, as lamented in
c8e8e98. Turbo C++ simply doesn't support a lot of arithmetic on
segment pointers.
And then that undecompilable far call to a function within the same
segment, but inside a different translation unit…
Also, thanks again to Egor for the SCOPY@ hack that debuted in 0460072.
Would have probably struggled with this a lot more without that.
And *then* you realize that TH01 effectively doesn't even use the
resident palette. 😐
And yes, we're procrastinating the whole issue of potentially using
a single translation unit for all three binaries by using a common
segment name, because it *really* isn't that easy.
Completes P0066, funded by Keyblade Wiedling Neko and Splashman.
2020-01-05 15:10:00 +00:00
|
|
|
#define MCB_NORMAL 0x4d
|
|
|
|
#define MCB_LAST 0x5a
|
|
|
|
|
|
|
|
struct mcb_t {
|
|
|
|
uint8_t m_type; // mcb type - chain or end
|
|
|
|
uint16_t __seg* m_psp; // owner id via psp segment
|
|
|
|
uint16_t m_size; // size of segment in paragraphs
|
|
|
|
uint8_t m_fill[3];
|
|
|
|
uint8_t m_name[8];
|
|
|
|
};
|
2022-03-12 18:28:11 +00:00
|
|
|
static const uint16_t MCB_PARAS = (sizeof(mcb_t) / 16);
|
[Decompilation] [th01] master.lib resident palette function reimplementations
Which store colors as GRB, as suggested by the structure's ID string.
Even master.lib's own functions add an additional XCHG AH, AL
instruction to get colors into and out of this format. MASTER.MAN
suggests that it's some sort of standard on PC-98. It does match the
order of ths hardware's palette register ports, after all.
(0AAh = green, 0ACh = red, 0AEh = blue)
Now we also know why __seg* wasn't used more commonly, as lamented in
c8e8e98. Turbo C++ simply doesn't support a lot of arithmetic on
segment pointers.
And then that undecompilable far call to a function within the same
segment, but inside a different translation unit…
Also, thanks again to Egor for the SCOPY@ hack that debuted in 0460072.
Would have probably struggled with this a lot more without that.
And *then* you realize that TH01 effectively doesn't even use the
resident palette. 😐
And yes, we're procrastinating the whole issue of potentially using
a single translation unit for all three binaries by using a common
segment name, because it *really* isn't that easy.
Completes P0066, funded by Keyblade Wiedling Neko and Splashman.
2020-01-05 15:10:00 +00:00
|
|
|
|
|
|
|
respal_t __seg* z_respal_exist(void)
|
|
|
|
{
|
|
|
|
union REGS regs;
|
|
|
|
struct SREGS sregs;
|
2022-08-10 20:20:27 +00:00
|
|
|
const char ID[] = RESPAL_ID;
|
[Decompilation] [th01] master.lib resident palette function reimplementations
Which store colors as GRB, as suggested by the structure's ID string.
Even master.lib's own functions add an additional XCHG AH, AL
instruction to get colors into and out of this format. MASTER.MAN
suggests that it's some sort of standard on PC-98. It does match the
order of ths hardware's palette register ports, after all.
(0AAh = green, 0ACh = red, 0AEh = blue)
Now we also know why __seg* wasn't used more commonly, as lamented in
c8e8e98. Turbo C++ simply doesn't support a lot of arithmetic on
segment pointers.
And then that undecompilable far call to a function within the same
segment, but inside a different translation unit…
Also, thanks again to Egor for the SCOPY@ hack that debuted in 0460072.
Would have probably struggled with this a lot more without that.
And *then* you realize that TH01 effectively doesn't even use the
resident palette. 😐
And yes, we're procrastinating the whole issue of potentially using
a single translation unit for all three binaries by using a common
segment name, because it *really* isn't that easy.
Completes P0066, funded by Keyblade Wiedling Neko and Splashman.
2020-01-05 15:10:00 +00:00
|
|
|
seg_t mcb;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
#define MCB reinterpret_cast<mcb_t __seg *>(mcb) /* For easy derefencing */
|
|
|
|
|
|
|
|
// "Get list of lists"
|
|
|
|
segread(&sregs);
|
|
|
|
regs.h.ah = 0x52;
|
|
|
|
intdosx(®s, ®s, &sregs);
|
|
|
|
|
|
|
|
mcb = *reinterpret_cast<seg_t *>(MK_FP(sregs.es, regs.w.bx - 2));
|
|
|
|
while(1) {
|
|
|
|
if(MCB->m_psp != 0) {
|
|
|
|
for(i = 0; i < sizeof(ID); i++) {
|
2022-08-10 20:20:27 +00:00
|
|
|
if(reinterpret_cast<respal_t *>(MCB + 1)->id[i] != ID[i]) {
|
[Decompilation] [th01] master.lib resident palette function reimplementations
Which store colors as GRB, as suggested by the structure's ID string.
Even master.lib's own functions add an additional XCHG AH, AL
instruction to get colors into and out of this format. MASTER.MAN
suggests that it's some sort of standard on PC-98. It does match the
order of ths hardware's palette register ports, after all.
(0AAh = green, 0ACh = red, 0AEh = blue)
Now we also know why __seg* wasn't used more commonly, as lamented in
c8e8e98. Turbo C++ simply doesn't support a lot of arithmetic on
segment pointers.
And then that undecompilable far call to a function within the same
segment, but inside a different translation unit…
Also, thanks again to Egor for the SCOPY@ hack that debuted in 0460072.
Would have probably struggled with this a lot more without that.
And *then* you realize that TH01 effectively doesn't even use the
resident palette. 😐
And yes, we're procrastinating the whole issue of potentially using
a single translation unit for all three binaries by using a common
segment name, because it *really* isn't that easy.
Completes P0066, funded by Keyblade Wiedling Neko and Splashman.
2020-01-05 15:10:00 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if(i == sizeof(ID)) {
|
|
|
|
return reinterpret_cast<respal_t __seg *>(mcb + MCB_PARAS);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if(MCB->m_type != MCB_NORMAL) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
mcb += MCB_PARAS + MCB->m_size;
|
|
|
|
};
|
|
|
|
|
|
|
|
#undef MCB
|
|
|
|
}
|
|
|
|
|
|
|
|
int z_respal_get_show(void)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
respal_t __seg *respal_seg = z_respal_exist();
|
|
|
|
if(respal_seg) {
|
|
|
|
grb_t *respal = respal_seg->pal;
|
|
|
|
for(i = 0; i < COLOR_COUNT; i++) {
|
2020-03-01 17:48:44 +00:00
|
|
|
z_palette_set_show(i, respal->r, respal->g, respal->b);
|
[Decompilation] [th01] master.lib resident palette function reimplementations
Which store colors as GRB, as suggested by the structure's ID string.
Even master.lib's own functions add an additional XCHG AH, AL
instruction to get colors into and out of this format. MASTER.MAN
suggests that it's some sort of standard on PC-98. It does match the
order of ths hardware's palette register ports, after all.
(0AAh = green, 0ACh = red, 0AEh = blue)
Now we also know why __seg* wasn't used more commonly, as lamented in
c8e8e98. Turbo C++ simply doesn't support a lot of arithmetic on
segment pointers.
And then that undecompilable far call to a function within the same
segment, but inside a different translation unit…
Also, thanks again to Egor for the SCOPY@ hack that debuted in 0460072.
Would have probably struggled with this a lot more without that.
And *then* you realize that TH01 effectively doesn't even use the
resident palette. 😐
And yes, we're procrastinating the whole issue of potentially using
a single translation unit for all three binaries by using a common
segment name, because it *really* isn't that easy.
Completes P0066, funded by Keyblade Wiedling Neko and Splashman.
2020-01-05 15:10:00 +00:00
|
|
|
respal++;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
int z_respal_set(void)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
respal_t __seg *respal_seg = z_respal_exist();
|
|
|
|
if(respal_seg) {
|
|
|
|
grb_t *respal = respal_seg->pal;
|
|
|
|
for(i = 0; i < COLOR_COUNT; i++) {
|
2020-03-03 10:45:03 +00:00
|
|
|
respal->g = z_Palettes[i].c.g;
|
|
|
|
respal->r = z_Palettes[i].c.r;
|
|
|
|
respal->b = z_Palettes[i].c.b;
|
[Decompilation] [th01] master.lib resident palette function reimplementations
Which store colors as GRB, as suggested by the structure's ID string.
Even master.lib's own functions add an additional XCHG AH, AL
instruction to get colors into and out of this format. MASTER.MAN
suggests that it's some sort of standard on PC-98. It does match the
order of ths hardware's palette register ports, after all.
(0AAh = green, 0ACh = red, 0AEh = blue)
Now we also know why __seg* wasn't used more commonly, as lamented in
c8e8e98. Turbo C++ simply doesn't support a lot of arithmetic on
segment pointers.
And then that undecompilable far call to a function within the same
segment, but inside a different translation unit…
Also, thanks again to Egor for the SCOPY@ hack that debuted in 0460072.
Would have probably struggled with this a lot more without that.
And *then* you realize that TH01 effectively doesn't even use the
resident palette. 😐
And yes, we're procrastinating the whole issue of potentially using
a single translation unit for all three binaries by using a common
segment name, because it *really* isn't that easy.
Completes P0066, funded by Keyblade Wiedling Neko and Splashman.
2020-01-05 15:10:00 +00:00
|
|
|
respal++;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
return 1;
|
|
|
|
}
|