Commit e7d3f606 authored by Donald Haase's avatar Donald Haase
Browse files

Initial commit

parents
/* KallistiOS ##version##
FB_tricks.c
Copyright (C) 2024 Donald Haase
*/
/*
This example loads a series of png images that are appropriately sized to
the display mode into frame buffers and cycles between them. By pressing
different buttons you can trigger different effects on that cycling.
These effects are achieved by manipulating PVR registers and should allow
significant time for parallel tasks.
*/
#include <kos.h>
#include <png/png.h>
#include <kos/img.h>
/* Init just the basic necessaries */
KOS_INIT_FLAGS(INIT_IRQ | INIT_FS_ROMDISK | INIT_CONTROLLER);
const char *filenames[] = {
"%s/PhillyGENSNL.png",
"%s/SteamBoat.png" };
#define FILE_COUNT (sizeof(filenames)/sizeof(filenames[0]))
#define FILE_ROOT "/rd"
#define TIMEOUT_SECS 2
/* These might be something to add into video.c */
void vid_disable(void) {
/* Blank screen and reset display enable (looks nicer) */
PVR_SET(PVR_VIDEO_CFG, PVR_GET(PVR_VIDEO_CFG) | 8); /* Blank */
PVR_SET(PVR_FB_CFG_1, PVR_GET(PVR_FB_CFG_1) & ~1); /* Display disable */
}
void vid_enable(void) {
/* Re-enable the display */
PVR_SET(PVR_VIDEO_CFG, PVR_GET(PVR_VIDEO_CFG) & ~8);
PVR_SET(PVR_FB_CFG_1, PVR_GET(PVR_FB_CFG_1) | 1);
}
void vid_shake(char speed) {
uint32 bitmapy = vid_mode->bitmapy;
int i, j;
int shake_dist = 24;
int shake_stride = 2;
printf("Commence the shaking!\n");
for(i=0; i < 5; i++) {
for(j=(shake_dist * -1); j<shake_dist; j+=shake_stride) {
PVR_SET(PVR_BITMAP_Y, ((bitmapy + j) << 16) | (bitmapy +j));
thd_sleep(5);
}
for(j=shake_dist; j>(shake_dist * -1); j-=shake_stride) {
PVR_SET(PVR_BITMAP_Y, ((bitmapy + j) << 16) | (bitmapy +j));
thd_sleep(5);
}
}
/* Go back to start */
PVR_SET(PVR_BITMAP_Y, (vid_mode->bitmapy << 16) | vid_mode->bitmapy);
printf("Shaking completed.\n");
}
void vid_rotate(int rotations) {
/* If VID_MAX_FB exists, then we're in v2.1.0 and below multibuffer */
#ifdef VID_MAX_FB
uint32 old_base = (fb_base[vid_mode->fb_curr]) & 0x007FFFFF;
#else
uint32 old_base = (vid_mode->fb_size * vid_mode->fb_curr) & 0x007FFFFF;
#endif
uint32 stride_div = 8;
uint32 stride = ((vid_mode->width / stride_div) * vid_pmode_bpp[vid_mode->pm]);
int32 i, dir = 1;
for(;rotations>0; rotations--) {
for(i = 0; i<stride_div; i++) {
old_base += (stride * dir);
/* Set vram base of current framebuffer */
PVR_SET(PVR_FB_ADDR, old_base);
/* Set odd-field if interlaced. */
if(vid_mode->flags & VID_INTERLACE)
PVR_SET(PVR_FB_IL_ADDR, old_base + (vid_mode->width * vid_pmode_bpp[vid_mode->pm]));
thd_sleep(16*stride_div);
}
dir *= -1;
}
/* Set back to start value */
#ifdef VID_MAX_FB
old_base = (fb_base[vid_mode->fb_curr]) & 0x007FFFFF;
#else
old_base = (vid_mode->fb_size * vid_mode->fb_curr) & 0x007FFFFF;
#endif
PVR_SET(PVR_FB_ADDR, old_base);
/* Set odd-field if interlaced. */
if(vid_mode->flags & VID_INTERLACE)
PVR_SET(PVR_FB_IL_ADDR, old_base + (vid_mode->width * vid_pmode_bpp[vid_mode->pm]));
}
/* This is a replacement for 'vid_flip' that scrolls the FB vertically into an adjascent one.
dir sets the direction for it to go 1 for forward -1 for backwards. Any other value will
default to forward. If asked to go in a direction with no more FBs, will go the other. */
/* 1 for forward, -1 for backwards, returns direction in case it has to bounce back or 0 on error */
char vid_flip_fun(char dir, char stride_div) {
/* This requires the new MB concept to work right. */
#ifdef VID_MAX_FB
return 0;
#else
uint32 old_base = (vid_mode->fb_size * vid_mode->fb_curr) & 0x007FFFFF;
uint32 new_base;
uint32 stride = ((vid_mode->width / stride_div) * vid_pmode_bpp[vid_mode->pm]);
/* If we don't have multiple buffers set up, just kick back error */
if( vid_mode->fb_count == 1 ) return 0;
/* Lets default to 'forward' */
if( (dir != 1) && (dir != -1) ) dir = 1;
/* Check if we'd roll over in either direction, if so flip direction */
if(( ((vid_mode->fb_curr + 1) == (vid_mode->fb_count)) && (dir == 1) ) ||
( (vid_mode->fb_curr == 0) && (dir == -1) ))
dir = -1 * dir;
/* Move the fb_curr in the desired direction */
vid_mode->fb_curr += dir;
/* Set the new base address for the fb */
new_base = (vid_mode->fb_size * vid_mode->fb_curr) & 0x007FFFFF;
for(;old_base != new_base; old_base += (dir * stride)) {
/* Set vram base of current framebuffer */
PVR_SET(PVR_FB_ADDR, old_base);
/* Set odd-field if interlaced. */
if(vid_mode->flags & VID_INTERLACE)
PVR_SET(PVR_FB_IL_ADDR, old_base + (vid_mode->width * vid_pmode_bpp[vid_mode->pm]));
thd_sleep(16);
}
/* Set the vram_* pointers as expected */
new_base = vid_mode->fb_size * ((vid_mode->fb_curr + 1) % vid_mode->fb_count);
vram_s = (uint16*)(PVR_RAM_BASE | new_base);
vram_l = (uint32*)(PVR_RAM_BASE | new_base);
return dir;
#endif /* VID_MAX_FB */
}
/* Load a single png into the current writable FB */
int fb_init(int which) {
char filename [80];
kos_img_t tmp_img;
/* Compose the current filename */
snprintf(filename, sizeof(filename), filenames[which], FILE_ROOT, vid_mode->width, vid_mode->height);
png_to_img(filename, PNG_NO_ALPHA, &tmp_img);
printf("Loading png %i (%s)\n", which, filename);
memcpy(vram_s, tmp_img.data, tmp_img.byte_count);
kos_img_free(&tmp_img, 0);
return 0;
}
int main(int argc, char **argv) {
uint32 done, start_secs, cur_secs, old_buttons = 0;
int f;
char dir = 1;
/* Press all buttons to exit */
cont_btn_callback(0, CONT_START | CONT_A | CONT_B | CONT_X | CONT_Y,
(cont_btn_callback_t)arch_exit);
/* First set the video mode */
vid_set_mode(DM_640x480 | DM_MULTIBUFFER, PM_RGB565);
/* Make sure we have enough FBs to hold the test images */
if((FILE_COUNT) > vid_mode->fb_count) {
printf("Not enough framebuffers to hold all the files, bailing.\n");
return -1;
}
/* Now restrain the framebuffer cycling to the total we can load */
vid_mode->fb_count = FILE_COUNT;
/* Blank the screen so we don't get rapid-fire of the FB's loading */
vid_disable();
/* Load all the files */
for(f=0; f < FILE_COUNT; f++) {
/* If we could load the file, move to the next FB */
if(fb_init(f) == 0) {
vid_flip(-1);
}
}
/* Now that all are loaded, lets reenable the screen */
vid_enable();
/* Lets set a counter that we should rotate through each at least once */
done = FILE_COUNT * 2;
/* Get a timestamp so that we can flip without intervention.
any button press will disable this timed system until the next mode. */
timer_ms_gettime(&start_secs, 0);
/* keep drawing frames until start is pressed */
while(done) {
/* Find the first controller and try to get it's state, if it's there */
cont_state_t * st = (cont_state_t *) maple_dev_status(maple_enum_type(0, MAPLE_FUNC_CONTROLLER));
/* If there was a controller state and it is different from our last one, check it out */
if((st != NULL) && (st->buttons != old_buttons)) {
/* Update our last state, this prevents key repeat */
old_buttons = st->buttons;
if(st->buttons & CONT_START) {
done = 0;
continue;
}
else if(st->buttons & CONT_A) {
vid_shake(-1);
start_secs = 0;
}
else if(st->buttons & CONT_B) {
vid_rotate(5);
start_secs = 0;
}
else if(st->buttons & CONT_Y) {
dir = vid_flip_fun(dir, 2);
start_secs = 0;
}
}
/* If the timer is still active lets check it */
if(start_secs) {
timer_ms_gettime(&cur_secs, 0);
if(cur_secs > (start_secs + TIMEOUT_SECS)) {
start_secs = cur_secs;
//vid_flip(-1);
vid_squish(-1);
done--;
continue;
}
}
}
return 0;
}
TARGET = FB_tricks.elf
OBJS = FB_tricks.o romdisk.o
KOS_ROMDISK_DIR = romdisk
all: rm-elf $(TARGET)
include $(KOS_BASE)/Makefile.rules
clean: rm-elf
-rm -f $(OBJS)
rm-elf:
-rm -f $(TARGET) romdisk.*
$(TARGET): $(OBJS)
kos-cc -o $(TARGET) $(OBJS) -lkosutils -lpng -lz -lm
run: $(TARGET)
$(KOS_LOADER) $(TARGET)
dist: $(TARGET)
-rm -f $(OBJS) romdisk_boot.img
$(KOS_STRIP) $(TARGET)
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment