diff --git a/Makefile b/Makefile index fb8cc25..923ae72 100644 --- a/Makefile +++ b/Makefile @@ -7,7 +7,7 @@ CC ?= gcc ARCH = -m32 CFLAGS += -Wall -Werror -Wfatal-errors -Wno-error=unused-variable -fstack-protector -std=c2x -g CFLAGS += -DDEBUG -CFLAGS += -DDEBUG_PRINT +#CFLAGS += -DDEBUG_PRINT OPT ?= -O0 DEPFLAGS = -MMD -MP @@ -21,7 +21,7 @@ main: $(OBJ) $(MAIN_HOSTED_OBJ) $(CC) $(ARCH) $^ -o $@ clean: - rm -f main print_class c/*.o c/*.d + rm -f main print_class c/*.o c/*.d *.elf *.bin .SUFFIXES: .INTERMEDIATE: diff --git a/Makefile.dreamcast.mk b/Makefile.dreamcast.mk index 0e32c28..304bb94 100644 --- a/Makefile.dreamcast.mk +++ b/Makefile.dreamcast.mk @@ -1,6 +1,6 @@ all: $(patsubst %.cpp,%.elf,$(wildcard example/*.cpp)) -OPT = -O0 +OPT = -O3 MAKEFILE_PATH := $(patsubst %/,%,$(dir $(abspath $(firstword $(MAKEFILE_LIST))))) LIB ?= $(MAKEFILE_PATH)/dreamcast @@ -9,6 +9,7 @@ CFLAGS += -DDEBUG #CFLAGS += -DDEBUG_PRINT CFLAGS += -I$(MAKEFILE_PATH) CFLAGS += -I$(MAKEFILE_PATH)/dreamcast/ +CFLAGS += -Wno-error=strict-aliasing -fno-strict-aliasing CARCH = -m4-single -ml include dreamcast/base.mk @@ -47,11 +48,14 @@ LIBGCC_OBJ = \ libgcc/_div_table.o CLASS_FILES = \ - p/Native.class.o \ + p/DreamcastVideo.class.o \ java/lang/String.class.o \ + java/lang/Integer.class.o \ java/lang/System.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: $(START_OBJ) $(OBJ) $(MAIN_OBJ) $(MAIN_DREAMCAST_OBJ) $(LIBGCC_OBJ) $(CLASS_FILES) diff --git a/c/decode.inc.c b/c/decode.inc.c index a347c7c..2823d7d 100644 --- a/c/decode.inc.c +++ b/c/decode.inc.c @@ -679,8 +679,8 @@ uint32_t decode_print_instruction(const uint8_t * code, uint32_t pc) case 132: // iinc { uint32_t index = _u1(&code[pc + 1]); - uint32_t _const = _u1(&code[pc + 2]); - debugf("%4d: iinc %u, %u\n", pc, index, _const); + int32_t _const = _s1(&code[pc + 2]); + debugf("%4d: iinc %u, %d\n", pc, index, _const); return pc + 3; } case 133: // i2l @@ -1910,7 +1910,7 @@ void decode_execute_instruction(struct vm * vm, const uint8_t * code, uint32_t p case 132: // iinc { 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; op_iinc(vm, index, _const); break; diff --git a/c/execute.c b/c/execute.c index 5b43ee1..4e51114 100644 --- a/c/execute.c +++ b/c/execute.c @@ -140,6 +140,7 @@ void op_bastore(struct vm * vm) int8_t value = 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); + debugf("%d %d\n", arrayref[0], index); assert(arrayref[0] > 0 && index < arrayref[0]); int8_t * bytearray = (int8_t *)&arrayref[1]; 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; 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) { 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; } diff --git a/c/execute.h b/c/execute.h index b1adedd..4bf72a3 100644 --- a/c/execute.h +++ b/c/execute.h @@ -125,7 +125,7 @@ void op_iflt(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_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_0(struct vm * vm); void op_iload_1(struct vm * vm); diff --git a/c/main_dreamcast.c b/c/main_dreamcast.c index ccced72..b248071 100644 --- a/c/main_dreamcast.c +++ b/c/main_dreamcast.c @@ -7,26 +7,32 @@ #include "sh7091_scif.h" -#include "p/Native.class.h" +#include "p/DreamcastVideo.class.h" #include "java/lang/String.class.h" +#include "java/lang/Integer.class.h" #include "java/lang/System.class.h" #include "java/io/PrintStream.class.h" #include "java/lang/Object.class.h" +#include "sega/dreamcast/holly/Holly.class.h" +#include "java/misc/Memory.class.h" void main() { scif_init(0); 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_Integer_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_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])); - 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 class_hash_table_length; diff --git a/gen_decoder.py b/gen_decoder.py index 418bf03..a635ce7 100644 --- a/gen_decoder.py +++ b/gen_decoder.py @@ -49,7 +49,7 @@ def parse_arguments(types, arguments): assert size in {1, 2, 4} assert signed is not None - if name in {"byte", "branch"}: + if name in {"byte", "branch", "_const"}: assert signed == True, name else: assert signed == False, name diff --git a/generate.sh b/generate.sh new file mode 100644 index 0000000..c19247e --- /dev/null +++ b/generate.sh @@ -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 diff --git a/java.mk b/java.mk index c0ff2ca..7ac51c9 100644 --- a/java.mk +++ b/java.mk @@ -1,7 +1,7 @@ -%.class: %.java - javac $< +#%.class: %.java +# javac $< -java/%.class: java/%.java +%.class: %.java javac --source 8 --target 8 --boot-class-path . $< OBJ = \ diff --git a/java/lang/Integer.class.h b/java/lang/Integer.class.h new file mode 100644 index 0000000..bde1496 --- /dev/null +++ b/java/lang/Integer.class.h @@ -0,0 +1,15 @@ +#pragma once + +#include + +#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 diff --git a/java/lang/Integer.java b/java/lang/Integer.java new file mode 100644 index 0000000..8da7e24 --- /dev/null +++ b/java/lang/Integer.java @@ -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); + } +} diff --git a/java/misc/Memory.class.h b/java/misc/Memory.class.h new file mode 100644 index 0000000..1883bc3 --- /dev/null +++ b/java/misc/Memory.class.h @@ -0,0 +1,15 @@ +#pragma once + +#include + +#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 diff --git a/main.bin b/main.bin new file mode 100755 index 0000000..d3e8978 Binary files /dev/null and b/main.bin differ diff --git a/opcodes.ods b/opcodes.ods index 3d9760c..7e19be4 100644 Binary files a/opcodes.ods and b/opcodes.ods differ diff --git a/p/DreamcastVideo.class.h b/p/DreamcastVideo.class.h new file mode 100644 index 0000000..729dcb8 --- /dev/null +++ b/p/DreamcastVideo.class.h @@ -0,0 +1,15 @@ +#pragma once + +#include + +#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 diff --git a/p/DreamcastVideo.java b/p/DreamcastVideo.java new file mode 100644 index 0000000..7a52e4d --- /dev/null +++ b/p/DreamcastVideo.java @@ -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); + } + } +} diff --git a/regs/Foo.java b/regs/Foo.java new file mode 100644 index 0000000..4e2aa21 --- /dev/null +++ b/regs/Foo.java @@ -0,0 +1,4 @@ +class Holly { + public static final int + +} diff --git a/regs/csv_input.py b/regs/csv_input.py new file mode 100644 index 0000000..e694b84 --- /dev/null +++ b/regs/csv_input.py @@ -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 diff --git a/regs/generate.py b/regs/generate.py new file mode 100644 index 0000000..ad924f9 --- /dev/null +++ b/regs/generate.py @@ -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 diff --git a/regs/holly.py b/regs/holly.py new file mode 100644 index 0000000..a1d4925 --- /dev/null +++ b/regs/holly.py @@ -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()) diff --git a/regs/register.py b/regs/register.py new file mode 100644 index 0000000..8f51410 --- /dev/null +++ b/regs/register.py @@ -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)) diff --git a/sega/dreamcast/holly/Holly.class.h b/sega/dreamcast/holly/Holly.class.h new file mode 100644 index 0000000..8c8e18b --- /dev/null +++ b/sega/dreamcast/holly/Holly.class.h @@ -0,0 +1,15 @@ +#pragma once + +#include + +#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 diff --git a/sega/dreamcast/holly/Holly.java b/sega/dreamcast/holly/Holly.java new file mode 100644 index 0000000..eee13f0 --- /dev/null +++ b/sega/dreamcast/holly/Holly.java @@ -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; +}