blob: bd0062ec72553eab1bbcaa7f1c2e87cd541e98f3 [file] [log] [blame]
// vdp.c
//
// Copyright (C) 2020 Dan Rodrigues <danrr.gh.oss@gmail.com>
//
// SPDX-License-Identifier: MIT
#include "vdp.h"
#include "vdp_regs.h"
// exported constants
const VDPLayer VDP_ALL_LAYERS = 0x1f;
// scrolling layer map attributes
const uint16_t SCROLL_MAP_X_FLIP_SHIFT = 9;
const uint16_t SCROLL_MAP_Y_FLIP_SHIFT = 10;
const uint16_t SCROLL_MAP_X_FLIP = 1 << SCROLL_MAP_X_FLIP_SHIFT;
const uint16_t SCROLL_MAP_Y_FLIP = 1 << SCROLL_MAP_Y_FLIP_SHIFT;
const uint16_t SCROLL_MAP_PAL_SHIFT = 12;
// sprite meta attributes
// x_block
const uint16_t SPRITE_X_FLIP = 1 << 10;
// y_block
const uint16_t SPRITE_Y_FLIP = 1 << 9;
const uint16_t SPRITE_16_TALL = 1 << 10;
const uint16_t SPRITE_16_WIDE = 1 << 11;
// g_block
const uint16_t SPRITE_PRIORITY_SHIFT = 10;
const uint16_t SPRITE_PAL_SHIFT = 12;
// MARK: Metrics
const uint16_t SCREEN_ACTIVE_WIDTH = 848;
const uint16_t SCREEN_ACTIVE_HEIGHT = 480;
const uint16_t SCREEN_OFFSCREEN_X = 240;
const uint16_t RASTER_X_MAX = SCREEN_ACTIVE_WIDTH + SCREEN_OFFSCREEN_X - 1;
// MARK: Functions
void vdp_enable_copper(uint8_t enable) {
VDP_ENABLE_COPPER = enable;
}
static void update_layer_address(uint8_t layer, uint16_t address, uint16_t *combined) {
#ifdef VDP_INTERLEAVED_MAP
const uint8_t address_shift = 11;
#else
const uint8_t address_shift = 12;
#endif
uint8_t shift = layer * 4;
uint16_t mask = 0xf << shift;
*combined &= ~mask;
*combined |= (address >> address_shift &0xf) << shift;
}
void vdp_set_layer_map_base(uint8_t layer, uint16_t address) {
static uint16_t combined_base = 0;
update_layer_address(layer, address, &combined_base);
VDP_SCROLL_MAP_ADDRESS_BASE = combined_base;
}
void vdp_set_layer_tile_base(uint8_t layer, uint16_t address) {
static uint16_t combined_base = 0;
update_layer_address(layer, address, &combined_base);
VDP_SCROLL_TILE_ADDRESS_BASE = combined_base;
}
void vdp_set_alpha_over_layers(VDPLayer layers) {
VDP_ALPHA_OVER_ENABLE = layers;
}
void vdp_enable_layers(VDPLayer layers) {
VDP_LAYER_ENABLE = layers;
}
void vdp_write_vram(uint16_t word) {
VDP_VRAM_WRITE_DATA = word;
}
void vdp_fill_vram(uint16_t count, uint16_t word) {
for (uint32_t i = 0; i < count; i++) {
VDP_VRAM_WRITE_DATA = word;
}
}
void vdp_seek_vram(uint16_t address) {
VDP_VRAM_ADDRESS = address;
}
void vdp_write_palette_range(uint8_t color_id_start, uint32_t count, const uint16_t *palette_start) {
VDP_PALETTE_ADDRESS = color_id_start;
for (uint32_t i = 0; i < count; i++) {
VDP_PALETTE_WRITE_DATA = palette_start[i];
}
}
void vdp_set_single_palette_color(uint8_t color_id, uint16_t color) {
VDP_PALETTE_ADDRESS = color_id;
VDP_PALETTE_WRITE_DATA = color;
}
void vdp_seek_palette(uint8_t color_id) {
VDP_PALETTE_ADDRESS = color_id;
}
void vdp_write_palette_color(uint16_t color) {
VDP_PALETTE_WRITE_DATA = color;
}
void vdp_set_layer_scroll(uint8_t layer, uint16_t scroll_x, uint16_t scroll_y) {
uint8_t index = layer & 0xff;
VDP_HSCROLL_BASE[index] = scroll_x;
VDP_VSCROLL_BASE[index] = scroll_y;
}
void vdp_set_vram_increment(uint8_t increment) {
VDP_ADDRESS_INCREMENT = increment;
}
void vdp_write_vram_block(const uint16_t *data, uint16_t size) {
if (!size) {
return;
}
// gcc generates suboptimal loop with a while()
const uint16_t *end = data + size;
do {
VDP_VRAM_WRITE_DATA = *data++;
} while (data != end);
}
void vdp_write_single_sprite_meta(uint8_t sprite_id, uint16_t x_block, uint16_t y_block, uint16_t g_block) {
VDP_SPRITE_BLOCK_ADDRESS = sprite_id;
VDP_SPRITE_DATA = x_block;
VDP_SPRITE_DATA = y_block;
VDP_SPRITE_DATA = g_block;
}
void vdp_seek_sprite(uint8_t sprite_id) {
VDP_SPRITE_BLOCK_ADDRESS = sprite_id;
}
void vdp_write_sprite_meta(uint16_t x_block, uint16_t y_block, uint16_t g_block) {
VDP_SPRITE_DATA = x_block;
VDP_SPRITE_DATA = y_block;
VDP_SPRITE_DATA = g_block;
}
void vdp_set_sprite_tile_base(uint16_t base) {
VDP_SPRITE_TILE_BASE = base / 2;
}
void vdp_clear_all_sprites() {
const uint16_t y_block = 480;
VDP_SPRITE_BLOCK_ADDRESS = 0;
for (uint32_t i = 0; i < 256; i++) {
VDP_SPRITE_DATA = 0;
VDP_SPRITE_DATA = y_block;
VDP_SPRITE_DATA = 0;
}
}
void vdp_set_wide_map_layers(VDPLayer layers) {
VDP_SCROLL_WIDE_MAP_ENABLE = layers;
}
void vdp_set_affine_matrix(int16_t a, int16_t b, int16_t c, int16_t d) {
VDP_MATRIX_A = a;
VDP_MATRIX_B = b;
VDP_MATRIX_C = c;
VDP_MATRIX_D = d;
}
void vdp_set_affine_pretranslate(int16_t x, int16_t y) {
VDP_AFFINE_PRETRANSLATE_X = x;
VDP_AFFINE_PRETRANSLATE_Y = y;
}
void vdp_set_affine_translate(int16_t x, int16_t y) {
VDP_HSCROLL_BASE[0] = x;
VDP_VSCROLL_BASE[1] = y;
}
void vdp_wait_frame_ended() {
const uint16_t final_line = 480;
while (VDP_CURRENT_RASTER_Y != (final_line - 1)) {}
while (VDP_CURRENT_RASTER_Y != final_line) {}
}
bool vdp_layer_is_odd(VDPLayer layer) {
switch (layer) {
case SCROLL0: case SCROLL2:
return false;
case SCROLL1: case SCROLL3:
return true;
default:
// fatal_error("Unexpected VDPLayer param");
return false;
}
}