holly: add video output data
This commit is contained in:
parent
0deedb9858
commit
eafd4e6f23
@ -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]))
|
||||
|
||||
|
@ -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
|
||||
|
@ -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 {
|
||||
|
19
holly/video_output.hpp
Normal file
19
holly/video_output.hpp
Normal 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
177
holly/video_output_mode.cpp
Normal 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)
|
||||
,
|
||||
};
|
||||
}
|
@ -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",,,,,,,
|
||||
|
|
Binary file not shown.
147
regs/gen/video_output.py
Normal file
147
regs/gen/video_output.py
Normal 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
24
regs/video_output.csv
Normal 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"
|
|
BIN
regs/video_output.ods
Normal file
BIN
regs/video_output.ods
Normal file
Binary file not shown.
Loading…
x
Reference in New Issue
Block a user