This commit is contained in:
Zack Buhman 2025-02-25 02:33:41 -06:00
commit d2853464c3
5 changed files with 399 additions and 0 deletions

21
.gitignore vendored Executable file
View File

@ -0,0 +1,21 @@
*.pyc
__pycache__
.~*
*.BIN
*.bin
*.elf
*.d
*.iso
*.cdi
*.o
*.out
*.gch
scramble
cdi4dc
tools/ttf_outline
tools/ttf_bitmap
tools/ftdi_transfer
k_means_vq
*.blend1
*.scramble
*.FCStd1

41
Makefile Normal file
View File

@ -0,0 +1,41 @@
all: main.elf
OPT = -O2
CSTD = -std=gnu11
MAKEFILE_PATH := $(patsubst %/,%,$(dir $(abspath $(firstword $(MAKEFILE_LIST)))))
LIB ?= $(MAKEFILE_PATH)/dreamcast
CFLAGS += -D__dreamcast__
CFLAGS += -I$(MAKEFILE_PATH)/c
CFLAGS += -I$(MAKEFILE_PATH)/dreamcast
CFLAGS += -I$(MAKEFILE_PATH)/
CFLAGS += -Wno-error=strict-aliasing -fno-strict-aliasing
CARCH = -m4-single -ml
include dreamcast/base.mk
include dreamcast/common.mk
include dreamcast/headers.mk
include dreamcast/ip.mk
include main.mk
%.class.o: %.class
$(BUILD_BINARY_O)
%.class.h: %.class
$(BUILD_BINARY_H)
libgcc/%.o: $(LIBGCC)
@mkdir -p $(dir $@)
ar x --output $(dir $@) $(LIBGCC) $(notdir $@)
sh4-none-elf-objdump -t $@ \
| grep -E '[.]hidden' \
| grep -vE 'UND' \
| cut -d' ' -f10 \
| xargs rebind --visibility=default $@
LIBGCC_OBJ =
main.elf: LDSCRIPT = $(LIB)/main.lds
main.elf: $(START_OBJ) $(MAIN_OBJ) $(LIBGCC_OBJ)

1
dreamcast Symbolic link
View File

@ -0,0 +1 @@
../dreamcast

8
main.mk Normal file
View File

@ -0,0 +1,8 @@
MAIN_OBJ = \
$(LIB)/holly/core.o \
$(LIB)/holly/region_array.o \
$(LIB)/holly/background.o \
$(LIB)/holly/ta_fifo_polygon_converter.o \
$(LIB)/holly/video_output.o \
$(LIB)/sh7091/serial.o \
src/main.o

328
src/main.cpp Normal file
View File

@ -0,0 +1,328 @@
#include <stdint.h>
#include "holly/background.hpp"
#include "holly/core.hpp"
#include "holly/core_bits.hpp"
#include "holly/holly.hpp"
#include "holly/isp_tsp.hpp"
#include "holly/region_array.hpp"
#include "holly/ta_bits.hpp"
#include "holly/ta_fifo_polygon_converter.hpp"
#include "holly/ta_global_parameter.hpp"
#include "holly/ta_parameter.hpp"
#include "holly/ta_vertex_parameter.hpp"
#include "holly/texture_memory_alloc3.hpp"
#include "holly/video_output.hpp"
#include "sh7091/sh7091.hpp"
#include "sh7091/sh7091_bits.hpp"
#include "sh7091/serial.hpp"
#include "sh7091/vbr.hpp"
#include "systembus.hpp"
#include "systembus_bits.hpp"
#include "memorymap.hpp"
#include "math/vec2.hpp"
#include "math/vec3.hpp"
#include "math/vec4.hpp"
#include "math/mat4x4.hpp"
using vec2 = vec<2, float>;
using vec3 = vec<3, float>;
using vec4 = vec<4, float>;
using mat4x4 = mat<4, 4, float>;
static inline __attribute__((always_inline)) void print_exception()
{
serial::string("expevt ");
serial::integer<uint16_t>(sh7091.CCN.EXPEVT);
serial::string("intevt ");
serial::integer<uint16_t>(sh7091.CCN.INTEVT);
serial::string("tra ");
serial::integer<uint16_t>(sh7091.CCN.TRA);
uint32_t spc;
uint32_t ssr;
asm volatile ("stc spc,%0" : "=r" (spc));
asm volatile ("stc ssr,%0" : "=r" (ssr));
serial::string("spc ");
serial::integer(spc);
serial::string("ssr ");
serial::integer(ssr);
}
// "dreamcast/sh7091/vbr.hpp" must be included prior to defining any of
// vbr100/vbr400/vbr600
void vbr100()
{
serial::string("vbr100\n");
print_exception();
while (1);
}
void vbr400()
{
serial::string("vbr400");
print_exception();
while (1);
}
static int render_done = 0;
void vbr600()
{
if (sh7091.CCN.EXPEVT == 0 && sh7091.CCN.INTEVT == 0x320) {
// read Holly "normal" interrupt status
uint32_t istnrm = system.ISTNRM;
// read Holly "error" interrupt status
uint32_t isterr = system.ISTERR;
if (isterr) {
serial::string("isterr: ");
serial::integer<uint32_t>(system.ISTERR);
}
if (istnrm & istnrm::end_of_render_tsp) {
system.ISTNRM = istnrm::end_of_render_tsp
| istnrm::end_of_render_isp
| istnrm::end_of_render_video;
render_done = 1;
return;
}
}
serial::string("vbr600");
print_exception();
while (1);
}
void interrupt_init()
{
// disable all Holly interrupts
system.IML2NRM = 0;
system.IML2ERR = 0;
system.IML2EXT = 0;
system.IML4NRM = 0;
system.IML4ERR = 0;
system.IML4EXT = 0;
system.IML6NRM = 0;
system.IML6ERR = 0;
system.IML6EXT = 0;
// reset Holly interrupt status
system.ISTERR = 0xffffffff;
system.ISTNRM = 0xffffffff;
// vbr100/vbr400/vbr600 will be called during interrupts/exceptions as a
// consequence of setting vbr to __vbr_link_start.
// See also:
// - dreamcast/common.lds
// - dreamcast/symbols.lds
// - dreamcast/sh7091/vbr.hpp
uint32_t vbr = reinterpret_cast<uint32_t>(&__vbr_link_start) - 0x100;
asm volatile ("ldc %0,vbr" : : "r" (vbr));
uint32_t sr;
asm volatile ("stc sr,%0" : "=r" (sr));
// unBLock interrupts/exceptions
// unmask all interrupts
sr &= ~sh::sr::bl; // BL
sr &= ~sh::sr::imask(15); // imask
asm volatile ("ldc %0,sr" : : "r" (sr));
}
void global_polygon_type_0(ta_parameter_writer& writer)
{
// this is the simplest possible polygon configuration:
// - untextured
// - packed 32-bit color
// - not gouraud shaded
// - no TA color calculation
const uint32_t parameter_control_word = para_control::para_type::polygon_or_modifier_volume
| para_control::list_type::opaque
| obj_control::col_type::packed_color
;
const uint32_t isp_tsp_instruction_word = isp_tsp_instruction_word::depth_compare_mode::greater
| isp_tsp_instruction_word::culling_mode::no_culling;
const uint32_t tsp_instruction_word = tsp_instruction_word::fog_control::no_fog
| tsp_instruction_word::src_alpha_instr::one
| tsp_instruction_word::dst_alpha_instr::zero
;
const uint32_t texture_control_word = 0;
writer.append<ta_global_parameter::polygon_type_0>() =
ta_global_parameter::polygon_type_0(parameter_control_word,
isp_tsp_instruction_word,
tsp_instruction_word,
texture_control_word,
0, // data_size_for_sort_dma
0 // next_address_for_sort_dma
);
}
static inline void render_triangle(ta_parameter_writer& writer,
uint32_t base_color,
vec3 ap,
vec3 bp,
vec3 cp)
{
writer.append<ta_vertex_parameter::polygon_type_0>() =
ta_vertex_parameter::polygon_type_0(polygon_vertex_parameter_control_word(false),
ap.x, ap.y, ap.z,
base_color
);
writer.append<ta_vertex_parameter::polygon_type_0>() =
ta_vertex_parameter::polygon_type_0(polygon_vertex_parameter_control_word(false),
bp.x, bp.y, bp.z,
base_color
);
writer.append<ta_vertex_parameter::polygon_type_0>() =
ta_vertex_parameter::polygon_type_0(polygon_vertex_parameter_control_word(true), // end of triangle strip
cp.x, cp.y, cp.z,
base_color
);
}
void transfer_scene(ta_parameter_writer& writer)
{
uint32_t base_color = 0xff55aa00;
vec3 ap = { 320.000f, 50.000f, 0.1f};
vec3 bp = { 539.393f, 430.000f, 0.1f};
vec3 cp = { 100.607f, 430.000f, 0.1f};
// global_polygon_type_N only needs to be sent if the polygon type /
// parameters change. E.g: multiple triangles that use the same texture and
// shading configuration only need a single global_polygon_type_N
global_polygon_type_0(writer);
// render_triangle may be called multiple times
render_triangle(writer,
base_color,
ap,
bp,
cp);
// end of opaque list
writer.append<ta_global_parameter::end_of_list>() =
ta_global_parameter::end_of_list(para_control::para_type::end_of_list);
}
uint8_t __attribute__((aligned(32))) ta_parameter_buf[1024 * 1024];
void main()
{
serial::init(0);
holly.SOFTRESET = softreset::pipeline_soft_reset
| softreset::ta_soft_reset;
holly.SOFTRESET = 0;
core_init();
interrupt_init();
// enable end_of_render_tsp interrupt at Holly level 6
//
// this corresponds to (sh7091.CCN.EXPEVT == 0 && sh7091.CCN.INTEVT == 0x320)
// via vbr600 from the CPU's perspective.
system.IML6NRM = istnrm::end_of_render_tsp;
const int framebuffer_width = 640;
const int framebuffer_height = 480;
const int tile_width = framebuffer_width / 32;
const int tile_height = framebuffer_height / 32;
const uint32_t ta_alloc = ta_alloc_ctrl::pt_opb::no_list
| ta_alloc_ctrl::tm_opb::no_list
| ta_alloc_ctrl::t_opb::no_list
| ta_alloc_ctrl::om_opb::no_list
| ta_alloc_ctrl::o_opb::_16x4byte;
const int opb_size_length = 1;
const struct opb_size opb_size[opb_size_length] = {
{
.opaque = 16 * 4,
.opaque_modifier = 0,
.translucent = 0,
.translucent_modifier = 0,
.punch_through = 0
}
};
// Create a "region array"; this creates pointers in texture memory for each
// of the 300 32×32px tiles in the 640x480px render output.
region_array_multipass(tile_width,
tile_height,
opb_size,
opb_size_length,
texture_memory_alloc.region_array[0].start,
texture_memory_alloc.object_list[0].start);
// Create a "background" ISP/TSP parameter at address
// texture_memory_alloc.background[0].start in texture memory.
// This is the "background color" shown behind all other polygons.
const uint32_t background_color = 0xff202040;
background_parameter2(texture_memory_alloc.background[0].start,
background_color);
// ta_parameter_writer is used to prepare a buffer of "ta parameters"/"drawing
// commands" for submission to the TA via DMA.
ta_parameter_writer writer = ta_parameter_writer(ta_parameter_buf);
// set the Dreamcast video output to "VGA" (with several other defaults
// including a 640x480 RGB565 framebuffer).
video_output::set_mode_vga();
while (1) {
// ta_polygon_converter_init2 must be called each frame, prior to sending
// data to the TA. This re-initializes TA internal registers and
// offsets. The TA reads from its input (in this case given via
// DMA/ta_polygon_converter_transfer), and writes the result to texture
// memory.
ta_polygon_converter_init2(texture_memory_alloc.isp_tsp_parameters[0].start,
texture_memory_alloc.isp_tsp_parameters[0].end,
texture_memory_alloc.object_list[0].start,
texture_memory_alloc.object_list[0].end,
opb_size[0].total(),
ta_alloc,
tile_width,
tile_height);
// reset the ta_parameter_writer
writer.offset = 0;
// perform all vertex/scene calculations, storing the result in System Memory
transfer_scene(writer);
// write any areas of writer.buf that only exist in SH4 cache back to System Memory
ta_polygon_converter_writeback(writer.buf, writer.offset);
// DMA transfer from System Memory to the TA FIFO
ta_polygon_converter_transfer(writer.buf, writer.offset);
// wait for the TA to finish processing all TA commands
ta_wait_opaque_list();
// render_done flag: see vbr600
render_done = 0;
core_start_render2(texture_memory_alloc.region_array[0].start,
texture_memory_alloc.isp_tsp_parameters[0].start,
texture_memory_alloc.background[0].start,
texture_memory_alloc.framebuffer[0].start,
framebuffer_width);
// wait for render completion
while (render_done == 0) {
asm volatile ("nop");
};
// wait for vsync
while (!spg_status::vsync(holly.SPG_STATUS));
holly.FB_R_SOF1 = texture_memory_alloc.framebuffer[0].start;
while (spg_status::vsync(holly.SPG_STATUS));
}
}