blob: c5d6dd313209c6a7f6b85f3c7bd648837e968676 [file] [log] [blame]
// main.c
//
// Copyright (C) 2020 Dan Rodrigues <danrr.gh.oss@gmail.com>
//
// SPDX-License-Identifier: Apache-2.0
// From Caravel:
#include "../../caravel/defs.h"
#include "../../caravel/stub.c"
// User proj additions:
#include <stdbool.h>
#include <stddef.h>
#include "vdp.h"
#include "vdp_regs.h"
#include "math_util.h"
#include "gamepad.h"
#include "gpio.h"
#include "sprite_text_attributes.h"
// Adjusted defines with minimized VDP:
#define SPRITES_TOTAL 31
#define SPRITES_INDEX_TOTAL 32
static uint16_t sprite_buffer[SPRITES_INDEX_TOTAL * 3];
static uint16_t *sprite_buffer_pointer;
#define LITE_ACTIVE_WIDTH 256
#define LITE_ACTIVE_HEIGHT 240
// UART: (very slow in sim when enabled)
//#define UART_ENABLED
static void println(const char *line);
// Additional VDP control:
typedef enum {
VDP_EXTRA_CTRL_ACTIVE = (1 << 0),
VDP_EXTRA_CTRL_HOLD_RASTER = (1 << 1)
} VDPExtraControlMask;
static void palette_init(void);
static void sprite_init(void);
// Sprites:
static void sprite_init(void);
static void draw_circle_sprites(uint16_t angle);
static void draw_greeting_sprites(uint16_t step);
static void sprite_write_buffer(uint16_t x, uint16_t y, uint16_t g);
static void sprite_reset_buffer(void);
static void sprite_upload_buffer(void);
static void sprite_putc(char c, uint16_t *x, uint16_t y);
static void custom_tiles_init(void);
void main() {
gpio_init();
println("GPIO inititalized");
// Enable VDP (remains in reset until this is set)
reg_mprj_slave = VDP_EXTRA_CTRL_ACTIVE | VDP_EXTRA_CTRL_HOLD_RASTER;
println("VDP on");
palette_init();
sprite_init();
println("VDP sprites on");
// Prepare screen content before starting raster counter
// This also gives the sim something to render when raster counters start
draw_circle_sprites(0);
draw_greeting_sprites(0);
// Only used to test VRAM writing / reading
// custom_tiles_init();
sprite_upload_buffer();
// Allow VDP raster to start after initial config
reg_mprj_slave = VDP_EXTRA_CTRL_ACTIVE;
println("VDP raster counter on");
// Allow one frame to finish so it can be captured by sim
vdp_wait_frame_ended();
uint16_t step = 0;
while(true) {
draw_circle_sprites(step);
draw_greeting_sprites(step);
step++;
vdp_wait_frame_ended();
sprite_upload_buffer();
}
}
static void println(const char *line) {
#ifdef UART_ENABLED
print(line);
print("\n");
#endif
}
static void palette_init() {
const uint16_t bg_color = 0xf488;
const uint16_t text_foreground_color = 0xffff;
const uint16_t text_background_color = 0xf000;
vdp_set_single_palette_color(0, bg_color);
vdp_set_single_palette_color(1, text_background_color);
vdp_set_single_palette_color(2, text_foreground_color);
}
static void draw_circle_sprites(uint16_t angle) {
const int16_t screen_center_x = LITE_ACTIVE_WIDTH / 2;
const int16_t screen_center_y = LITE_ACTIVE_HEIGHT / 2;
const uint8_t star_count = 8;
const int16_t circle_radius = 110;
const int16_t angle_delta = SIN_PERIOD / star_count;
const SpriteTextCharAttributes *attributes = st_char_attributes('*');
for (uint32_t i = 0; i < star_count; i++) {
// Position within circle
int16_t x = screen_center_x - 8;
int16_t y = screen_center_y - 8;
x += (cos(angle) * circle_radius) / SIN_MAX;
y += (sin(angle) * circle_radius) / SIN_MAX;
angle += angle_delta;
// Draw sprite
uint16_t x_block = x;
uint16_t y_block = y;
y_block |= SPRITE_16_TALL | SPRITE_16_WIDE;
uint16_t g_block = attributes->base_tile;
sprite_write_buffer(x_block, y_block, g_block);
}
}
static void draw_greeting_sprites(uint16_t step) {
const char *greeting1 = "* VDP LITE *";
const char *greeting2 = "ASIC SPRITES!";
// Greeting 1 has a wave effect
const int16_t g1_x = 80;
const int16_t g1_y = 90;
uint16_t x = g1_x;
const int16_t g1_wave_amplitude = 8;
const int16_t g1_wave_period = 8;
const uint16_t g1_wave_angle_delta = SIN_PERIOD / g1_wave_period;
uint32_t char_count = 0;
uint16_t g1_angle = step * 16;
while (*greeting1) {
int16_t wave_offset_y = (sin(g1_angle) * g1_wave_amplitude / 2) / SIN_MAX;
int16_t y = g1_y + wave_offset_y;
sprite_putc(*greeting1++, &x, y);
g1_angle += g1_wave_angle_delta;
char_count++;
}
// Greeting 2 has a bounce effect
const int16_t g2_x = 78;
const int16_t g2_y = 170;
const int16_t g2_wave_amplitude = 80;
const int16_t g2_wave_period = 32;
const uint16_t g2_wave_angle_delta = SIN_PERIOD / g2_wave_period;
uint16_t g2_angle = step * 4;
char_count = 0;
x = g2_x;
while (*greeting2) {
g2_angle %= SIN_PERIOD / 2;
int16_t wave_offset_y = -(sin(g2_angle) * g2_wave_amplitude / 2) / SIN_MAX;
int16_t y = g2_y + wave_offset_y;
sprite_putc(*greeting2++, &x, y);
g2_angle += g2_wave_angle_delta;
char_count++;
}
}
static void sprite_putc(char c, uint16_t *x, uint16_t y) {
const SpriteTextCharAttributes *attributes = st_char_attributes(c);
if (c == ' ') {
*x += 5;
return;
}
uint16_t x_block = *x;
uint16_t y_block = y;
y_block |= SPRITE_16_TALL;
y_block |= attributes->wide ? SPRITE_16_WIDE : 0;
uint16_t g_block = attributes->base_tile;
sprite_write_buffer(x_block, y_block, g_block);
*x += attributes->width;
}
static void sprite_init() {
vdp_enable_layers(SPRITES);
// Move all sprites offscreen
vdp_seek_sprite(0);
for (uint32_t i = 0; i < SPRITES_INDEX_TOTAL; i++) {
vdp_write_sprite_meta(0, LITE_ACTIVE_HEIGHT, 0);
sprite_buffer[i * 3 + 0] = 0;
sprite_buffer[i * 3 + 1] = LITE_ACTIVE_HEIGHT;
sprite_buffer[i * 3 + 2] = 0;
}
sprite_reset_buffer();
}
static void sprite_reset_buffer() {
sprite_buffer_pointer = sprite_buffer;
}
static void sprite_write_buffer(uint16_t x, uint16_t y, uint16_t g) {
*sprite_buffer_pointer++ = x;
*sprite_buffer_pointer++ = y;
*sprite_buffer_pointer++ = g;
}
static void sprite_upload_buffer() {
vdp_seek_sprite(0);
for (uint16_t *buffer_read = sprite_buffer; buffer_read < sprite_buffer_pointer; buffer_read += 3) {
vdp_write_sprite_meta(buffer_read[0], buffer_read[1], buffer_read[2]);
}
sprite_reset_buffer();
}
static void custom_tiles_init() {
static const uint32_t tiles0[] = {
0x12222221,
0x11222211,
0x11122111,
0x11122111,
0x11122111,
0x11222211,
0x12222221,
0x12222221,
0x10101010,
0x11222211,
0x11122111,
0x11122111,
0x11122111,
0x12222221,
0x11222211,
0x10101010
};
static const uint32_t tiles1[] = {
0x11111111,
0x22222222,
0x11111111,
0x22222222,
0x11111111,
0x22222222,
0x11111111,
0x22222222,
0x12222221,
0x11222211,
0x11122111,
0x11122111,
0x11122111,
0x11222211,
0x12222221,
0x12222221,
};
// VDP *must* start or else can't write VRAM
reg_mprj_slave = VDP_EXTRA_CTRL_ACTIVE;
const uint16_t vram_base = 0x80 * 32 / 2;
vdp_set_vram_increment(1);
vdp_seek_vram(vram_base);
vdp_write_vram_block((uint16_t *)tiles0, 0x10 * 2);
vdp_seek_vram(vram_base + 0x10 * 0x10);
vdp_write_vram_block((uint16_t *)tiles1, 0x10 * 2);
// Sprites:
sprite_init();
vdp_seek_sprite(0);
vdp_write_sprite_meta(8, 20 | SPRITE_16_TALL | SPRITE_16_WIDE, 0x80);
}