add DreamcastVideo example

This commit is contained in:
Zack Buhman 2024-12-27 01:22:47 -06:00
parent 89cac52b14
commit de4d8761f1
23 changed files with 414 additions and 18 deletions

View File

@ -7,7 +7,7 @@ CC ?= gcc
ARCH = -m32 ARCH = -m32
CFLAGS += -Wall -Werror -Wfatal-errors -Wno-error=unused-variable -fstack-protector -std=c2x -g CFLAGS += -Wall -Werror -Wfatal-errors -Wno-error=unused-variable -fstack-protector -std=c2x -g
CFLAGS += -DDEBUG CFLAGS += -DDEBUG
CFLAGS += -DDEBUG_PRINT #CFLAGS += -DDEBUG_PRINT
OPT ?= -O0 OPT ?= -O0
DEPFLAGS = -MMD -MP DEPFLAGS = -MMD -MP
@ -21,7 +21,7 @@ main: $(OBJ) $(MAIN_HOSTED_OBJ)
$(CC) $(ARCH) $^ -o $@ $(CC) $(ARCH) $^ -o $@
clean: clean:
rm -f main print_class c/*.o c/*.d rm -f main print_class c/*.o c/*.d *.elf *.bin
.SUFFIXES: .SUFFIXES:
.INTERMEDIATE: .INTERMEDIATE:

View File

@ -1,6 +1,6 @@
all: $(patsubst %.cpp,%.elf,$(wildcard example/*.cpp)) all: $(patsubst %.cpp,%.elf,$(wildcard example/*.cpp))
OPT = -O0 OPT = -O3
MAKEFILE_PATH := $(patsubst %/,%,$(dir $(abspath $(firstword $(MAKEFILE_LIST))))) MAKEFILE_PATH := $(patsubst %/,%,$(dir $(abspath $(firstword $(MAKEFILE_LIST)))))
LIB ?= $(MAKEFILE_PATH)/dreamcast LIB ?= $(MAKEFILE_PATH)/dreamcast
@ -9,6 +9,7 @@ CFLAGS += -DDEBUG
#CFLAGS += -DDEBUG_PRINT #CFLAGS += -DDEBUG_PRINT
CFLAGS += -I$(MAKEFILE_PATH) CFLAGS += -I$(MAKEFILE_PATH)
CFLAGS += -I$(MAKEFILE_PATH)/dreamcast/ CFLAGS += -I$(MAKEFILE_PATH)/dreamcast/
CFLAGS += -Wno-error=strict-aliasing -fno-strict-aliasing
CARCH = -m4-single -ml CARCH = -m4-single -ml
include dreamcast/base.mk include dreamcast/base.mk
@ -47,11 +48,14 @@ LIBGCC_OBJ = \
libgcc/_div_table.o libgcc/_div_table.o
CLASS_FILES = \ CLASS_FILES = \
p/Native.class.o \ p/DreamcastVideo.class.o \
java/lang/String.class.o \ java/lang/String.class.o \
java/lang/Integer.class.o \
java/lang/System.class.o \ java/lang/System.class.o \
java/io/PrintStream.class.o \ java/io/PrintStream.class.o \
java/lang/Object.class.o java/lang/Object.class.o \
sega/dreamcast/holly/Holly.class.o \
java/misc/Memory.class.o
main.elf: LDSCRIPT = $(LIB)/main.lds main.elf: LDSCRIPT = $(LIB)/main.lds
main.elf: $(START_OBJ) $(OBJ) $(MAIN_OBJ) $(MAIN_DREAMCAST_OBJ) $(LIBGCC_OBJ) $(CLASS_FILES) main.elf: $(START_OBJ) $(OBJ) $(MAIN_OBJ) $(MAIN_DREAMCAST_OBJ) $(LIBGCC_OBJ) $(CLASS_FILES)

View File

@ -679,8 +679,8 @@ uint32_t decode_print_instruction(const uint8_t * code, uint32_t pc)
case 132: // iinc case 132: // iinc
{ {
uint32_t index = _u1(&code[pc + 1]); uint32_t index = _u1(&code[pc + 1]);
uint32_t _const = _u1(&code[pc + 2]); int32_t _const = _s1(&code[pc + 2]);
debugf("%4d: iinc %u, %u\n", pc, index, _const); debugf("%4d: iinc %u, %d\n", pc, index, _const);
return pc + 3; return pc + 3;
} }
case 133: // i2l case 133: // i2l
@ -1910,7 +1910,7 @@ void decode_execute_instruction(struct vm * vm, const uint8_t * code, uint32_t p
case 132: // iinc case 132: // iinc
{ {
uint32_t index = _u1(&code[pc + 1]); uint32_t index = _u1(&code[pc + 1]);
uint32_t _const = _u1(&code[pc + 2]); int32_t _const = _s1(&code[pc + 2]);
vm->current_frame->next_pc = pc + 3; vm->current_frame->next_pc = pc + 3;
op_iinc(vm, index, _const); op_iinc(vm, index, _const);
break; break;

View File

@ -140,6 +140,7 @@ void op_bastore(struct vm * vm)
int8_t value = operand_stack_pop_u32(vm->current_frame); int8_t value = operand_stack_pop_u32(vm->current_frame);
int32_t index = operand_stack_pop_u32(vm->current_frame); int32_t index = operand_stack_pop_u32(vm->current_frame);
int32_t * arrayref = (int32_t *)operand_stack_pop_u32(vm->current_frame); int32_t * arrayref = (int32_t *)operand_stack_pop_u32(vm->current_frame);
debugf("%d %d\n", arrayref[0], index);
assert(arrayref[0] > 0 && index < arrayref[0]); assert(arrayref[0] > 0 && index < arrayref[0]);
int8_t * bytearray = (int8_t *)&arrayref[1]; int8_t * bytearray = (int8_t *)&arrayref[1];
bytearray[index] = value; bytearray[index] = value;
@ -1027,9 +1028,9 @@ void op_ifnull(struct vm * vm, int32_t branch)
} }
} }
void op_iinc(struct vm * vm, uint32_t index, uint32_t _const) void op_iinc(struct vm * vm, uint32_t index, int32_t _const)
{ {
uint32_t value = vm->current_frame->local_variable[index]; int32_t value = vm->current_frame->local_variable[index];
value += _const; value += _const;
vm->current_frame->local_variable[index] = value; vm->current_frame->local_variable[index] = value;
} }
@ -1227,6 +1228,11 @@ void op_istore_0(struct vm * vm)
void op_istore_1(struct vm * vm) void op_istore_1(struct vm * vm)
{ {
uint32_t value = operand_stack_pop_u32(vm->current_frame); uint32_t value = operand_stack_pop_u32(vm->current_frame);
debugf("istore_1: %d\n", value);
if (value == 1) {
debugf("istore1_1 == 1: %d\n", value);
(void)(value);
}
vm->current_frame->local_variable[1] = value; vm->current_frame->local_variable[1] = value;
} }

View File

@ -125,7 +125,7 @@ void op_iflt(struct vm * vm, int32_t branch);
void op_ifne(struct vm * vm, int32_t branch); void op_ifne(struct vm * vm, int32_t branch);
void op_ifnonnull(struct vm * vm, int32_t branch); void op_ifnonnull(struct vm * vm, int32_t branch);
void op_ifnull(struct vm * vm, int32_t branch); void op_ifnull(struct vm * vm, int32_t branch);
void op_iinc(struct vm * vm, uint32_t index, uint32_t _const); void op_iinc(struct vm * vm, uint32_t index, int32_t _const);
void op_iload(struct vm * vm, uint32_t index); void op_iload(struct vm * vm, uint32_t index);
void op_iload_0(struct vm * vm); void op_iload_0(struct vm * vm);
void op_iload_1(struct vm * vm); void op_iload_1(struct vm * vm);

View File

@ -7,26 +7,32 @@
#include "sh7091_scif.h" #include "sh7091_scif.h"
#include "p/Native.class.h" #include "p/DreamcastVideo.class.h"
#include "java/lang/String.class.h" #include "java/lang/String.class.h"
#include "java/lang/Integer.class.h"
#include "java/lang/System.class.h" #include "java/lang/System.class.h"
#include "java/io/PrintStream.class.h" #include "java/io/PrintStream.class.h"
#include "java/lang/Object.class.h" #include "java/lang/Object.class.h"
#include "sega/dreamcast/holly/Holly.class.h"
#include "java/misc/Memory.class.h"
void main() void main()
{ {
scif_init(0); scif_init(0);
const uint8_t * class_file_buffers[] = { const uint8_t * class_file_buffers[] = {
(const uint8_t *)&_binary_p_Native_class_start, (const uint8_t *)&_binary_p_DreamcastVideo_class_start,
(const uint8_t *)&_binary_java_lang_String_class_start, (const uint8_t *)&_binary_java_lang_String_class_start,
(const uint8_t *)&_binary_java_lang_Integer_class_start,
(const uint8_t *)&_binary_java_lang_System_class_start, (const uint8_t *)&_binary_java_lang_System_class_start,
(const uint8_t *)&_binary_java_io_PrintStream_class_start, (const uint8_t *)&_binary_java_io_PrintStream_class_start,
(const uint8_t *)&_binary_java_lang_Object_class_start, (const uint8_t *)&_binary_java_lang_Object_class_start,
(const uint8_t *)&_binary_sega_dreamcast_holly_Holly_class_start,
(const uint8_t *)&_binary_java_misc_Memory_class_start,
}; };
int class_file_buffers_length = (sizeof (class_file_buffers)) / (sizeof (class_file_buffers[0])); int class_file_buffers_length = (sizeof (class_file_buffers)) / (sizeof (class_file_buffers[0]));
const uint8_t * main_class = (const uint8_t *)"p/Native"; const uint8_t * main_class = (const uint8_t *)"p/DreamcastVideo";
int main_class_length = string_length((const char *)main_class); int main_class_length = string_length((const char *)main_class);
int class_hash_table_length; int class_hash_table_length;

View File

@ -49,7 +49,7 @@ def parse_arguments(types, arguments):
assert size in {1, 2, 4} assert size in {1, 2, 4}
assert signed is not None assert signed is not None
if name in {"byte", "branch"}: if name in {"byte", "branch", "_const"}:
assert signed == True, name assert signed == True, name
else: else:
assert signed == False, name assert signed == False, name

3
generate.sh Normal file
View File

@ -0,0 +1,3 @@
python gen_decoder.py > c/decode.inc.c
python regs/holly.py ../dreamcast/regs/holly.csv > sega/dreamcast/holly/Holly.java

View File

@ -1,7 +1,7 @@
%.class: %.java #%.class: %.java
javac $< # javac $<
java/%.class: java/%.java %.class: %.java
javac --source 8 --target 8 --boot-class-path . $< javac --source 8 --target 8 --boot-class-path . $<
OBJ = \ OBJ = \

15
java/lang/Integer.class.h Normal file
View File

@ -0,0 +1,15 @@
#pragma once
#include <stdint.h>
#ifdef __cplusplus
extern "C" {
#endif
extern uint32_t _binary_java_lang_Integer_class_start __asm("_binary_java_lang_Integer_class_start");
extern uint32_t _binary_java_lang_Integer_class_end __asm("_binary_java_lang_Integer_class_end");
extern uint32_t _binary_java_lang_Integer_class_size __asm("_binary_java_lang_Integer_class_size");
#ifdef __cplusplus
}
#endif

67
java/lang/Integer.java Normal file
View File

@ -0,0 +1,67 @@
package java.lang;
public class Integer {
public static final short[] DIGITS;
static {
short[] digits = new short[100];
for (int h = 0; h < 10; h++) {
int high = (h + '0') << 8;
for (int l = 0; l < 10; l++) {
int low = l + '0';
digits[h * 10 + l] = (short)(high | low);
}
}
DIGITS = digits;
}
public static int stringSize(int n) {
int sign_digits = 0;
if (n < 0) {
sign_digits = 1;
n = -n;
}
int digits = 1;
int cmp = 10;
for (int i = 0; i < 9; i++) {
if (n < cmp)
return digits + sign_digits;
digits += 1;
cmp *= 10;
}
return 10 + sign_digits;
}
public static String toString(int n) {
int pos = stringSize(n);
byte[] buf = new byte[pos];
buf[0] = (char)'0';
boolean sign = n < 0;
if (sign) {
n = -n;
}
while (n >= 10) {
int div = n / 100;
pos -= 2;
int mod = n - (div * 100);
short digits = DIGITS[mod];
buf[pos] = (byte)(digits >> 8);
buf[pos+1] = (byte)(digits);
n = div;
}
if (n != 0) {
pos -= 1;
buf[pos] = (byte)('0' + n);
}
if (sign) {
pos -= 1;
buf[pos] = (byte)('-');
}
return new String(buf);
}
}

15
java/misc/Memory.class.h Normal file
View File

@ -0,0 +1,15 @@
#pragma once
#include <stdint.h>
#ifdef __cplusplus
extern "C" {
#endif
extern uint32_t _binary_java_misc_Memory_class_start __asm("_binary_java_misc_Memory_class_start");
extern uint32_t _binary_java_misc_Memory_class_end __asm("_binary_java_misc_Memory_class_end");
extern uint32_t _binary_java_misc_Memory_class_size __asm("_binary_java_misc_Memory_class_size");
#ifdef __cplusplus
}
#endif

BIN
main.bin Executable file

Binary file not shown.

Binary file not shown.

15
p/DreamcastVideo.class.h Normal file
View File

@ -0,0 +1,15 @@
#pragma once
#include <stdint.h>
#ifdef __cplusplus
extern "C" {
#endif
extern uint32_t _binary_p_DreamcastVideo_class_start __asm("_binary_p_DreamcastVideo_class_start");
extern uint32_t _binary_p_DreamcastVideo_class_end __asm("_binary_p_DreamcastVideo_class_end");
extern uint32_t _binary_p_DreamcastVideo_class_size __asm("_binary_p_DreamcastVideo_class_size");
#ifdef __cplusplus
}
#endif

23
p/DreamcastVideo.java Normal file
View File

@ -0,0 +1,23 @@
package p;
import sega.dreamcast.holly.Holly;
import java.misc.Memory;
class DreamcastVideo {
public static void main() {
System.out.print("FB_R_SOF1: ");
int a = Memory.getU4(Holly.FB_R_SOF1);
System.out.println(Integer.toString(Holly.FB_R_SOF1));
System.out.println(Integer.toString(a));
System.out.print("FB_R_CTRL: ");
int b = Memory.getU4(Holly.FB_R_CTRL);
System.out.println(Integer.toString(b));
System.out.println();
int red = 0x000077ff;
int fb = 0xa5000000 + a;
for (int i = 0; i < 640 * 480; i++) {
Memory.putU4(fb + (i * 4), red);
}
}
}

4
regs/Foo.java Normal file
View File

@ -0,0 +1,4 @@
class Holly {
public static final int
}

27
regs/csv_input.py Normal file
View File

@ -0,0 +1,27 @@
import csv
def as_dict(header, row0):
row = [s.strip() for s in row0]
return dict(zip(header, row))
def read_input(filename):
with open(filename) as f:
reader = csv.reader(f, delimiter=",", quotechar='"')
header, *rows = reader
rows = [
as_dict(header, row)
for row in rows
if "".join(map(str, row)).strip()
]
return rows
def read_input_headerless(filename):
with open(filename) as f:
reader = csv.reader(f, delimiter=",", quotechar='"')
rows = [
[s.strip() for s in row]
for row in reader
]
return rows

42
regs/generate.py Normal file
View File

@ -0,0 +1,42 @@
import io
def should_autonewline(line):
return (
"static_assert" not in line
and "extern" not in line
and (len(line.split()) < 2 or line.split()[1] != '=') # hacky; meh
)
def _render(out, lines, indent_length):
indent = " "
level = 0
namespace = 0
for l in lines:
if l and (l[0] == "}" or l[0] == ")"):
level -= indent_length
if level < 0:
assert namespace >= 0
namespace -= 1
level = 0
if len(l) == 0:
out.write("\n")
else:
out.write(indent * level + l + "\n")
if l and (l[-1] == "{" or l[-1] == "("):
if l.startswith("namespace"):
namespace += 1
else:
level += indent_length
if level == 0 and l and l[-1] == ";":
if should_autonewline(l):
out.write("\n")
return out
def renderer(indent_length=2):
out = io.StringIO()
def render(lines):
return _render(out, lines, indent_length)
return render, out

24
regs/holly.py Normal file
View File

@ -0,0 +1,24 @@
from register import parse_row, group_by_block
from csv_input import read_input
import sys
from generate import renderer
def generate_register(base_address, register):
yield f"public static final int {register.name} = {hex(base_address + register.address)};"
def generate_classes(package_name, base_address, blocks):
yield f"package sega.dreamcast.{package_name};"
yield ""
for block, registers in blocks:
yield f"public class {block.capitalize()} {{"
for register in registers:
yield from generate_register(base_address, register)
yield "}"
if __name__ == "__main__":
rows = read_input(sys.argv[1])
blocks = group_by_block(map(parse_row, rows))
render, out = renderer(indent_length=4)
holly_base_address = 0xa05f8000
render(generate_classes("holly", holly_base_address, blocks))
sys.stdout.write(out.getvalue())

54
regs/register.py Normal file
View File

@ -0,0 +1,54 @@
from dataclasses import dataclass
from collections import defaultdict
@dataclass
class Register:
block: str
address: int
size: int
name: str
rw: set[str]
description: str
def parse_address(row):
address = int(row["address"], 16)
if "offset" in row:
offset = int(row["offset"], 16)
address += offset << 16
return address
def parse_rw(row):
s = set(row["r/w"])
assert (s & {"R", "W"}) == s
return s
blocks_names = set()
def parse_block_name(row):
global blocks_names
block = row["block"]
name = row["name"]
key = (block, name)
assert key not in blocks_names
return block, name
def parse_row(row):
block, name = parse_block_name(row)
return Register(
block=block,
address=parse_address(row),
size=int(row["size"]),
name=name,
rw=parse_rw(row),
description=row["description"]
)
def group_by_block(registers):
groups = defaultdict(list)
for register in registers:
groups[register.block].append(register)
sorted_groups = []
for block, registers in groups.items():
sorted_groups.append(
(block, list(sorted(registers, key=lambda r: r.address))))
return list(sorted(sorted_groups, key=lambda g: g[1][0].address))

View File

@ -0,0 +1,15 @@
#pragma once
#include <stdint.h>
#ifdef __cplusplus
extern "C" {
#endif
extern uint32_t _binary_sega_dreamcast_holly_Holly_class_start __asm("_binary_sega_dreamcast_holly_Holly_class_start");
extern uint32_t _binary_sega_dreamcast_holly_Holly_class_end __asm("_binary_sega_dreamcast_holly_Holly_class_end");
extern uint32_t _binary_sega_dreamcast_holly_Holly_class_size __asm("_binary_sega_dreamcast_holly_Holly_class_size");
#ifdef __cplusplus
}
#endif

View File

@ -0,0 +1,76 @@
package sega.dreamcast.holly;
public class Holly {
public static final int ID = 0xa05f8000;
public static final int REVISION = 0xa05f8004;
public static final int SOFTRESET = 0xa05f8008;
public static final int STARTRENDER = 0xa05f8014;
public static final int TEST_SELECT = 0xa05f8018;
public static final int PARAM_BASE = 0xa05f8020;
public static final int REGION_BASE = 0xa05f802c;
public static final int SPAN_SORT_CFG = 0xa05f8030;
public static final int VO_BORDER_COL = 0xa05f8040;
public static final int FB_R_CTRL = 0xa05f8044;
public static final int FB_W_CTRL = 0xa05f8048;
public static final int FB_W_LINESTRIDE = 0xa05f804c;
public static final int FB_R_SOF1 = 0xa05f8050;
public static final int FB_R_SOF2 = 0xa05f8054;
public static final int FB_R_SIZE = 0xa05f805c;
public static final int FB_W_SOF1 = 0xa05f8060;
public static final int FB_W_SOF2 = 0xa05f8064;
public static final int FB_X_CLIP = 0xa05f8068;
public static final int FB_Y_CLIP = 0xa05f806c;
public static final int FPU_SHAD_SCALE = 0xa05f8074;
public static final int FPU_CULL_VAL = 0xa05f8078;
public static final int FPU_PARAM_CFG = 0xa05f807c;
public static final int HALF_OFFSET = 0xa05f8080;
public static final int FPU_PERP_VAL = 0xa05f8084;
public static final int ISP_BACKGND_D = 0xa05f8088;
public static final int ISP_BACKGND_T = 0xa05f808c;
public static final int ISP_FEED_CFG = 0xa05f8098;
public static final int SDRAM_REFRESH = 0xa05f80a0;
public static final int SDRAM_ARB_CFG = 0xa05f80a4;
public static final int SDRAM_CFG = 0xa05f80a8;
public static final int FOG_COL_RAM = 0xa05f80b0;
public static final int FOG_COL_VERT = 0xa05f80b4;
public static final int FOG_DENSITY = 0xa05f80b8;
public static final int FOG_CLAMP_MAX = 0xa05f80bc;
public static final int FOG_CLAMP_MIN = 0xa05f80c0;
public static final int SPG_TRIGGER_POS = 0xa05f80c4;
public static final int SPG_HBLANK_INT = 0xa05f80c8;
public static final int SPG_VBLANK_INT = 0xa05f80cc;
public static final int SPG_CONTROL = 0xa05f80d0;
public static final int SPG_HBLANK = 0xa05f80d4;
public static final int SPG_LOAD = 0xa05f80d8;
public static final int SPG_VBLANK = 0xa05f80dc;
public static final int SPG_WIDTH = 0xa05f80e0;
public static final int TEXT_CONTROL = 0xa05f80e4;
public static final int VO_CONTROL = 0xa05f80e8;
public static final int VO_STARTX = 0xa05f80ec;
public static final int VO_STARTY = 0xa05f80f0;
public static final int SCALER_CTL = 0xa05f80f4;
public static final int PAL_RAM_CTRL = 0xa05f8108;
public static final int SPG_STATUS = 0xa05f810c;
public static final int FB_BURSTCTRL = 0xa05f8110;
public static final int FB_C_SOF = 0xa05f8114;
public static final int Y_COEFF = 0xa05f8118;
public static final int PT_ALPHA_REF = 0xa05f811c;
public static final int TA_OL_BASE = 0xa05f8124;
public static final int TA_ISP_BASE = 0xa05f8128;
public static final int TA_OL_LIMIT = 0xa05f812c;
public static final int TA_ISP_LIMIT = 0xa05f8130;
public static final int TA_NEXT_OPB = 0xa05f8134;
public static final int TA_ITP_CURRENT = 0xa05f8138;
public static final int TA_GLOB_TILE_CLIP = 0xa05f813c;
public static final int TA_ALLOC_CTRL = 0xa05f8140;
public static final int TA_LIST_INIT = 0xa05f8144;
public static final int TA_YUV_TEX_BASE = 0xa05f8148;
public static final int TA_YUV_TEX_CTRL = 0xa05f814c;
public static final int TA_YUV_TEX_CNT = 0xa05f8150;
public static final int TA_LIST_CONT = 0xa05f8160;
public static final int TA_NEXT_OPB_INIT = 0xa05f8164;
public static final int FOG_TABLE = 0xa05f8200;
public static final int TA_OL_POINTERS = 0xa05f8600;
public static final int PALETTE_RAM = 0xa05f9000;
}