holly: add video output data

This commit is contained in:
Zack Buhman 2024-03-08 16:17:59 +08:00
parent 0deedb9858
commit eafd4e6f23
10 changed files with 372 additions and 4 deletions

View File

@ -41,8 +41,6 @@ def symmetric(ser, b):
l.extend(res) l.extend(res)
if len(l) + 8 >= i: if len(l) + 8 >= i:
break break
else:
time.sleep(0.0001)
ser.write(bytes([c])) ser.write(bytes([c]))

View File

@ -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 holly/object_list_data.hpp: regs/object_list.csv regs/gen/core_bits.py
python regs/gen/core_bits.py $< object_list_data > $@ 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/maple_bus_commands.hpp: regs/maple_bus_commands.csv regs/gen/maple_bus_commands.py maple/maple_bus_commands.hpp: regs/maple_bus_commands.csv regs/gen/maple_bus_commands.py

View File

@ -392,7 +392,7 @@ namespace text_control {
namespace vo_control { namespace vo_control {
constexpr uint32_t pclk_delay_reset = 1 << 21; 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; constexpr uint32_t pixel_double = 1 << 8;
namespace field_mode { namespace field_mode {

19
holly/video_output.hpp Normal file
View File

@ -0,0 +1,19 @@
#include <cstdint>
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;
};
}

177
holly/video_output_mode.cpp Normal file
View File

@ -0,0 +1,177 @@
#include <cstdint>
#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)
,
};
}

View File

@ -207,7 +207,7 @@
"TEXT_CONTROL",,"4-0","stride",,"0x1f",,,,,, "TEXT_CONTROL",,"4-0","stride",,"0x1f",,,,,,
,,,,,,,,,,, ,,,,,,,,,,,
"VO_CONTROL",,21,"pclk_delay_reset",1,,,,,,, "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",,8,"pixel_double",1,,,,,,,
"VO_CONTROL","field_mode","7-4","use_field_flag_from_spg","0x0",,,,,,, "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",,,,,,, "VO_CONTROL","field_mode","7-4","use_inverse_of_field_flag_from_spg","0x1",,,,,,,

1 register_name enum_name bits bit_name value mask description
207 TEXT_CONTROL 4-0 stride 0x1f
208
209 VO_CONTROL 21 pclk_delay_reset 1
210 VO_CONTROL 20-16 pclk_delay 0b1111 0b11111
211 VO_CONTROL 8 pixel_double 1
212 VO_CONTROL field_mode 7-4 use_field_flag_from_spg 0x0
213 VO_CONTROL field_mode 7-4 use_inverse_of_field_flag_from_spg 0x1

Binary file not shown.

147
regs/gen/video_output.py Normal file
View File

@ -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 <cstdint>"
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())

24
regs/video_output.csv Normal file
View File

@ -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"
1 VGA NTSC_NI NTSC_I PAL_NI PAL_I
2 FB_R_CTRL vclk_div 1 0 0 0 0
3 SPG_LOAD vcount 20c 106 20c 138 270
4 hcount 359 359 359 35f 35f
5 SPG_HBLANK hbend 07e 07e 07e 08d 08d
6 hbstart 345 345 345 34b 34b
7 SPG_VBLANK vbend 028 012 024 016 02c
8 vbstart 208 102 204 134 26c
9 SPG_WIDTH eqwidth 00f 00f 01f 0f 01f
10 bpwidth 319 319 16c 31f 16a
11 vswidth 3 3 6 3 5
12 hswidth 3f 3f 3f 3f 3f
13 SPG_CONTROL sync_direction 1 1 1 1 1
14 pal 0 0 0 1 1
15 ntsc 0 1 1 0 0
16 interlace 0 0 1 0 1
17 VO_STARTX horizontal_start_position 0a8 0a4 0a4 0ae 0ae
18 VO_STARTY vertical_start_position_on_field_2 028 012 012 02e 02e
19 vertical_start_position_on_field_1 028 011 012 02e 02d
20 VO_CONTROL pclk_delay 16 16 16 16 16
21 pixel_double 0 1 1 1 1
22 SPG_HBLANK_INT line_comp_val 345 345 345 34b 34b
23 SPG_VBLANK_INT vblank_out_interrupt_line_number 015 015 015 015 015
24 vblank_in_interrupt_line_number 208 102 102 134 134

BIN
regs/video_output.ods Normal file

Binary file not shown.