Compare commits

...

10 Commits

Author SHA1 Message Date
dd8c1010c5 vdp2: add color_calculation_ratio 2024-03-24 21:51:37 +08:00
50173b96e3 raytracing: remove all uses of fp_raw_tag{} 2024-02-02 08:58:53 +08:00
fb041b392c smpc: update macro usage of input_ examples 2024-02-02 08:58:53 +08:00
a1707c72a8 normal_sprite_animated: update macro usage 2024-02-02 08:58:53 +08:00
863701941e scsp: add sound_cpu__midi_debug example
I don't think this is particularly useful (yet).
2024-02-02 08:58:53 +08:00
72bdd34675 vdp1: add cube2 example 2024-02-02 08:58:53 +08:00
2eef2be39e raytracing: update example 2024-02-02 08:58:53 +08:00
fb0b237e0c ttf-bitmap: allow arbitrary width/height glyphs 2024-02-02 08:49:35 +08:00
5f290a3e93 MIT license 2023-09-29 17:50:06 +00:00
511bfd1d0c remove duplicate start functions
This is now provided by default in saturn/common.mk.

Also updates moved header names, changed bit macro names, and changes
to the common.mk build process.
2023-07-30 18:03:19 +00:00
42 changed files with 962 additions and 178 deletions

1
.gitignore vendored
View File

@ -1,3 +1,4 @@
*.d
*.gch
*.o
*.elf

21
LICENSE Normal file
View File

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2023 saturn-examples contributors
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@ -17,6 +17,7 @@ ALL += wordle/wordle.cue
ALL += scsp/slot.cue
ALL += scsp/sound_cpu__slot.cue
ALL += scsp/sound_cpu__interrupt.cue
ALL += scsp/sound_cpu__midi_debug.cue
ALL += editor/main_saturn.cue
all: $(ALL)
@ -26,6 +27,12 @@ include $(LIB)/common.mk
%.data.o: %.data
$(BUILD_BINARY_O)
%.pattern.o: %.pattern
$(BUILD_BINARY_O)
%.tile.o: %.tile
$(BUILD_BINARY_O)
%.bin.o: %.bin
$(BUILD_BINARY_O)
@ -44,8 +51,11 @@ vdp2/nbg0.elf: vdp2/nbg0.o res/butterfly.data.o res/butterfly.data.pal.o
vdp2/nbg0_16color.elf: vdp2/nbg0_16color.o res/kirby.data.o res/kirby.data.pal.o
vdp2/color_calculation_ratio.elf: vdp2/color_calculation_ratio.o res/mai00.data.o res/mai.data.pal.o res/haohmaru.data.o res/haohmaru.data.pal.o res/forest.data.pal.o res/forest.pattern.o res/forest.tile.o
vdp1/polygon.elf: vdp1/polygon.o
vdp1/cube.elf: vdp1/cube.o $(LIBGCC)
vdp1/cube2.elf: vdp1/cube2.o
vdp1/normal_sprite.elf: vdp1/normal_sprite.o res/mai00.data.o res/mai.data.pal.o
vdp1/normal_sprite_color_bank.elf: vdp1/normal_sprite_color_bank.o res/mai00.data.o res/mai.data.pal.o
@ -81,7 +91,7 @@ common/keyboard.hpp: common/keyboard.py
common/keyboard.cpp: common/keyboard.py common/keyboard.hpp
python common/keyboard.py definition > $@
smpc/input_keyboard.o: common/keyboard.hpp
smpc/input_keyboard.cpp.d: common/keyboard.hpp
smpc/input_keyboard.elf: smpc/input_keyboard.o sh/lib1funcs.o res/dejavusansmono.font.bin.o common/keyboard.o common/draw_font.o common/palette.o
@ -90,7 +100,7 @@ wordle/main_saturn.o: common/keyboard.hpp
wordle/word_list.hpp: wordle/word_list.csv wordle/word_list.py
python wordle/word_list.py > $@
wordle/wordle.o: wordle/word_list.hpp
wordle/wordle.cpp.d: wordle/word_list.hpp
wordle/wordle.elf: wordle/main_saturn.o wordle/wordle.o wordle/draw.o sh/lib1funcs.o res/dejavusansmono.font.bin.o common/keyboard.o common/draw_font.o common/palette.o
@ -121,6 +131,8 @@ scsp/sound_cpu__slot.elf: scsp/sound_cpu__slot.o m68k/slot.bin.o
scsp/sound_cpu__interrupt.elf: scsp/sound_cpu__interrupt.o m68k/interrupt.bin.o sh/lib1funcs.o res/sperrypc.font.bin.o common/draw_font.o common/palette.o
scsp/sound_cpu__midi_debug.elf: scsp/sound_cpu__midi_debug.o m68k/midi_debug.bin.o sh/lib1funcs.o res/nec.bitmap.bin.o
scsp/fm.elf: scsp/fm.o res/nec.bitmap.bin.o sh/lib1funcs.o saturn/start.o scsp/sine-44100-s16be-1ch-100sample.pcm.o
scsp/sound_cpu__midi.elf: scsp/sound_cpu__midi.o m68k/midi.bin.o res/nec.bitmap.bin.o sh/lib1funcs.o saturn/start.o
@ -145,7 +157,7 @@ clean-sh:
-not -path './saturn/*' \
-not -path './tools/*' \
-regextype posix-egrep \
-regex '.*\.(iso|o|bin|elf|cue)$$' \
-regex '.*\.(iso|o|bin|elf|cue|gch)$$' \
-exec rm {} \;
rm -f \
common/keyboard.cpp \

View File

@ -325,8 +325,8 @@ void main()
2-word: value of bit 5-0 * 0x4000
*/
vdp2.reg.MPOFN = MPOFN__N0MP(0); // bits 8~6
vdp2.reg.MPABN0 = MPABN0__N0MPB(0) | MPABN0__N0MPA(plane_a); // bits 5~0
vdp2.reg.MPCDN0 = MPABN0__N0MPD(0) | MPABN0__N0MPC(0); // bits 5~0
vdp2.reg.MPABN0 = MPABN0__N0MPB(plane_a) | MPABN0__N0MPA(plane_a); // bits 5~0
vdp2.reg.MPCDN0 = MPCDN0__N0MPD(plane_a) | MPCDN0__N0MPC(plane_a); // bits 5~0
// zeroize character/cell data from 0 up to plane_a_offset
fill<uint32_t>(&vdp2.vram.u32[(0 / 4)], 0, plane_offset(plane_a));

View File

@ -15,6 +15,14 @@ include $(LIB)/m68k/common.mk
synth 101s $* 441 vol -10dB
mv $@.raw $@
# 88200 bytes
%-44100-s16be-1ch-1sec.pcm: Makefile
sox \
-r 44100 -e signed-integer -b 16 -c 1 -n -B \
$@.raw \
synth 1 sin 440 vol -10dB
mv $@.raw $@
%.pcm.o: %.pcm
$(BUILD_BINARY_O)
@ -26,3 +34,5 @@ slot.elf: slot.o sine-44100-s16be-1ch-1sec.pcm.o
interrupt.elf: interrupt.o jojo-11025-s16be-1ch.pcm.o
midi.elf: midi.o sine-44100-s16be-1ch-100sample.pcm.o midi_test-c-major-scale.mid.o f2.mid.o ../midi/parse.o $(LIBGCC)
midi_debug.elf: midi_debug.o

View File

@ -4,7 +4,7 @@
extern void * _jojo_start __asm("_binary_jojo_11025_s16be_1ch_pcm_start");
static volatile int32_t frame = 0x0;
static int32_t frame = 0x0;
constexpr int32_t tactl = 7;
constexpr int32_t frame_size = ((1 << tactl) * 256) / (44100 / 11025);

24
m68k/midi_debug.cpp Normal file
View File

@ -0,0 +1,24 @@
#include <cstdint>
#include "scsp.h"
static uint32_t ram_ix = 0;
static uint16_t * debug_buf = &scsp.ram.u16[0x080000 / 2];
static uint16_t * debug_length = &scsp.ram.u16[(0x080000 / 4) - 2];
void main()
{
*debug_length = 0;
while (true) {
uint16_t midiu = scsp.reg.ctrl.MIDIU;
uint16_t mibuf = MIDIU__MIBUF(midiu);
uint16_t miemp = midiu & MIDIU__MIEMP;
if (miemp) continue;
(*debug_length)++;
(*debug_buf) = mibuf;
debug_buf++;
}
}

View File

@ -2,7 +2,7 @@
#include "scsp.h"
extern void * _sine_start __asm("_binary_sine_44100_s16be_1ch_pcm_start");
extern void * _sine_start __asm("_binary_sine_44100_s16be_1ch_1sec_pcm_start");
void main()
{

View File

@ -15,6 +15,7 @@ struct vec<4, T>
inline constexpr vec();
inline constexpr vec(T scalar);
inline constexpr vec(T _x, T _y, T _z, T _w);
inline constexpr vec(vec<3, T> const& xyz, T w);
constexpr inline vec<4, T> operator-() const;
inline constexpr T const& operator[](int i) const;
@ -34,8 +35,13 @@ inline constexpr vec<4, T>::vec(T scalar)
{}
template <typename T>
inline constexpr vec<4, T>::vec(T _x, T _y, T _z, T _w)
: x(_x), y(_y), z(_z), w(_w)
inline constexpr vec<4, T>::vec(T x, T y, T z, T w)
: x(x), y(y), z(z), w(w)
{}
template <typename T>
inline constexpr vec<4, T>::vec(vec<3, T> const& xyz, T w)
: x(xyz.x), y(xyz.y), z(xyz.z), w(w)
{}
template <typename T>

View File

@ -4,8 +4,8 @@
#include "smpc.h"
#include "sh2.h"
#include "vec.hpp"
#include "fp.hpp"
#include "../math/vec3.hpp"
#include "../math/fp.hpp"
#include "raytracing.hpp"
constexpr inline
@ -22,9 +22,9 @@ inline constexpr T rgb(const vec3& color)
vec3 c = functor1(clamp, color) * fp16_16(channel_mask);
T red = static_cast<T>(c.r.value >> 16);
T green = static_cast<T>(c.g.value >> 16);
T blue = static_cast<T>(c.b.value >> 16);
T red = static_cast<T>(c.x.value >> 16);
T green = static_cast<T>(c.y.value >> 16);
T blue = static_cast<T>(c.z.value >> 16);
return (1 << last_bit)
| (blue << (P * 2))
@ -84,7 +84,8 @@ void start_slave()
*/
sh2_vec[0x94] = (uint32_t)(&slave_main);
for (volatile int i = 0; i < 10; i++);
for (int i = 0; i < 10; i++)
asm volatile ("nop");
smpc.reg.SF = 1;
smpc.reg.COMREG = COMREG__SSHON;
@ -92,7 +93,7 @@ void start_slave()
while ((smpc.reg.SF & 0x01) == 1);
}
void main_asdf()
void main()
{
// DISP: Please make sure to change this bit from 0 to 1 during V blank.
vdp2.reg.TVMD = ( TVMD__DISP | TVMD__LSMD__NON_INTERLACE
@ -131,10 +132,3 @@ void main_asdf()
render(0, put_pixel);
}
extern "C"
void start(void)
{
main_asdf();
while (1) {}
}

View File

@ -1,19 +1,13 @@
#include <stdint.h>
#include "vec.hpp"
#include "vec3.hpp"
#include "fp.hpp"
#include "raytracing.hpp"
namespace viewport {
constexpr int width = 1;
constexpr int height = 1;
}
vec3 canvas_to_viewport(int cx, int cy)
vec3 canvas_to_viewport(const int cx, const int cy)
{
return vec3(
fp16_16(((cx * viewport::width) * (1 << 16)) >> canvas::bit_width, fp_raw_tag{}),
fp16_16(((cy * viewport::height) * (1 << 16)) >> canvas::bit_height, fp_raw_tag{}),
return vec3(fp16_16(cx * viewport::width) / fp16_16(canvas::square_width),
fp16_16(cy * viewport::height) / fp16_16(canvas::square_height),
fp16_16(1)
);
}
@ -53,44 +47,44 @@ constexpr scene scene {
1, // radius
{1, 0, 0}, // color
8, // specular
fp16_16(65536 * 0.2, fp_raw_tag{}) // reflective
fp16_16(0.2) // reflective
},
{
{2, 0, 4},
1,
{0, 0, 1},
10,
fp16_16(65536 * 0.3, fp_raw_tag{})
fp16_16(0.3)
},
{
{-2, 0, 4},
fp16_16(1),
{0, 1, 0},
10,
fp16_16(65536 * 0.4, fp_raw_tag{})
fp16_16(0.4)
},
{
{0, -31, 0},
fp16_16(30),
{1, 1, 0},
0,
fp16_16(65536 * 0.5, fp_raw_tag{})
fp16_16(0.5)
}
},
{ // lights
{
light_type::ambient, // type
fp16_16(65536 * 0.2, fp_raw_tag{}), // intensity
fp16_16(0.2), // intensity
{{0, 0, 0}} //
},
{
light_type::point, // type
fp16_16(65536 * 0.6, fp_raw_tag{}), // intensity
fp16_16(0.6), // intensity
{{2, 1, 0}} // position
},
{
light_type::directional, // type
fp16_16(65536 * 0.6, fp_raw_tag{}), // intensity
fp16_16(0.6), // intensity
{{1, 4, 4}} // direction
}
}
@ -179,7 +173,7 @@ fp16_16 compute_lighting(const vec3& point, const vec3& normal,
t_max = fp_limits<fp16_16>::max();
}
constexpr fp16_16 t_min = fp16_16(128, fp_raw_tag{});
constexpr fp16_16 t_min = fp16_16(128.f / 65536.f);
auto [shadow_t, shadow_sphere] = closest_intersection(point, light_vector, t_min, t_max);
if (shadow_sphere != nullptr)
continue;
@ -235,7 +229,7 @@ static vec3 trace_ray
} else {
auto reflected_ray = reflect_ray(direction_neg, normal);
auto reflected_color = trace_ray(point, reflected_ray,
fp16_16(128, fp_raw_tag{}),
fp16_16(128.f / 65536.f),
fp_limits<fp16_16>::max(),
recursion_depth - 1);
return local_color * (fp16_16(1) - reflective) + reflected_color * reflective;
@ -245,16 +239,13 @@ static vec3 trace_ray
void render(int half, void (&put_pixel) (int32_t x, int32_t y, const vec3& c))
{
using namespace canvas;
vec3 origin = vec3(0, 0, 0);
int x_low = half ? 0 : -(320/2);
int x_high = half ? (320/2) : 0;
int x_low = half ? 0 : -(canvas::width/2);
int x_high = half ? (canvas::width/2) : 0;
//for (int x = -(width/2); x < (width/2); x++) {
for (int x = x_low; x < x_high; x++) {
for (int y = -(height/2 + 1); y < (height/2 + 1); y++) {
for (int y = -(canvas::height/2 + 1); y < (canvas::height/2 + 1); y++) {
vec3 direction = canvas_to_viewport(x, y);
vec3 color = trace_ray(origin, direction,
fp16_16(1),

View File

@ -1,15 +1,20 @@
#pragma once
#include "fp.hpp"
#include "../math/fp.hpp"
#include "raytracing.hpp"
using vec3 = vec<3, fp16_16>;
namespace viewport {
constexpr int width = 1;
constexpr int height = 1;
}
namespace canvas {
constexpr int bit_width = 8;
constexpr int bit_height = 8;
constexpr int width = (1 << bit_width);
constexpr int height = (1 << bit_height);
constexpr int square_width = 256;
constexpr int square_height = 256;
constexpr int width = 320;
constexpr int height = 240;
}
void render(int half, void (&put_pixel) (int32_t x, int32_t y, const vec3& c));

BIN
res/forest.data Normal file

Binary file not shown.

BIN
res/forest.data.pal Normal file

Binary file not shown.

BIN
res/forest.pattern Normal file

Binary file not shown.

3
res/forest.tile Normal file

File diff suppressed because one or more lines are too long

BIN
res/forest.xcf Normal file

Binary file not shown.

BIN
res/haohmaru.data Normal file

Binary file not shown.

BIN
res/haohmaru.data.pal Normal file

Binary file not shown.

BIN
res/haohmaru.xcf Normal file

Binary file not shown.

View File

@ -740,7 +740,7 @@ void main()
*/
vdp2.reg.MPOFN = MPOFN__N0MP(0); // bits 8~6
vdp2.reg.MPABN0 = MPABN0__N0MPB(0) | MPABN0__N0MPA(plane_a); // bits 5~0
vdp2.reg.MPCDN0 = MPABN0__N0MPD(0) | MPABN0__N0MPC(0); // bits 5~0
vdp2.reg.MPCDN0 = MPCDN0__N0MPD(0) | MPCDN0__N0MPC(0); // bits 5~0
// zeroize character/cell data from 0 up to plane_a_offset
fill<uint32_t>(&vdp2.vram.u32[(0 / 4)], 0, plane_offset(plane_a));

View File

@ -33,10 +33,3 @@ void main()
slot.LOOP |= LOOP__KYONEX;
}
extern "C"
void start(void)
{
main();
while (1);
}

View File

@ -239,10 +239,3 @@ void main()
init_sound();
}
extern "C"
void start(void)
{
main();
while (1);
}

View File

@ -240,7 +240,7 @@ void v_blank_in_int()
scu.reg.IMS = ~(IMS__SMPC | IMS__V_BLANK_IN);
// flip planes;
vdp2.reg.MPABN0 = MPABN0__N0MPB(0) | MPABN0__N0MPA(plane_a + plane_ix);
vdp2.reg.MPCDN0 = MPCDN0__N0MPB(0) | MPCDN0__N0MPA(plane_a + plane_ix);
//plane_ix = !plane_ix;
// wait at least 300us, as specified in the SMPC manual.
@ -355,8 +355,8 @@ void main()
2-word: value of bit 5-0 * 0x4000
*/
vdp2.reg.MPOFN = MPOFN__N0MP(0); // bits 8~6
vdp2.reg.MPABN0 = MPABN0__N0MPB(0) | MPABN0__N0MPA(plane_a); // bits 5~0
vdp2.reg.MPCDN0 = MPABN0__N0MPD(0) | MPABN0__N0MPC(0); // bits 5~0
vdp2.reg.MPCDN0 = MPCDN0__N0MPB(0) | MPCDN0__N0MPA(plane_a); // bits 5~0
vdp2.reg.MPCDN0 = MPCDN0__N0MPD(0) | MPCDN0__N0MPC(0); // bits 5~0
// zeroize character/cell data from 0 up to plane_a_offset
fill<uint32_t>(&vdp2.vram.u32[(0 / 4)], 0, plane_offset(plane_a));

View File

@ -0,0 +1,204 @@
#include <stdint.h>
#include "vdp2.h"
#include "smpc.h"
#include "scsp.h"
#include "scu.h"
#include "sh2.h"
#include "../common/copy.hpp"
#include "../common/vdp2_func.hpp"
#include "../common/string.hpp"
extern void * _nec_bitmap_start __asm("_binary_res_nec_bitmap_bin_start");
extern void * _m68k_start __asm("_binary_m68k_midi_debug_bin_start");
extern void * _m68k_size __asm("_binary_m68k_midi_debug_bin_size");
constexpr inline uint16_t rgb15(int32_t r, int32_t g, int32_t b)
{
return ((b & 31) << 10) | ((g & 31) << 5) | ((r & 31) << 0);
}
void palette_data()
{
vdp2.cram.u16[1 + 0 ] = rgb15( 0, 0, 0);
vdp2.cram.u16[2 + 0 ] = rgb15(31, 31, 31);
vdp2.cram.u16[1 + 16] = rgb15(31, 31, 31);
vdp2.cram.u16[2 + 16] = rgb15( 0, 0, 0);
vdp2.cram.u16[1 + 32] = rgb15(10, 10, 10);
vdp2.cram.u16[2 + 32] = rgb15(31, 31, 31);
}
namespace pix_fmt_4bpp
{
constexpr inline uint32_t
bit(uint8_t n, int32_t i)
{
i &= 7;
auto b = (n >> (7 - i)) & 1;
return ((b + 1) << ((7 - i) * 4));
}
constexpr inline uint32_t
bits(uint8_t n)
{
return
bit(n, 0) | bit(n, 1) | bit(n, 2) | bit(n, 3)
| bit(n, 4) | bit(n, 5) | bit(n, 6) | bit(n, 7);
}
static_assert(bits(0b1100'1110) == 0x2211'2221);
static_assert(bits(0b1010'0101) == 0x2121'1212);
static_assert(bits(0b1000'0000) == 0x2111'1111);
}
void cell_data()
{
const uint8_t * normal = reinterpret_cast<uint8_t*>(&_nec_bitmap_start);
for (int ix = 0; ix <= (0x7f - 0x20); ix++) {
for (int y = 0; y < 8; y++) {
const uint8_t row_n = normal[ix * 8 + y];
vdp2.vram.u32[ 0 + (ix * 8) + y] = pix_fmt_4bpp::bits(row_n);
}
}
}
constexpr int32_t plane_a = 2;
constexpr inline int32_t plane_offset(int32_t n) { return n * 0x2000; }
constexpr int32_t page_size = 64 * 64 * 2; // N0PNB__1WORD (16-bit)
constexpr int32_t plane_size = page_size * 1;
constexpr int32_t page_width = 64;
static int plane_ix = 0;
void init_vdp2()
{
v_blank_in();
// DISP: Please make sure to change this bit from 0 to 1 during V blank.
vdp2.reg.TVMD = ( TVMD__DISP | TVMD__LSMD__NON_INTERLACE
| TVMD__VRESO__240 | TVMD__HRESO__NORMAL_320);
/* set the color mode to 5bits per channel, 1024 colors */
vdp2.reg.RAMCTL = RAMCTL__CRMD__RGB_5BIT_1024;
/* enable display of NBG0 */
vdp2.reg.BGON = BGON__N0ON;
/* set character format for NBG0 to palettized 16 color
set enable "cell format" for NBG0
set character size for NBG0 to 1x1 cell */
vdp2.reg.CHCTLA = CHCTLA__N0CHCN__16_COLOR
| CHCTLA__N0BMEN__CELL_FORMAT
| CHCTLA__N0CHSZ__1x1_CELL;
/* "Note: In color RAM modes 0 and 2, 2048-color becomes 1024-color" */
/* use 1-word (16-bit) pattern names */
vdp2.reg.PNCN0 = PNCN0__N0PNB__1WORD;
/* plane size */
vdp2.reg.PLSZ = PLSZ__N0PLSZ__1x1;
/* map plane offset
1-word: value of bit 6-0 * 0x2000
2-word: value of bit 5-0 * 0x4000
*/
vdp2.reg.MPOFN = MPOFN__N0MP(0); // bits 8~6
vdp2.reg.MPABN0 = MPABN0__N0MPB(plane_a) | MPABN0__N0MPA(plane_a); // bits 5~0
vdp2.reg.MPCDN0 = MPCDN0__N0MPD(plane_a) | MPCDN0__N0MPC(plane_a); // bits 5~0
// zeroize character/cell data from 0 up to plane_a_offset
fill<uint32_t>(&vdp2.vram.u32[(0 / 4)], 0, plane_offset(plane_a));
// zeroize plane_a; `0` is the ascii 0x20 ("space") which doubles as
// "transparency" character.
fill<uint32_t>(&vdp2.vram.u32[(plane_offset(plane_a) / 4)], 0, plane_size * 2);
}
void
set_char(int32_t x, int32_t y, uint8_t palette, uint8_t c)
{
const auto ix = (plane_offset(plane_a + plane_ix) / 2) + (y * page_width) + x;
vdp2.vram.u16[ix] =
PATTERN_NAME_TABLE_1WORD__PALETTE(palette)
| PATTERN_NAME_TABLE_1WORD__CHARACTER((c - 0x20));
}
static uint16_t * debug_buf = &scsp.ram.u16[0x080000 / 4];
static uint16_t * debug_length = &scsp.ram.u16[(0x080000 / 4) - 2];
void render()
{
static uint8_t lbuf[4];
string::hex(lbuf, 4, *debug_length);
for (uint32_t i = 0; i < 4; i++) set_char(i, 0, 0, lbuf[i]);
for (uint32_t i = 0; i < *debug_length; i++) {
uint8_t buf[2];
string::hex(buf, 2, debug_buf[i]);
int32_t x = 1 + (i % 8);
int32_t y = 1 + (i / 8);
set_char(x * 3 + 0, y, 0, buf[0]);
set_char(x * 3 + 1, y, 0, buf[1]);
}
}
extern "C"
void v_blank_in_int(void) __attribute__ ((interrupt_handler));
void v_blank_in_int()
{
scu.reg.IST &= ~(IST__V_BLANK_IN);
scu.reg.IMS = ~(IMS__V_BLANK_IN);
render();
}
void main()
{
/* SEGA SATURN TECHNICAL BULLETIN # 51
The document suggests that Sound RAM is (somewhat) preserved
during SNDOFF.
*/
while ((smpc.reg.SF & 1) != 0);
smpc.reg.SF = 1;
smpc.reg.COMREG = COMREG__SNDOFF;
while (smpc.reg.OREG[31].val != OREG31__SNDOFF);
scsp.reg.ctrl.MIXER = MIXER__MEM4MB;
/*
The Saturn BIOS does not (un)initialize the DSP. Without zeroizing the DSP
program, the SCSP DSP appears to have a program that continuously writes to
0x30000 through 0x3ffff in sound RAM, which has the effect of destroying any
samples stored there.
*/
reg32 * dsp_steps = reinterpret_cast<reg32*>(&(scsp.reg.dsp.STEP[0].MPRO[0]));
fill<reg32>(dsp_steps, 0, (sizeof (scsp.reg.dsp.STEP)));
uint32_t * m68k_main_start = reinterpret_cast<uint32_t*>(&_m68k_start);
uint32_t m68k_main_size = reinterpret_cast<uint32_t>(&_m68k_size);
copy<uint32_t>(&scsp.ram.u32[0], m68k_main_start, m68k_main_size);
while ((smpc.reg.SF & 1) != 0);
smpc.reg.SF = 1;
smpc.reg.COMREG = COMREG__SNDON;
while (smpc.reg.OREG[31].val != OREG31__SNDON);
// do nothing while the sound CPU manipulates the SCSP
init_vdp2();
palette_data();
cell_data();
sh2_vec[SCU_VEC__V_BLANK_IN] = (u32)(&v_blank_in_int);
scu.reg.IST = 0;
scu.reg.IMS = ~(IMS__V_BLANK_IN);
}

View File

@ -43,10 +43,3 @@ void main()
// do nothing while the sound CPU manipulates the SCSP
}
extern "C"
void start(void)
{
main();
while (1);
}

View File

@ -330,7 +330,7 @@ void main()
// It appears Kronos does not correctly calculate the color address in the
// VDP1 debugger. Kronos will report FFFC when the actual color table address
// in this example is 7FFE0.
vdp1.vram.cmd[2].COLR = COLR__ADDRESS(color_address); // non-palettized (rgb15) color data
vdp1.vram.cmd[2].COLR = COLR__LOOKUP_TABLE__ADDRESS(color_address); // non-palettized (rgb15) color data
vdp1.vram.cmd[2].SRCA = SRCA(character_address[0]);
vdp1.vram.cmd[2].SIZE = SIZE__X(sprite_stride) | SIZE__Y(sprite_height);
vdp1.vram.cmd[2].XA = foo[0].x;
@ -345,7 +345,7 @@ void main()
// It appears Kronos does not correctly calculate the color address in the
// VDP1 debugger. Kronos will report FFFC when the actual color table address
// in this example is 7FFE0.
vdp1.vram.cmd[3].COLR = COLR__ADDRESS(color_address); // non-palettized (rgb15) color data
vdp1.vram.cmd[3].COLR = COLR__LOOKUP_TABLE__ADDRESS(color_address); // non-palettized (rgb15) color data
vdp1.vram.cmd[3].SRCA = SRCA(character_address[1]);
vdp1.vram.cmd[3].SIZE = SIZE__X(sprite_stride) | SIZE__Y(sprite_height);
vdp1.vram.cmd[3].XA = foo[1].x;
@ -373,10 +373,3 @@ void main()
scu.reg.IST = 0;
scu.reg.IMS &= ~(IMS__SMPC);
}
extern "C"
void start(void)
{
main();
while (1);
}

View File

@ -415,7 +415,7 @@ void main()
// It appears Kronos does not correctly calculate the color address in the
// VDP1 debugger. Kronos will report FFFC when the actual color table address
// in this example is 7FFE0.
vdp1.vram.cmd[2].COLR = COLR__ADDRESS(color_address); // non-palettized (rgb15) color data
vdp1.vram.cmd[2].COLR = COLR__LOOKUP_TABLE__ADDRESS(color_address); // non-palettized (rgb15) color data
vdp1.vram.cmd[2].SRCA = SRCA(character_address[0]);
vdp1.vram.cmd[2].SIZE = SIZE__X(sprite_stride) | SIZE__Y(sprite_height);
vdp1.vram.cmd[2].XA = foo[0].x;
@ -430,7 +430,7 @@ void main()
// It appears Kronos does not correctly calculate the color address in the
// VDP1 debugger. Kronos will report FFFC when the actual color table address
// in this example is 7FFE0.
vdp1.vram.cmd[3].COLR = COLR__ADDRESS(color_address); // non-palettized (rgb15) color data
vdp1.vram.cmd[3].COLR = COLR__LOOKUP_TABLE__ADDRESS(color_address); // non-palettized (rgb15) color data
vdp1.vram.cmd[3].SRCA = SRCA(character_address[1]);
vdp1.vram.cmd[3].SIZE = SIZE__X(sprite_stride) | SIZE__Y(sprite_height);
vdp1.vram.cmd[3].XA = foo[1].x;
@ -462,10 +462,3 @@ void main()
scu.reg.IST = 0;
scu.reg.IMS = ~(IMS__SMPC | IMS__V_BLANK_IN);
}
extern "C"
void start(void)
{
main();
while (1);
}

68
tools/tile.py Normal file
View File

@ -0,0 +1,68 @@
import sys
import struct
with open(sys.argv[1], 'rb') as f:
buf = f.read()
stride = 320
def get_tile(buf, tx, ty):
tile = []
for y in range(8):
row = []
for x in range(8):
yy = ty * 8 + y
xx = tx * 8 + x
ix = yy * stride + xx
px = buf[ix]
row.append(px)
tile.append(tuple(row))
return tuple(tile)
def tile_input(buf):
tiles = []
pattern = []
for ty in range(240 // 8):
for tx in range(320 // 8):
tile = get_tile(buf, tx, ty)
#if tile in tiles:
# pass
#else:
# tiles.append(tile)
#tile_ix = tiles.index(tile)
tiles.append(tile)
tile_ix = len(tiles) - 1
pattern.append(tile_ix)
return tiles, pattern
def emit_tile(tile):
for row in tile:
for ix in range(len(row) // 2):
a = row[ix * 2 + 0]
b = row[ix * 2 + 1]
c = ((a & 0xf) << 4) | ((b & 0xf) << 0)
yield c
def emit_tiles(tiles):
for tile in tiles:
yield from emit_tile(tile)
def emit_tile_data(tiles):
b = bytes(emit_tiles(tiles))
with open(sys.argv[2], 'wb') as f:
f.write(b)
def emit_pattern_data(pattern):
with open(sys.argv[3], 'wb') as f:
for ix in pattern:
assert ix < 1200, ix
b = struct.pack('>H', ix)
f.write(b)
assert len(sys.argv) == 4, len(sys.argv)
tiles, pattern = tile_input(buf)
emit_tile_data(tiles)
emit_pattern_data(pattern)

View File

@ -25,26 +25,22 @@ load_bitmap_char(FT_Face face,
//printf("num_grays %d\n", face->glyph->bitmap.num_grays);
//printf("pitch %d\n", face->glyph->bitmap.pitch);
//printf("width %d\n", face->glyph->bitmap.width);
assert(face->glyph->bitmap.width == 8);
//printf("char_code %lx rows %d\n", char_code, face->glyph->bitmap.rows);
assert((face->glyph->bitmap.rows % 8) == 0);
assert(face->glyph->bitmap.width / face->glyph->bitmap.pitch == 8);
//assert((face->glyph->bitmap.rows % 8) == 0);
//assert(face->glyph->bitmap.width / face->glyph->bitmap.pitch == 8);
for (int y = 0; y < (int)face->glyph->bitmap.rows; y++) {
uint8_t * row = &face->glyph->bitmap.buffer[y * face->glyph->bitmap.pitch];
uint8_t row_out = 0;
for (unsigned int x = 0; x < face->glyph->bitmap.width; x++) {
int bit;
if (x < face->glyph->bitmap.width) {
bit = (row[x / 8] >> (7 - (x % 8))) & 1;
} else {
bit = 0;
}
if (x % 8 == 0) row_out = 0;
const uint8_t bit = (row[x / 8] >> (7 - (x % 8))) & 1;
//std::cerr << (bit ? "█" : " ");
row_out |= (bit << (7 - (x % 8)));
if (x % 8 == 7 || x == (face->glyph->bitmap.width - 1))
buf[(y * face->glyph->bitmap.pitch) + (x / 8)] = row_out;
}
//std::cerr << '\n';
buf[y] = row_out;
//std::cerr << "|\n";
}
// 'pitch' is bytes; 'width' is pixels

View File

@ -141,6 +141,9 @@ void main()
vdp2.reg.TVMD = ( TVMD__DISP | TVMD__LSMD__NON_INTERLACE
| TVMD__VRESO__240 | TVMD__HRESO__NORMAL_320);
// disable all VDP2 backgrounds (e.g: the Sega bios logo)
vdp2.reg.BGON = 0;
// VDP2 User's Manual:
// "When sprite data is in an RGB format, sprite register 0 is selected"
// "When the value of a priority number is 0h, it is read as transparent"
@ -190,10 +193,3 @@ void main()
render();
}
}
extern "C"
void start(void)
{
main();
while (1) {}
}

227
vdp1/cube2.cpp Normal file
View File

@ -0,0 +1,227 @@
#include <stdint.h>
#include "vdp2.h"
#include "vdp1.h"
#include <concepts>
#include "../common/vdp2_func.hpp"
#include "../math/fp.hpp"
#include "../math/vec3.hpp"
#include "../math/mat3x3.hpp"
#include "cos.hpp"
// |--
// |
// |
using vec3 = vec<3, fp16_16>;
using mat3x3 = mat<3, 3, fp16_16>;
static constexpr vec3 vertices[8] = {
{-0.5, -0.5, 0.5}, // top left front
{ 0.5, -0.5, 0.5}, // top right front
{ 0.5, 0.5, 0.5}, // bottom right front
{-0.5, 0.5, 0.5}, // bottom left front
{-0.5, -0.5, -0.5}, // top left back
{ 0.5, -0.5, -0.5}, // top right back
{ 0.5, 0.5, -0.5}, // bottom right back
{-0.5, 0.5, -0.5}, // bottom left back
};
static constexpr uint32_t faces[6][4] = {
{0, 1, 2, 3}, // front clockwise
{5, 4, 7, 6}, // back clockwise
{0, 4, 5, 1}, // top clockwise
{3, 2, 6, 7}, // bottom clockwise
{4, 0, 3, 7}, // left clockwise
{1, 5, 6, 2}, // right clockwise
};
consteval vec3 normal(int32_t ix)
{
const uint32_t * face = faces[ix];
vec3 a = vertices[face[1]] - vertices[face[0]];
vec3 b = vertices[face[3]] - vertices[face[0]];
return vertices[face[0]] + cross(a, b);
}
static constexpr vec3 normals[6] = {
normal(0),
normal(1),
normal(2),
normal(3),
normal(4),
normal(5),
};
struct canvas {
fp16_16 width;
fp16_16 height;
};
constexpr struct canvas canvas = { 240, 240 };
template <typename T>
vec<3, T> viewport_to_canvas(T x, T y)
{
return vec<3, T>(x * canvas.width, y * canvas.height, T(1));
}
template <typename T>
inline constexpr vec<3, T> project_vertex(vec<3, T> const& v)
{
// / (v.z - T(5))
// / (v.z - T(5))
return viewport_to_canvas<T>((v.x * T(0.5) + T(2.0/3.0)),
(v.y * T(0.5) + T(0.5)));
}
constexpr inline uint16_t rgb15(int32_t r, int32_t g, int32_t b)
{
return ((b & 31) << 10) | ((g & 31) << 5) | ((r & 31) << 0);
}
constexpr uint16_t colors[] = {
rgb15(31, 0, 0), // red
rgb15( 0, 31, 0), // green
rgb15( 0, 0, 31), // blue
rgb15(31, 0, 31), // magenta
rgb15( 0, 31, 31), // cyan
rgb15(31, 31, 0), // yellow
};
static int32_t tick = 0;
void
render()
{
tick++;
int ix = 2;
const int rx = tick >> 2;
const mat3x3 rotationX {
1, 0, 0,
0, cos(rx), sin(rx),
0, -sin(rx), cos(rx)
};
const int ry = tick >> 1;
const mat3x3 rotationY {
cos(ry), 0, -sin(ry),
0, 1, 0,
sin(ry), 0, cos(ry)
};
const mat3x3 transform = rotationX * rotationY;
for (int i = 0; i < 6; i++) {
const uint32_t * face = faces[i];
const vec3& origin = transform * vertices[face[0]];
const vec3& normal = (transform * normals[i]);
const vec3& origin_p = project_vertex(origin);
const vec3& normal_p = project_vertex(normal);
const vec3 camera = {2.0/3.0, 0.5, 100};
fp16_16 cull = dot((origin - camera), normal - origin);
if ((cull.value >> 16) < 0) {
vdp1.vram.cmd[ix].CTRL = CTRL__JP__JUMP_NEXT | CTRL__COMM__POLYGON;
vdp1.vram.cmd[ix].LINK = 0;
vdp1.vram.cmd[ix].PMOD = PMOD__ECD | PMOD__SPD;
vdp1.vram.cmd[ix].COLR = COLR__RGB | colors[i];
for (int p = 0; p < 4; p++) {
const vec3& v0 = vertices[face[p]];
const vec3 v1 = transform * v0;
const vec3& v2 = project_vertex(v1);
vdp1.vram.cmd[ix].point[p].X = static_cast<int>(v2.x);
vdp1.vram.cmd[ix].point[p].Y = static_cast<int>(v2.y);
}
ix++;
/*
vdp1.vram.cmd[ix].CTRL = CTRL__JP__JUMP_NEXT | CTRL__COMM__LINE;
vdp1.vram.cmd[ix].LINK = 0;
vdp1.vram.cmd[ix].PMOD = PMOD__ECD | PMOD__SPD;
vdp1.vram.cmd[ix].COLR = COLR__RGB | colors[i];
vdp1.vram.cmd[ix].point[0].X = static_cast<int>(origin_p.x);
vdp1.vram.cmd[ix].point[0].Y = static_cast<int>(origin_p.y);
vdp1.vram.cmd[ix].point[1].X = static_cast<int>(normal_p.x);
vdp1.vram.cmd[ix].point[1].Y = static_cast<int>(normal_p.y);
ix++;
*/
}
}
vdp1.vram.cmd[ix].CTRL = CTRL__END;
}
void main()
{
v_blank_in();
// DISP: Please make sure to change this bit from 0 to 1 during V blank.
vdp2.reg.TVMD = ( TVMD__DISP | TVMD__LSMD__NON_INTERLACE
| TVMD__VRESO__240 | TVMD__HRESO__NORMAL_320);
// disable all VDP2 backgrounds (e.g: the Sega bios logo)
vdp2.reg.BGON = 0;
// VDP2 User's Manual:
// "When sprite data is in an RGB format, sprite register 0 is selected"
// "When the value of a priority number is 0h, it is read as transparent"
//
// The power-on value of PRISA is zero. Set the priority for sprite register 0
// to some number greater than zero, so that the color data is not interpreted
// as "transparent".
vdp2.reg.PRISA = PRISA__S0PRIN(1); // Sprite register 0 Priority Number
/* TVM settings must be performed from the second H-blank IN interrupt after the
V-blank IN interrupt to the H-blank IN interrupt immediately after the V-blank
OUT interrupt. */
// "normal" display resolution, 16 bits per pixel, 512x256 framebuffer
vdp1.reg.TVMR = TVMR__TVM__NORMAL;
// swap framebuffers every 1 cycle; non-interlace
vdp1.reg.FBCR = 0;
// during a framebuffer erase cycle, write the color "black" to each pixel
constexpr uint16_t black = 0x0000;
vdp1.reg.EWDR = black;
// the EWLR/EWRR macros use somewhat nontrivial math for the X coordinates
// erase upper-left coordinate
vdp1.reg.EWLR = EWLR__16BPP_X1(0) | EWLR__Y1(0);
// erase lower-right coordinate
vdp1.reg.EWRR = EWRR__16BPP_X3(319) | EWRR__Y3(239);
vdp1.vram.cmd[0].CTRL = CTRL__JP__JUMP_NEXT | CTRL__COMM__SYSTEM_CLIP_COORDINATES;
vdp1.vram.cmd[0].LINK = 0;
vdp1.vram.cmd[0].XC = 319;
vdp1.vram.cmd[0].YC = 239;
vdp1.vram.cmd[1].CTRL = CTRL__JP__JUMP_NEXT | CTRL__COMM__LOCAL_COORDINATE;
vdp1.vram.cmd[1].LINK = 0;
vdp1.vram.cmd[1].XA = 0;
vdp1.vram.cmd[1].YA = 0;
vdp1.vram.cmd[2].CTRL = CTRL__END;
// start drawing (execute the command list) on every frame
vdp1.reg.PTMR = PTMR__PTM__FRAME_CHANGE;
while (true) {
v_blank_in();
render();
}
}

View File

@ -204,10 +204,3 @@ void main()
// start drawing (execute the command list) on every frame
vdp1.reg.PTMR = PTMR__PTM__FRAME_CHANGE;
}
extern "C"
void start(void)
{
main();
while (1) {}
}

View File

@ -150,10 +150,3 @@ void main()
// start drawing (execute the command list) on every frame
vdp1.reg.PTMR = PTMR__PTM__FRAME_CHANGE;
}
extern "C"
void start(void)
{
main();
while (1) {}
}

View File

@ -173,7 +173,7 @@ void main()
// It appears Kronos does not correctly calculate the color address in the
// VDP1 debugger. Kronos will report FFFC when the actual color table address
// in this example is 7FFE0.
vdp1.vram.cmd[2].COLR = COLR__ADDRESS(color_address); // non-palettized (rgb15) color data
vdp1.vram.cmd[2].COLR = COLR__LOOKUP_TABLE__ADDRESS(color_address); // non-palettized (rgb15) color data
vdp1.vram.cmd[2].SRCA = SRCA(character_address);
vdp1.vram.cmd[2].SIZE = SIZE__X(sprite_width) | SIZE__Y(sprite_height);
vdp1.vram.cmd[2].XA = 100;
@ -193,10 +193,3 @@ void main()
scu.reg.IST = 0;
scu.reg.IMS = ~(IMS__V_BLANK_IN);
}
extern "C"
void start(void)
{
main();
while (1) {}
}

View File

@ -57,7 +57,7 @@ uint32_t character_pattern_table(const uint32_t top)
// `table_size` is in bytes; divide by two to get uint16_t indicies.
uint32_t buf_ix = 0;
for (uint32_t table_ix = 0; table_ix < (table_size / 2); table_ix++) {
uint32_t tmp = buf[buf_ix];
uint16_t tmp = buf[buf_ix];
table[table_ix] = (((tmp >> 8) & 0xff) << 8)
| (((tmp >> 0) & 0xff) << 0);
@ -72,7 +72,7 @@ void main()
{
uint32_t color_address, character_address;
uint32_t top = (sizeof (union vdp1_vram));
uint32_t color_bank = 5; // completely random and arbitrary value
uint32_t color_bank = 0; // completely random and arbitrary value
color_palette(color_bank);
// For color bank color, COLR is concatenated bitwise with pixel data. See
// Figure 6.17 in the VDP1 manual.
@ -146,10 +146,3 @@ void main()
// start drawing (execute the command list) on every frame
vdp1.reg.PTMR = PTMR__PTM__FRAME_CHANGE;
}
extern "C"
void start(void)
{
main();
while (1) {}
}

View File

@ -73,10 +73,3 @@ void main()
// start drawing (execute the command list) on every frame
vdp1.reg.PTMR = PTMR__PTM__FRAME_CHANGE;
}
extern "C"
void start(void)
{
main();
while (1) {}
}

View File

@ -103,10 +103,3 @@ void main()
// start drawing (execute the command list) on every frame
vdp1.reg.PTMR = PTMR__PTM__FRAME_CHANGE;
}
extern "C"
void start(void)
{
main();
while (1) {}
}

View File

@ -0,0 +1,310 @@
#include <stdint.h>
#include "vdp2.h"
#include "vdp1.h"
#include "../common/vdp2_func.hpp"
#include "../common/copy.hpp"
extern void * _mai_data_pal_start __asm("_binary_res_mai_data_pal_start");
extern void * _mai_data_pal_size __asm("_binary_res_mai_data_pal_size");
extern void * _mai00_data_start __asm("_binary_res_mai00_data_start");
extern void * _mai00_data_size __asm("_binary_res_mai00_data_size");
extern void * _haohmaru_data_pal_start __asm("_binary_res_haohmaru_data_pal_start");
extern void * _haohmaru_data_pal_size __asm("_binary_res_haohmaru_data_pal_size");
extern void * _haohmaru_data_start __asm("_binary_res_haohmaru_data_start");
extern void * _haohmaru_data_size __asm("_binary_res_haohmaru_data_size");
extern void * _forest_pattern_start __asm("_binary_res_forest_pattern_start");
extern void * _forest_pattern_size __asm("_binary_res_forest_pattern_size");
extern void * _forest_tile_start __asm("_binary_res_forest_tile_start");
extern void * _forest_tile_size __asm("_binary_res_forest_tile_size");
extern void * _forest_data_pal_start __asm("_binary_res_forest_data_pal_start");
extern void * _forest_data_pal_size __asm("_binary_res_forest_data_pal_size");
inline constexpr uint16_t rgb15(const uint8_t * rgb24)
{
return ((rgb24[2] >> 3) << 10) // blue
| ((rgb24[1] >> 3) << 5) // green
| ((rgb24[0] >> 3) << 0); // red
}
void vdp2_color_palette(const uint32_t color_index_offset,
const uint8_t * buf,
const uint32_t buf_size)
{
uint16_t * table = &vdp2.cram.u16[color_index_offset];
uint32_t buf_ix = 0;
for (uint32_t i = 0; i < (buf_size / 3); i++) {
table[i] = rgb15(&buf[buf_ix]);
buf_ix += 3;
}
}
void vdp2_color_palette()
{
{ /* mai palette */
const uint32_t buf_size = reinterpret_cast<uint32_t>(&_mai_data_pal_size);
const uint8_t * buf = reinterpret_cast<uint8_t*>(&_mai_data_pal_start);
vdp2_color_palette(0, buf, buf_size);
}
{ /* forest palette */
const uint32_t buf_size = reinterpret_cast<uint32_t>(&_forest_data_pal_size);
const uint8_t * buf = reinterpret_cast<uint8_t*>(&_forest_data_pal_start);
vdp2_color_palette(16, buf, buf_size);
}
{ /* haohmaru palette */
const uint32_t buf_size = reinterpret_cast<uint32_t>(&_haohmaru_data_pal_size);
const uint8_t * buf = reinterpret_cast<uint8_t*>(&_haohmaru_data_pal_start);
vdp2_color_palette(32, buf, buf_size);
}
}
uint32_t character_pattern_table(const uint32_t top, const uint32_t * buf, const uint32_t buf_size)
{
// Unlike vdp2 cell format, vdp1 sprites appear to be much more dimensionally
// flexible. The data is interpreted as a row-major packed array, where the
// row/horizontal stride is equal to the sprite width (as configured in the
// draw command). This is identical to how the input palette index data is
// structured, so there is no transformation to do here, only a plain memory
// copy.
// Divide `buf_size` by two because this converts (indexed color) 8 bit pixels
// to 4 bit pixels. Round up to the nearest 0x20 (for an 8000 pixel/8000 byte
// image, this rounding is a no-op).
const uint32_t table_size = ((buf_size / 2) + 0x20 - 1) & (-0x20);
const uint32_t table_address = top - table_size;
uint16_t * table = &vdp1.vram.u16[(table_address / 2)];
// `table_size` is in bytes; divide by two to get uint16_t indicies.
uint32_t buf_ix = 0;
for (uint32_t table_ix = 0; table_ix < (table_size / 2); table_ix++) {
uint32_t tmp = buf[buf_ix];
table[table_ix] = (((tmp >> 24) & 0xf) << 12)
| (((tmp >> 16) & 0xf) << 8 )
| (((tmp >> 8 ) & 0xf) << 4 )
| (((tmp >> 0 ) & 0xf) << 0 );
buf_ix += 1;
}
return table_address;
}
uint32_t forest_cell_data(uint32_t top)
{
const uint32_t buf_size = reinterpret_cast<uint32_t>(&_forest_tile_size);
const uint32_t * buf = reinterpret_cast<uint32_t*>(&_forest_tile_start);
// round to nearest multiple of 32
const uint32_t table_size = ((buf_size) + 0x20 - 1) & (-0x20);
const uint32_t base_address = top - table_size; // in bytes
copy<uint32_t>(&vdp2.vram.u32[base_address / 4], buf, buf_size);
return base_address;
}
void forest_init()
{
/* enable display of NBG0 */
vdp2.reg.BGON = BGON__N0ON | BGON__N0TPON;
/* set character format for NBG0 to palettized 16 color
set enable "cell format" for NBG0
set character size for NBG0 to 1x1 cell */
vdp2.reg.CHCTLA = CHCTLA__N0CHCN__16_COLOR
| CHCTLA__N0BMEN__CELL_FORMAT
| CHCTLA__N0CHSZ__1x1_CELL;
/* plane size */
vdp2.reg.PLSZ = PLSZ__N0PLSZ__1x1;
/* map plane offset
1-word: value of bit 6-0 * 0x2000
2-word: value of bit 5-0 * 0x4000
*/
constexpr int plane_a = 0;
constexpr int plane_a_offset = plane_a * 0x4000;
vdp2.reg.MPOFN = MPOFN__N0MP(0); // bits 8~6
vdp2.reg.MPABN0 = MPABN0__N0MPB(plane_a) | MPABN0__N0MPA(plane_a); // bits 5~0
vdp2.reg.MPCDN0 = MPCDN0__N0MPD(plane_a) | MPCDN0__N0MPC(plane_a); // bits 5~0
uint32_t top = (sizeof (union vdp2_vram));
uint32_t cell_top = top = forest_cell_data(0x080000);
uint32_t pattern_name = cell_top / 32;
/* use 2-word (32-bit) pattern names */
vdp2.reg.PNCN0 = PNCN0__N0PNB__2WORD;
const uint32_t buf_size = reinterpret_cast<uint32_t>(&_forest_pattern_size);
const uint16_t * buf = reinterpret_cast<uint16_t*>(&_forest_pattern_start);
uint32_t * pattern = &vdp2.vram.u32[(plane_a_offset / 4)];
uint32_t x = 0;
uint32_t y = 0;
for (uint32_t i = 0; i < (buf_size / 2); i++) {
pattern[y * 64 + x] = 1 << 16 | (buf[i] + (cell_top / 32));
x++;
if (x >= 40) {
x = 0;
y++;
}
}
}
void main()
{
vdp2.reg.VRSIZE = 0;
vdp2.reg.RAMCTL = 0;
vdp2.reg.CYCA0 = 0xeeee'eeee;
vdp2.reg.CYCA1 = 0xeeee'eeee;
vdp2.reg.CYCB0 = 0xeeee'eeee;
vdp2.reg.CYCB1 = 0xeeee'eeee;
vdp2.reg.VRSIZE = 0;
vdp2_color_palette();
forest_init();
vdp2.reg.RAMCTL = RAMCTL__VRAMD | RAMCTL__VRBMD;
vdp2.reg.CYCA0 = 0x0fff'ffff;
vdp2.reg.CYCA1 = 0xffff'ffff;
vdp2.reg.CYCB0 = 0xffff'ffff;
vdp2.reg.CYCB1 = 0x4fff'ffff;
uint32_t mai_character_address;
uint32_t haohmaru_character_address;
uint32_t top = (sizeof (union vdp1_vram));
{ /* mai */
const uint32_t buf_size = reinterpret_cast<uint32_t>(&_mai00_data_size);
const uint32_t * buf = reinterpret_cast<uint32_t*>(&_mai00_data_start);
top = mai_character_address = character_pattern_table(top, buf, buf_size);
}
{ /* haohmaru */
const uint32_t buf_size = reinterpret_cast<uint32_t>(&_haohmaru_data_size);
const uint32_t * buf = reinterpret_cast<uint32_t*>(&_haohmaru_data_start);
top = haohmaru_character_address = character_pattern_table(top, buf, buf_size);
}
// DISP: Please make sure to change this bit from 0 to 1 during V blank.
vdp2.reg.TVMD = ( TVMD__DISP | TVMD__LSMD__NON_INTERLACE
| TVMD__VRESO__240 | TVMD__HRESO__NORMAL_320);
// VDP2 User's Manual:
// "When sprite data is in an RGB format, sprite register 0 is selected"
// "When the value of a priority number is 0h, it is read as transparent"
//
// From a VDP2 perspective: in VDP1 16-color lookup table mode, VDP1 is still
// sending RGB data to VDP2. This sprite color data as configured in
// `color_lookup_table` from a VDP2 priority perspective uses sprite register 0.
//
// The power-on value of PRISA is zero. Set the priority for sprite register 0
// to some number greater than zero, so that the color data is not interpreted
// as "transparent".
vdp2.reg.PRISA = PRISA__S0PRIN(2) // Sprite register 0 PRIority Number
| PRISA__S1PRIN(1);
vdp2.reg.PRINA = PRINA__N0PRIN(1);
/* TVM settings must be performed from the second H-blank IN interrupt after the
V-blank IN interrupt to the H-blank IN interrupt immediately after the V-blank
OUT interrupt. */
// "normal" display resolution, 16 bits per pixel, 512x256 framebuffer
vdp1.reg.TVMR = TVMR__TVM__NORMAL;
// swap framebuffers every 1 cycle; non-interlace
vdp1.reg.FBCR = 0;
// during a framebuffer erase cycle, write the color "black" to each pixel
constexpr uint16_t black = 0x0000;
vdp1.reg.EWDR = black;
// the EWLR/EWRR macros use somewhat nontrivial math for the X coordinates
// erase upper-left coordinate
vdp1.reg.EWLR = EWLR__16BPP_X1(0) | EWLR__Y1(0);
// erase lower-right coordinate
vdp1.reg.EWRR = EWRR__16BPP_X3(319) | EWRR__Y3(239);
vdp1.vram.cmd[0].CTRL = CTRL__JP__JUMP_NEXT | CTRL__COMM__SYSTEM_CLIP_COORDINATES;
vdp1.vram.cmd[0].LINK = 0;
vdp1.vram.cmd[0].XC = 319;
vdp1.vram.cmd[0].YC = 239;
vdp1.vram.cmd[1].CTRL = CTRL__JP__JUMP_NEXT | CTRL__COMM__LOCAL_COORDINATE;
vdp1.vram.cmd[1].LINK = 0;
vdp1.vram.cmd[1].XA = 0;
vdp1.vram.cmd[1].YA = 0;
vdp1.vram.cmd[2].CTRL = CTRL__JP__JUMP_NEXT | CTRL__COMM__NORMAL_SPRITE;
vdp1.vram.cmd[2].LINK = 0;
// The "end code" is 0xf, which is being used in the mai sprite palette. If
// both transparency and end codes are enabled, it seems there are only 14
// usable colors in the 4-bit color mode.
vdp1.vram.cmd[2].PMOD = PMOD__ECD | PMOD__COLOR_MODE__COLOR_BANK_16;
// It appears Kronos does not correctly calculate the color address in the
// VDP1 debugger. Kronos will report FFFC when the actual color table address
// in this example is 7FFE0.
vdp1.vram.cmd[2].COLR = 0;
vdp1.vram.cmd[2].SRCA = mai_character_address >> 3;
vdp1.vram.cmd[2].SIZE = SIZE__X(72) | SIZE__Y(100);
vdp1.vram.cmd[2].XA = 100;
vdp1.vram.cmd[2].YA = 100;
vdp1.vram.cmd[3].CTRL = CTRL__JP__JUMP_NEXT | CTRL__COMM__NORMAL_SPRITE;
vdp1.vram.cmd[3].LINK = 0;
// The "end code" is 0xf, which is being used in the mai sprite palette. If
// both transparency and end codes are enabled, it seems there are only 14
// usable colors in the 4-bit color mode.
vdp1.vram.cmd[3].PMOD = PMOD__ECD | PMOD__COLOR_MODE__COLOR_BANK_16;
// It appears Kronos does not correctly calculate the color address in the
// VDP1 debugger. Kronos will report FFFC when the actual color table address
// in this example is 7FFE0.
vdp1.vram.cmd[3].COLR = (0b01 << 14) | 32;
vdp1.vram.cmd[3].SRCA = haohmaru_character_address >> 3;
vdp1.vram.cmd[3].SIZE = SIZE__X(104) | SIZE__Y(132);
vdp1.vram.cmd[3].XA = 120;
vdp1.vram.cmd[3].YA = 110;
vdp1.vram.cmd[4].CTRL = CTRL__END;
// start drawing (execute the command list) on every frame
vdp1.reg.PTMR = PTMR__PTM__FRAME_CHANGE;
vdp2.reg.SPCTL = SPCTL__SPCCCS__EQUAL
| SPCTL__SPCCN(1)
| SPCTL__SPTYPE(0);
vdp2.reg.CCCTL = CCCTL__SPCCEN;
int dir = 1;
int ratio = 0;
while (1) {
v_blank_in();
vdp2.reg.CCRSA = CCRSA__S0CCRT(ratio >> 2);
ratio += dir;
if (ratio >= (32 * 4) || ratio < 0) {
dir = -dir;
ratio += dir;
}
}
}

View File

@ -12,7 +12,7 @@
#include <stdint.h>
#include "vdp2.h"
#include "../common/vdp2_func.h"
#include "../common/vdp2_func.hpp"
extern void * _butterfly_data_pal_start __asm("_binary_res_butterfly_data_pal_start");
extern void * _butterfly_data_pal_size __asm("_binary_res_butterfly_data_pal_size");
@ -107,8 +107,8 @@ void main()
constexpr int plane_size = page_size * 1;
vdp2.reg.MPOFN = MPOFN__N0MP(0); // bits 8~6
vdp2.reg.MPABN0 = MPABN0__N0MPB(0) | MPABN0__N0MPA(plane_a); // bits 5~0
vdp2.reg.MPCDN0 = MPABN0__N0MPD(0) | MPABN0__N0MPC(0); // bits 5~0
vdp2.reg.MPABN0 = MPABN0__N0MPB(plane_a) | MPABN0__N0MPA(plane_a); // bits 5~0
vdp2.reg.MPCDN0 = MPCDN0__N0MPD(plane_a) | MPCDN0__N0MPC(plane_a); // bits 5~0
constexpr int cell_size = (8 * 8) * 2; // N0CHCN__2048_COLOR (16-bit)
constexpr int character_size = cell_size * (1 * 1); // N0CHSZ__1x1_CELL

View File

@ -105,7 +105,7 @@ void main()
vdp2.reg.MPOFN = MPOFN__N0MP(0); // bits 8~6
vdp2.reg.MPABN0 = MPABN0__N0MPB(0) | MPABN0__N0MPA(plane_a); // bits 5~0
vdp2.reg.MPCDN0 = MPABN0__N0MPD(0) | MPABN0__N0MPC(0); // bits 5~0
vdp2.reg.MPCDN0 = MPCDN0__N0MPD(0) | MPCDN0__N0MPC(0); // bits 5~0
uint32_t top = (sizeof (union vdp2_vram));
palette_data();
@ -118,7 +118,7 @@ void main()
/* use 2-word (32-bit) pattern names */
vdp2.reg.PNCN0 = PNCN0__N0PNB__2WORD;
fill<uint32_t>(&vdp2.vram.u32[(plane_a_offset / 2)], pattern_name, plane_size);
fill<uint32_t>(&vdp2.vram.u32[(plane_a_offset / 4)], pattern_name, plane_size);
// both 1-word and 2-word have identical behavior; 2-word is enabled to reduce/focus suspicion.
}

View File

@ -248,10 +248,3 @@ void main()
scu.reg.IST = 0;
scu.reg.IMS = ~(IMS__SMPC | IMS__V_BLANK_IN);
}
extern "C"
void start(void)
{
main();
while (1);
}