diff --git a/client.py b/client.py index 516745d..a26f832 100644 --- a/client.py +++ b/client.py @@ -41,8 +41,6 @@ def symmetric(ser, b): l.extend(res) if len(l) + 8 >= i: break - else: - time.sleep(0.0001) ser.write(bytes([c])) diff --git a/headers.mk b/headers.mk index ec34f63..27a82f7 100644 --- a/headers.mk +++ b/headers.mk @@ -31,6 +31,9 @@ holly/ta_vertex_parameter.hpp: regs/vertex_parameter_format.csv regs/gen/ta_para holly/object_list_data.hpp: regs/object_list.csv regs/gen/core_bits.py python regs/gen/core_bits.py $< object_list_data > $@ +holly/video_output_mode.cpp: regs/video_output.csv regs/gen/video_output.py + python regs/gen/video_output.py $< > $@ + # MAPLE maple/maple_bus_commands.hpp: regs/maple_bus_commands.csv regs/gen/maple_bus_commands.py diff --git a/holly/core_bits.hpp b/holly/core_bits.hpp index fd22958..a85c607 100644 --- a/holly/core_bits.hpp +++ b/holly/core_bits.hpp @@ -392,7 +392,7 @@ namespace text_control { namespace vo_control { constexpr uint32_t pclk_delay_reset = 1 << 21; - constexpr uint32_t pclk_delay(uint32_t num) { return (num & 0xf) << 16; } + constexpr uint32_t pclk_delay(uint32_t num) { return (num & 0x1f) << 16; } constexpr uint32_t pixel_double = 1 << 8; namespace field_mode { diff --git a/holly/video_output.hpp b/holly/video_output.hpp new file mode 100644 index 0000000..49d7394 --- /dev/null +++ b/holly/video_output.hpp @@ -0,0 +1,19 @@ +#include + +namespace video_output { + +struct mode { + const uint32_t fb_r_ctrl; + const uint32_t spg_load; + const uint32_t spg_hblank; + const uint32_t spg_vblank; + const uint32_t spg_width; + const uint32_t spg_control; + const uint32_t vo_startx; + const uint32_t vo_starty; + const uint32_t vo_control; + const uint32_t spg_hblank_int; + const uint32_t spg_vblank_int; +}; + +} diff --git a/holly/video_output_mode.cpp b/holly/video_output_mode.cpp new file mode 100644 index 0000000..2a7e570 --- /dev/null +++ b/holly/video_output_mode.cpp @@ -0,0 +1,177 @@ +#include + +#include "core_bits.hpp" +#include "video_output.hpp" + +namespace video_output { + const struct mode vga = { + .fb_r_ctrl = fb_r_ctrl::vclk_div::pclk_vclk_1 + , + .spg_load = spg_load::vcount(0x20c) + | spg_load::hcount(0x359) + , + .spg_hblank = spg_hblank::hbend(0x07e) + | spg_hblank::hbstart(0x345) + , + .spg_vblank = spg_vblank::vbend(0x028) + | spg_vblank::vbstart(0x208) + , + .spg_width = spg_width::eqwidth(0x00f) + | spg_width::bpwidth(0x319) + | spg_width::vswidth(0x3) + | spg_width::hswidth(0x3f) + , + .spg_control = spg_control::sync_direction::output + , + .vo_startx = vo_startx::horizontal_start_position(0x0a8) + , + .vo_starty = vo_starty::vertical_start_position_on_field_2(0x028) + | vo_starty::vertical_start_position_on_field_1(0x028) + , + .vo_control = vo_control::pclk_delay(0x16) + , + .spg_hblank_int = spg_hblank_int::line_comp_val(0x345) + , + .spg_vblank_int = spg_vblank_int::vblank_out_interrupt_line_number(0x015) + | spg_vblank_int::vblank_in_interrupt_line_number(0x208) + , + }; + const struct mode ntsc_ni = { + .fb_r_ctrl = fb_r_ctrl::vclk_div::pclk_vclk_2 + , + .spg_load = spg_load::vcount(0x106) + | spg_load::hcount(0x359) + , + .spg_hblank = spg_hblank::hbend(0x07e) + | spg_hblank::hbstart(0x345) + , + .spg_vblank = spg_vblank::vbend(0x012) + | spg_vblank::vbstart(0x102) + , + .spg_width = spg_width::eqwidth(0x00f) + | spg_width::bpwidth(0x319) + | spg_width::vswidth(0x3) + | spg_width::hswidth(0x3f) + , + .spg_control = spg_control::sync_direction::output + | spg_control::ntsc + , + .vo_startx = vo_startx::horizontal_start_position(0x0a4) + , + .vo_starty = vo_starty::vertical_start_position_on_field_2(0x012) + | vo_starty::vertical_start_position_on_field_1(0x011) + , + .vo_control = vo_control::pclk_delay(0x16) + | vo_control::pixel_double + , + .spg_hblank_int = spg_hblank_int::line_comp_val(0x345) + , + .spg_vblank_int = spg_vblank_int::vblank_out_interrupt_line_number(0x015) + | spg_vblank_int::vblank_in_interrupt_line_number(0x102) + , + }; + const struct mode ntsc_i = { + .fb_r_ctrl = fb_r_ctrl::vclk_div::pclk_vclk_2 + , + .spg_load = spg_load::vcount(0x20c) + | spg_load::hcount(0x359) + , + .spg_hblank = spg_hblank::hbend(0x07e) + | spg_hblank::hbstart(0x345) + , + .spg_vblank = spg_vblank::vbend(0x024) + | spg_vblank::vbstart(0x204) + , + .spg_width = spg_width::eqwidth(0x01f) + | spg_width::bpwidth(0x16c) + | spg_width::vswidth(0x6) + | spg_width::hswidth(0x3f) + , + .spg_control = spg_control::sync_direction::output + | spg_control::ntsc + | spg_control::interlace + , + .vo_startx = vo_startx::horizontal_start_position(0x0a4) + , + .vo_starty = vo_starty::vertical_start_position_on_field_2(0x012) + | vo_starty::vertical_start_position_on_field_1(0x012) + , + .vo_control = vo_control::pclk_delay(0x16) + | vo_control::pixel_double + , + .spg_hblank_int = spg_hblank_int::line_comp_val(0x345) + , + .spg_vblank_int = spg_vblank_int::vblank_out_interrupt_line_number(0x015) + | spg_vblank_int::vblank_in_interrupt_line_number(0x102) + , + }; + const struct mode pal_ni = { + .fb_r_ctrl = fb_r_ctrl::vclk_div::pclk_vclk_2 + , + .spg_load = spg_load::vcount(0x138) + | spg_load::hcount(0x35f) + , + .spg_hblank = spg_hblank::hbend(0x08d) + | spg_hblank::hbstart(0x34b) + , + .spg_vblank = spg_vblank::vbend(0x016) + | spg_vblank::vbstart(0x134) + , + .spg_width = spg_width::eqwidth(0x0f) + | spg_width::bpwidth(0x31f) + | spg_width::vswidth(0x3) + | spg_width::hswidth(0x3f) + , + .spg_control = spg_control::sync_direction::output + | spg_control::pal + , + .vo_startx = vo_startx::horizontal_start_position(0x0ae) + , + .vo_starty = vo_starty::vertical_start_position_on_field_2(0x02e) + | vo_starty::vertical_start_position_on_field_1(0x02e) + , + .vo_control = vo_control::pclk_delay(0x16) + | vo_control::pixel_double + , + .spg_hblank_int = spg_hblank_int::line_comp_val(0x34b) + , + .spg_vblank_int = spg_vblank_int::vblank_out_interrupt_line_number(0x015) + | spg_vblank_int::vblank_in_interrupt_line_number(0x134) + , + }; + const struct mode pal_i = { + .fb_r_ctrl = fb_r_ctrl::vclk_div::pclk_vclk_2 + , + .spg_load = spg_load::vcount(0x270) + | spg_load::hcount(0x35f) + , + .spg_hblank = spg_hblank::hbend(0x08d) + | spg_hblank::hbstart(0x34b) + , + .spg_vblank = spg_vblank::vbend(0x02c) + | spg_vblank::vbstart(0x26c) + , + .spg_width = spg_width::eqwidth(0x01f) + | spg_width::bpwidth(0x16a) + | spg_width::vswidth(0x5) + | spg_width::hswidth(0x3f) + , + .spg_control = spg_control::sync_direction::output + | spg_control::pal + | spg_control::interlace + , + .vo_startx = vo_startx::horizontal_start_position(0x0ae) + , + .vo_starty = vo_starty::vertical_start_position_on_field_2(0x02e) + | vo_starty::vertical_start_position_on_field_1(0x02d) + , + .vo_control = vo_control::pclk_delay(0x16) + | vo_control::pixel_double + , + .spg_hblank_int = spg_hblank_int::line_comp_val(0x34b) + , + .spg_vblank_int = spg_vblank_int::vblank_out_interrupt_line_number(0x015) + | spg_vblank_int::vblank_in_interrupt_line_number(0x134) + , + }; +} diff --git a/regs/core_bits.csv b/regs/core_bits.csv index dec7246..3c8d0f0 100644 --- a/regs/core_bits.csv +++ b/regs/core_bits.csv @@ -207,7 +207,7 @@ "TEXT_CONTROL",,"4-0","stride",,"0x1f",,,,,, ,,,,,,,,,,, "VO_CONTROL",,21,"pclk_delay_reset",1,,,,,,, -"VO_CONTROL",,"20-16","pclk_delay",,"0b1111",,,,,, +"VO_CONTROL",,"20-16","pclk_delay",,"0b11111",,,,,, "VO_CONTROL",,8,"pixel_double",1,,,,,,, "VO_CONTROL","field_mode","7-4","use_field_flag_from_spg","0x0",,,,,,, "VO_CONTROL","field_mode","7-4","use_inverse_of_field_flag_from_spg","0x1",,,,,,, diff --git a/regs/core_bits.ods b/regs/core_bits.ods index 986407c..e026747 100644 Binary files a/regs/core_bits.ods and b/regs/core_bits.ods differ diff --git a/regs/gen/video_output.py b/regs/gen/video_output.py new file mode 100644 index 0000000..9099970 --- /dev/null +++ b/regs/gen/video_output.py @@ -0,0 +1,147 @@ +from collections import defaultdict +import sys +from dataclasses import dataclass +from generate import renderer + +from csv_input import read_input_headerless + +def one_bit(register, bit, value): + v = int(value, 16) + if v == 0: + return None + elif v == 1: + return f"{register}::{bit}" + else: + assert False, (register, bit, value) + +def enum(enums): + def do(register, bit, value): + v = int(value, 16) + return f"{register}::{bit}::{enums[v]}" + return do + +special = { + ("spg_control", "pal"): one_bit, + ("spg_control", "ntsc"): one_bit, + ("spg_control", "interlace"): one_bit, + ("spg_control", "sync_direction"): enum({0: "input", 1: "output"}), + ("vo_control", "pixel_double"): one_bit, + ("fb_r_ctrl", "vclk_div"): enum({0: "pclk_vclk_2", 1: "pclk_vclk_1"}), +} + +def parse_regs(rows): + last_register = None + for row in rows: + register, bit, *values0 = row + if register == "": + assert last_register is not None + register = last_register + last_register = register + values = [v for v in values0] + yield register, bit, values + +@dataclass +class Bit: + name: str + value: int + +@dataclass +class Register: + name: str + bits: list[Bit] + +@dataclass +class Mode: + name: str + registers: list[Register] + +def group_by_register(regs): + by_register = defaultdict(list) + register_names = [] + for register, bit, value in regs: + if register not in register_names: + register_names.append(register) + by_register[register].append(Bit( + name=bit.lower(), + value=value + )) + + return [ + Register( + name=register.lower(), + bits=by_register[register] + ) + for register in register_names + ] + +def transpose_by_name(format_names, regs): + by_name = defaultdict(list) + for register, bit, values in regs: + assert len(values) == len(format_names) + for name, value in zip(format_names, values): + by_name[name].append((register, bit, value)) + return [ + Mode( + name=name, + registers=group_by_register(by_name[name]) + ) + for name in format_names + ] + +def render_bit(register, bit, value): + if (register, bit) in special: + return special[(register, bit)](register, bit, value) + else: + return f"{register}::{bit}(0x{value})" + +def render_mode(mode, max_len): + for register in mode.registers: + i_out = 0 + for bit in register.bits: + lhs = f".{register.name}" if i_out == 0 else "" + sym = '=' if i_out == 0 else "|" + _render_bit = render_bit(register.name, bit.name, bit.value) + if _render_bit is not None: + i_out += 1 + yield f"{lhs.ljust(max_len)} {sym} {_render_bit}" + + yield f"{''.ljust(max_len)} ," + + +def render_modes(modes, max_len): + for mode in modes: + yield f"const struct mode {mode.name.lower()} = {{" + yield from render_mode(mode, max_len) + yield "};" + +def render_namespace(modes, max_len): + yield "namespace video_output {" + yield from render_modes(modes, max_len) + yield "}" + +def render_header(): + yield "#include " + yield "" + yield '#include "core_bits.hpp"' + yield '#include "video_output.hpp"' + yield "" + +def max_length(regs): + max_length = 0 + for register, *_ in regs: + if len(register) > max_length: + max_length = len(register) + return max_length + 1 + +if __name__ == "__main__": + rows = read_input_headerless(sys.argv[1]) + formats0, *regs0 = rows + b0, b1, *format_names = formats0 + assert (b0, b1) == ("", ""), (b0, b1) + regs = list(parse_regs(regs0)) + modes = transpose_by_name(format_names, regs) + max_length = max_length(regs) + render, out = renderer() + render(render_header()) + render(render_namespace(modes, max_length)) + sys.stdout.write(out.getvalue()) diff --git a/regs/video_output.csv b/regs/video_output.csv new file mode 100644 index 0000000..4108b8c --- /dev/null +++ b/regs/video_output.csv @@ -0,0 +1,24 @@ +,,"VGA","NTSC_NI","NTSC_I","PAL_NI","PAL_I" +"FB_R_CTRL","vclk_div","1",0,0,0,0 +"SPG_LOAD","vcount","20c",106,"20c",138,270 +,"hcount","359","359","359","35f","35f" +"SPG_HBLANK","hbend","07e","07e","07e","08d","08d" +,"hbstart","345","345",345,"34b","34b" +"SPG_VBLANK","vbend","028","012","024","016","02c" +,"vbstart","208","102","204","134","26c" +"SPG_WIDTH","eqwidth","00f","00f","01f","0f","01f" +,"bpwidth","319","319","16c","31f","16a" +,"vswidth","3","3","6","3","5" +,"hswidth","3f","3f","3f","3f","3f" +"SPG_CONTROL","sync_direction","1","1","1","1","1" +,"pal","0","0","0","1","1" +,"ntsc","0","1","1","0","0" +,"interlace","0","0","1","0","1" +"VO_STARTX","horizontal_start_position","0a8","0a4","0a4","0ae","0ae" +"VO_STARTY","vertical_start_position_on_field_2","028","012","012","02e","02e" +,"vertical_start_position_on_field_1","028","011","012","02e","02d" +"VO_CONTROL","pclk_delay","16","16","16","16","16" +,"pixel_double","0","1","1","1","1" +"SPG_HBLANK_INT","line_comp_val","345","345","345","34b","34b" +"SPG_VBLANK_INT","vblank_out_interrupt_line_number","015","015","015","015","015" +,"vblank_in_interrupt_line_number","208","102","102","134","134" diff --git a/regs/video_output.ods b/regs/video_output.ods new file mode 100644 index 0000000..988b53f Binary files /dev/null and b/regs/video_output.ods differ