2015-02-24 15:17:50 +00:00
|
|
|
/* ReC98
|
|
|
|
* -----
|
2015-03-03 05:47:23 +00:00
|
|
|
* Main include file
|
2015-02-24 15:17:50 +00:00
|
|
|
*/
|
|
|
|
|
2020-03-13 00:42:40 +00:00
|
|
|
#ifndef REC98_H
|
|
|
|
#define REC98_H
|
|
|
|
|
2015-03-03 05:47:23 +00:00
|
|
|
#include <master.h>
|
2015-03-16 21:35:52 +00:00
|
|
|
#include <stddef.h>
|
2019-12-11 20:42:27 +00:00
|
|
|
#include "platform.h"
|
2019-11-08 20:03:03 +00:00
|
|
|
#include "pc98.h"
|
2015-03-16 21:35:52 +00:00
|
|
|
|
|
|
|
// master.lib extensions
|
|
|
|
// ---------------------
|
|
|
|
#define palette_entry_rgb_show(fn) \
|
|
|
|
palette_entry_rgb(fn); \
|
|
|
|
palette_show();
|
2019-11-26 21:47:07 +00:00
|
|
|
|
[Maintenance] Templatize RGB and palette types for 4- and 8-bit components
Right, PC-98 hardware only supports 4 bits per RGB component, for a
total of 4,096 possible colors. The 8-bit RGB color values we've been
seeing throughout the later games are a master.lib extension, to allow
for more toning precision. Which TH01, with all its NIH syndrome,
doesn't use.
And yup, that means templates in the most basic header files… Since
that would have meant renaming *everything* to compile as C++, I simply
made these types exclusive to C++ code, thcrap style.
Part of P0066, funded by Keyblade Wiedling Neko and Splashman.
2020-01-05 11:05:42 +00:00
|
|
|
#ifdef __cplusplus
|
|
|
|
// master.lib palettes use twice the bits per RGB component for more
|
|
|
|
// toning precision
|
2020-03-01 11:24:18 +00:00
|
|
|
typedef RGB<uint8_t, 256> RGB8;
|
[Maintenance] Templatize RGB and palette types for 4- and 8-bit components
Right, PC-98 hardware only supports 4 bits per RGB component, for a
total of 4,096 possible colors. The 8-bit RGB color values we've been
seeing throughout the later games are a master.lib extension, to allow
for more toning precision. Which TH01, with all its NIH syndrome,
doesn't use.
And yup, that means templates in the most basic header files… Since
that would have meant renaming *everything* to compile as C++, I simply
made these types exclusive to C++ code, thcrap style.
Part of P0066, funded by Keyblade Wiedling Neko and Splashman.
2020-01-05 11:05:42 +00:00
|
|
|
typedef Palette<RGB8> Palette8;
|
|
|
|
#endif
|
2015-03-16 21:35:52 +00:00
|
|
|
// ---------------------
|
2015-03-03 05:47:23 +00:00
|
|
|
|
2015-03-01 21:52:25 +00:00
|
|
|
// Macros
|
|
|
|
// ------
|
|
|
|
#define CLAMP_INC(val, max) \
|
|
|
|
(val)++; \
|
|
|
|
if((val) > (max)) { \
|
|
|
|
(val) = (max); \
|
|
|
|
}
|
|
|
|
|
|
|
|
#define CLAMP_DEC(val, min) \
|
|
|
|
(val)--; \
|
|
|
|
if((val) < (min)) { \
|
|
|
|
(val) = (min); \
|
|
|
|
}
|
|
|
|
|
|
|
|
#define RING_INC(val, ring_end) \
|
|
|
|
(val)++; \
|
|
|
|
if((val) > (ring_end)) { \
|
|
|
|
(val) = 0; \
|
|
|
|
}
|
|
|
|
|
|
|
|
#define RING_DEC(val, ring_end) \
|
|
|
|
(val)--; \
|
|
|
|
if((val) < 0) { \
|
|
|
|
(val) = ring_end; \
|
|
|
|
}
|
2015-03-07 16:35:30 +00:00
|
|
|
|
|
|
|
// Resident structure
|
|
|
|
#define RES_ID_LEN sizeof(RES_ID)
|
|
|
|
#define RES_ID_STRLEN (RES_ID_LEN - 1)
|
|
|
|
#define RES_PARASIZE ((sizeof(resident_t) + 0xF) >> 4)
|
2015-03-01 21:52:25 +00:00
|
|
|
// ------
|
|
|
|
|
2019-11-30 14:48:36 +00:00
|
|
|
typedef union {
|
|
|
|
struct {
|
2019-12-11 20:42:27 +00:00
|
|
|
int8_t lo, hi;
|
2019-11-30 14:48:36 +00:00
|
|
|
} byte;
|
2019-12-11 20:42:27 +00:00
|
|
|
int16_t v;
|
2019-11-30 14:48:36 +00:00
|
|
|
} twobyte_t;
|
|
|
|
|
2019-09-15 14:09:08 +00:00
|
|
|
/// Typedefs
|
|
|
|
/// --------
|
|
|
|
// Generic callback function types. Note the difference between function
|
|
|
|
// distance (nearfunc / farfunc) and pointer variable distance
|
|
|
|
// (t_near / t_far).
|
|
|
|
typedef void (near pascal *near nearfunc_t_near)(void);
|
|
|
|
typedef void ( far pascal *near farfunc_t_near)(void);
|
|
|
|
typedef void (near pascal * far nearfunc_t_far)(void);
|
|
|
|
typedef void ( far pascal * far farfunc_t_far)(void);
|
|
|
|
/// --------
|
|
|
|
|
2015-03-01 21:52:25 +00:00
|
|
|
// PC-98 VRAM planes
|
|
|
|
// -----------------
|
2020-03-06 23:25:03 +00:00
|
|
|
// 1bpp types, describing horizontal lines of 8, 16, or 32 pixels.
|
|
|
|
typedef uint8_t dots8_t;
|
|
|
|
typedef uint16_t dots16_t;
|
|
|
|
typedef uint32_t dots32_t;
|
2019-12-12 14:01:13 +00:00
|
|
|
// ... and the same for the rare cases where ZUN's code used signed types.
|
2020-03-06 23:25:03 +00:00
|
|
|
typedef int8_t sdots8_t;
|
|
|
|
typedef int16_t sdots16_t;
|
|
|
|
typedef int32_t sdots32_t;
|
2019-12-12 14:01:13 +00:00
|
|
|
|
[Maintenance] Decide how to handle pre-shifted sprites in C land
Ideally, the future sprite compiler should automatically pre-shift such
sprites, and correctly place the shifted variants in memory, by merely
parsing the C header. On disk, you'd then only have a .BMP with each
individual cel at x=0.
And that's why we need macros and consistent naming: To express these
semantics, without having to duplicate the sprite declaration in some
other format. sSPARKS[8][8][8] wouldn't help anyone 😛
Now, we could go even further there by defining a separate type
(`preshifted_dots8_t`), and maybe get rid of the _W macro by replacing
it with a method on that type. However,
• that would be inconsistent, since we'll need the _H macro anyway, for
both the actual rendering code and the sprite compiler
• we couldn't directly call such a method on a 2D or 3D array, and have
to go down to a single element to do so (`sSPARKS[0][0][0].w()`)
• making it a static method instead duplicates the type all over the
code
• and any variables of that type would no longer be scalar-type values
that can be stored in registers, requiring weird workarounds in those
places. As we've already seen with subpixels.
Part of P0085, funded by -Tom-.
2020-03-25 14:22:51 +00:00
|
|
|
// Defines a hardcoded 1bpp sprite, pre-shifted to all 8 start X positions
|
|
|
|
// within a single VRAM byte.
|
|
|
|
#define PRESHIFT 8
|
|
|
|
|
2015-02-24 15:17:50 +00:00
|
|
|
typedef enum {
|
|
|
|
PL_B, PL_R, PL_G, PL_E, PL_COUNT
|
|
|
|
} vram_plane_t;
|
|
|
|
|
[C decompilation] [th01/reiiden] Randomly shaped VRAM copy functions, #1
So apparently, TH01 isn't double-buffered in the usual sense, and instead uses
the second hardware framebuffer (page 1) exclusively to keep the background
image and any non-animated sprites, including the cards. Then, in order to
limit flickering when animating the bullet, character and boss sprites on top
of that (or just to the limit number of VRAM accesses, who knows), ZUN goes to
great lengths and tries to make sure to only copy back the pixels that were
modified on plane 0 in the last frame.
(Which doesn't work that well though. When you play the game, you still notice
tons of flickering whenever sprites overlap.)
And by "great lengths", I mean "having a separate counterpart function for
each shape and sprite animated which recalculates and copies back the same
pixels from plane 1 to plane 0", because that's what the new functions here
lead me to believe. Both of them are only called at one place: the wave
function on the second half of Elis' entrance animation, and the horizontal
masked line function for Reimu's X attack animations.
2015-03-10 16:39:00 +00:00
|
|
|
typedef struct {
|
2020-03-06 23:25:03 +00:00
|
|
|
dots8_t B, R, G, E;
|
|
|
|
} planar8_t;
|
[C decompilation] [th01/reiiden] Randomly shaped VRAM copy functions, #1
So apparently, TH01 isn't double-buffered in the usual sense, and instead uses
the second hardware framebuffer (page 1) exclusively to keep the background
image and any non-animated sprites, including the cards. Then, in order to
limit flickering when animating the bullet, character and boss sprites on top
of that (or just to the limit number of VRAM accesses, who knows), ZUN goes to
great lengths and tries to make sure to only copy back the pixels that were
modified on plane 0 in the last frame.
(Which doesn't work that well though. When you play the game, you still notice
tons of flickering whenever sprites overlap.)
And by "great lengths", I mean "having a separate counterpart function for
each shape and sprite animated which recalculates and copies back the same
pixels from plane 1 to plane 0", because that's what the new functions here
lead me to believe. Both of them are only called at one place: the wave
function on the second half of Elis' entrance animation, and the horizontal
masked line function for Reimu's X attack animations.
2015-03-10 16:39:00 +00:00
|
|
|
|
|
|
|
typedef struct {
|
2020-03-06 23:25:03 +00:00
|
|
|
dots16_t B, R, G, E;
|
|
|
|
} planar16_t;
|
[C decompilation] [th01/reiiden] Randomly shaped VRAM copy functions, #1
So apparently, TH01 isn't double-buffered in the usual sense, and instead uses
the second hardware framebuffer (page 1) exclusively to keep the background
image and any non-animated sprites, including the cards. Then, in order to
limit flickering when animating the bullet, character and boss sprites on top
of that (or just to the limit number of VRAM accesses, who knows), ZUN goes to
great lengths and tries to make sure to only copy back the pixels that were
modified on plane 0 in the last frame.
(Which doesn't work that well though. When you play the game, you still notice
tons of flickering whenever sprites overlap.)
And by "great lengths", I mean "having a separate counterpart function for
each shape and sprite animated which recalculates and copies back the same
pixels from plane 1 to plane 0", because that's what the new functions here
lead me to believe. Both of them are only called at one place: the wave
function on the second half of Elis' entrance animation, and the horizontal
masked line function for Reimu's X attack animations.
2015-03-10 16:39:00 +00:00
|
|
|
|
2020-01-12 18:13:26 +00:00
|
|
|
typedef struct {
|
2020-03-06 23:25:03 +00:00
|
|
|
dots32_t B, R, G, E;
|
|
|
|
} planar32_t;
|
2020-01-12 18:13:26 +00:00
|
|
|
|
2020-03-19 13:12:13 +00:00
|
|
|
// Abstracted dot and planar types, with their width defined by a macro.
|
|
|
|
#define dots_t_(x) dots##x##_t
|
|
|
|
#define dots_t(x) dots_t_(x)
|
|
|
|
#define planar_t_(x) planar##x##_t
|
|
|
|
#define planar_t(x) planar_t_(x)
|
|
|
|
|
2015-02-24 15:17:50 +00:00
|
|
|
// Since array subscripts create slightly different assembly in places, we
|
|
|
|
// offer both variants.
|
2020-03-06 23:25:03 +00:00
|
|
|
extern dots8_t *VRAM_PLANE[PL_COUNT];
|
|
|
|
extern dots8_t *VRAM_PLANE_B;
|
|
|
|
extern dots8_t *VRAM_PLANE_G;
|
|
|
|
extern dots8_t *VRAM_PLANE_R;
|
|
|
|
extern dots8_t *VRAM_PLANE_E;
|
2015-02-24 14:45:38 +00:00
|
|
|
|
2020-01-12 19:34:42 +00:00
|
|
|
#define VRAM_OFFSET(x, y) ((x) >> 3) + (y << 6) + (y << 4)
|
|
|
|
|
2020-03-15 19:11:55 +00:00
|
|
|
#ifdef __cplusplus
|
|
|
|
static inline unsigned int vram_offset_shift(int x, int y)
|
|
|
|
{
|
|
|
|
return VRAM_OFFSET(x, y);
|
|
|
|
}
|
2020-03-16 17:54:02 +00:00
|
|
|
|
|
|
|
static inline unsigned int vram_offset_muldiv(int x, int y)
|
|
|
|
{
|
|
|
|
return (y * ROW_SIZE) + (x / 8);
|
|
|
|
}
|
2020-03-15 19:11:55 +00:00
|
|
|
#endif
|
|
|
|
|
2020-01-12 19:34:42 +00:00
|
|
|
#define VRAM_CHUNK(plane, offset, bit_count) \
|
2020-03-06 23:25:03 +00:00
|
|
|
*(dots##bit_count##_t *)(VRAM_PLANE_##plane + offset)
|
2020-01-12 19:34:42 +00:00
|
|
|
|
|
|
|
#define VRAM_SNAP(dst, plane, offset, bit_count) \
|
|
|
|
dst = VRAM_CHUNK(plane, offset, bit_count);
|
|
|
|
|
2020-03-06 23:25:03 +00:00
|
|
|
#define VRAM_SNAP_PLANAR(dst, offset, bit_count) \
|
2020-01-12 19:34:42 +00:00
|
|
|
VRAM_SNAP(dst.B, B, offset, bit_count); \
|
|
|
|
VRAM_SNAP(dst.R, R, offset, bit_count); \
|
|
|
|
VRAM_SNAP(dst.G, G, offset, bit_count); \
|
|
|
|
VRAM_SNAP(dst.E, E, offset, bit_count);
|
|
|
|
|
|
|
|
#define VRAM_PUT(plane, offset, src, bit_count) \
|
|
|
|
VRAM_CHUNK(plane, offset, bit_count) = src;
|
|
|
|
|
2020-03-06 23:25:03 +00:00
|
|
|
#define VRAM_PUT_PLANAR(offset, src, bit_count) \
|
2020-01-12 19:34:42 +00:00
|
|
|
VRAM_PUT(B, offset, src.B, bit_count); \
|
|
|
|
VRAM_PUT(R, offset, src.R, bit_count); \
|
|
|
|
VRAM_PUT(G, offset, src.G, bit_count); \
|
|
|
|
VRAM_PUT(E, offset, src.E, bit_count);
|
|
|
|
|
2015-02-24 14:45:38 +00:00
|
|
|
#define PLANE_DWORD_BLIT(dst, src) \
|
2020-03-06 23:25:03 +00:00
|
|
|
for(p = 0; p < PLANE_SIZE; p += (int)sizeof(dots32_t)) { \
|
|
|
|
*(dots32_t*)((dst) + p) = *(dots32_t*)((src) + p); \
|
2015-02-24 14:45:38 +00:00
|
|
|
}
|
2015-02-25 22:05:20 +00:00
|
|
|
|
2015-03-14 22:25:50 +00:00
|
|
|
void pascal vram_planes_set(void);
|
2015-03-01 21:52:25 +00:00
|
|
|
// -----------------
|
2020-03-13 00:42:40 +00:00
|
|
|
|
|
|
|
#endif /* REC98_H */
|