diff --git a/holly/framebuffer.cpp b/holly/framebuffer.cpp new file mode 100644 index 0000000..13af1ef --- /dev/null +++ b/holly/framebuffer.cpp @@ -0,0 +1,185 @@ +#include "holly/holly.hpp" +#include "holly/core_bits.hpp" + +#include "framebuffer.hpp" + +namespace framebuffer { + + struct framebuffer framebuffer; + + void init(int px_width, + int px_height, + uint32_t framebuffer_start) + { + framebuffer.px_width = px_width; + framebuffer.px_height = px_height; + + int x_size = framebuffer.px_width; + int y_size = framebuffer.px_height; + + // write + + holly.FB_X_CLIP + = fb_x_clip::fb_x_clip_max(x_size - 1) + | fb_x_clip::fb_x_clip_min(0); + + holly.FB_Y_CLIP + = fb_y_clip::fb_y_clip_max(y_size - 1) + | fb_y_clip::fb_y_clip_min(0); + + holly.FB_R_SIZE + = fb_r_size::fb_modulus(1) + | fb_r_size::fb_y_size(y_size - 1) + | fb_r_size::fb_x_size((x_size * bytes_per_pixel) / 4 - 1); + + holly.FB_R_SOF1 + = framebuffer_start; + + uint32_t vclk_div = (px_width >= 640) ? fb_r_ctrl::vclk_div::pclk_vclk_1 + : fb_r_ctrl::vclk_div::pclk_vclk_2; + holly.FB_R_CTRL + = vclk_div + | fb_r_ctrl::fb_depth::_565_rgb_16bit + | fb_r_ctrl::fb_enable; + } + + void scaler_init() + { + holly.Y_COEFF + = y_coeff::coefficient_1(0x80) + | y_coeff::coefficient_0_2(0x40); + + // in 6.10 fixed point; 0x0400 is 1x vertical scale + holly.SCALER_CTL = scaler_ctl::vertical_scale_factor(0x0400); + + holly.FB_BURSTCTRL + = fb_burstctrl::wr_burst(0x09) + | fb_burstctrl::vid_lat(0x3f) + | fb_burstctrl::vid_burst(0x39); + } + + void spg_set_mode_720x480_vga() + { + holly.SPG_CONTROL + = spg_control::sync_direction::output; + + holly.SPG_LOAD + = spg_load::vcount(525 - 1) // number of lines per field + | spg_load::hcount(858 - 1); // number of video clock cycles per line + + holly.SPG_HBLANK + = spg_hblank::hbend(117) // H Blank ending position + | spg_hblank::hbstart(837); // H Blank starting position + + holly.SPG_VBLANK + = spg_vblank::vbend(40) // V Blank ending position + | spg_vblank::vbstart(520); // V Blank starting position + + holly.SPG_WIDTH + = spg_width::eqwidth(16 - 1) // Specify the equivalent pulse width (number of video clock cycles - 1) + | spg_width::bpwidth(794 - 1) // Specify the broad pulse width (number of video clock cycles - 1) + | spg_width::vswidth(3) // V Sync width (number of lines) + | spg_width::hswidth(64 - 1); // H Sync width (number of video clock cycles - 1) + + holly.VO_STARTX + = vo_startx::horizontal_start_position(117); + + holly.VO_STARTY + = vo_starty::vertical_start_position_on_field_2(40) + | vo_starty::vertical_start_position_on_field_1(40); + + holly.VO_CONTROL + = vo_control::pclk_delay(22); + + holly.SPG_HBLANK_INT + = spg_hblank_int::line_comp_val(837); + + holly.SPG_VBLANK_INT + = spg_vblank_int::vblank_out_interrupt_line_number(21) + | spg_vblank_int::vblank_in_interrupt_line_number(520); + } + + void spg_set_mode_640x480_vga() + { + holly.SPG_CONTROL + = spg_control::sync_direction::output; + + holly.SPG_LOAD + = spg_load::vcount(525 - 1) // number of lines per field + | spg_load::hcount(858 - 1); // number of video clock cycles per line + + holly.SPG_HBLANK + = spg_hblank::hbend(126) // H Blank ending position + | spg_hblank::hbstart(837); // H Blank starting position + + holly.SPG_VBLANK + = spg_vblank::vbend(40) // V Blank ending position + | spg_vblank::vbstart(520); // V Blank starting position + + holly.SPG_WIDTH + = spg_width::eqwidth(16 - 1) // Specify the equivalent pulse width (number of video clock cycles - 1) + | spg_width::bpwidth(794 - 1) // Specify the broad pulse width (number of video clock cycles - 1) + | spg_width::vswidth(3) // V Sync width (number of lines) + | spg_width::hswidth(64 - 1); // H Sync width (number of video clock cycles - 1) + + holly.VO_STARTX + = vo_startx::horizontal_start_position(168); + + holly.VO_STARTY + = vo_starty::vertical_start_position_on_field_2(40) + | vo_starty::vertical_start_position_on_field_1(40); + + holly.VO_CONTROL + = vo_control::pclk_delay(22); + + holly.SPG_HBLANK_INT + = spg_hblank_int::line_comp_val(837); + + holly.SPG_VBLANK_INT + = spg_vblank_int::vblank_out_interrupt_line_number(21) + | spg_vblank_int::vblank_in_interrupt_line_number(520); + } + + void spg_set_mode_320x240_ntsc_ni() + { + holly.SPG_CONTROL + = spg_control::sync_direction::output + | spg_control::ntsc; + + holly.SPG_LOAD + = spg_load::vcount(263 - 1) + | spg_load::hcount(858 - 1); + + holly.SPG_HBLANK + = spg_hblank::hbend(126) + | spg_hblank::hbstart(837); + + holly.SPG_VBLANK + = spg_vblank::vbend(18) + | spg_vblank::vbstart(258); + + holly.SPG_WIDTH + = spg_width::eqwidth(16 - 1) + | spg_width::bpwidth(794 - 1) + | spg_width::vswidth(3) + | spg_width::hswidth(64 - 1); + + holly.VO_STARTX + = vo_startx::horizontal_start_position(164); + + holly.VO_STARTY + = vo_starty::vertical_start_position_on_field_2(18) + | vo_starty::vertical_start_position_on_field_1(17); + + holly.VO_CONTROL + = vo_control::pclk_delay(22) + | vo_control::pixel_double; + + holly.SPG_HBLANK_INT + = spg_hblank_int::line_comp_val(837); + + holly.SPG_VBLANK_INT + = spg_vblank_int::vblank_out_interrupt_line_number(21) + | spg_vblank_int::vblank_in_interrupt_line_number(258); + } +} diff --git a/holly/framebuffer.hpp b/holly/framebuffer.hpp new file mode 100644 index 0000000..6f70d41 --- /dev/null +++ b/holly/framebuffer.hpp @@ -0,0 +1,41 @@ +#pragma once + +namespace framebuffer { + + constexpr inline int div32(int n) + { + return (n + 32 - 1) / 32; + } + + struct framebuffer { + int px_width; + int px_height; + + framebuffer() + : px_width(0), px_height(0) + {} + + framebuffer(int width, int height) + : px_width(width), px_height(height) + {} + + int tile_width() { + return div32(px_width); + } + int tile_height() { + return div32(px_height); + } + }; + + const int bytes_per_pixel = 2; + + extern struct framebuffer framebuffer; + + void init(int px_width, + int px_height, + uint32_t framebuffer_start); + void scaler_init(); + void spg_set_mode_720x480_vga(); + void spg_set_mode_640x480_vga(); + void spg_set_mode_320x240_ntsc_ni(); +}