kopia lustrzana https://github.com/Jean-MarcHarvengt/MCUME
1718 wiersze
46 KiB
C++
Executable File
1718 wiersze
46 KiB
C++
Executable File
/*
|
|
This file is part of VGA_t4 library.
|
|
|
|
VGA_t4 library is free software: you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation, either version 3 of the License, or
|
|
(at your option) any later version.
|
|
|
|
Copyright (C) 2020 J-M Harvengt
|
|
|
|
Inspired from the original Teensy3 uVGA library of Eric PREVOTEAU.
|
|
QTIMER/FlexIO code based on Teensy4 examples of KurtE, Manitou and easone
|
|
from the Teensy4 forum (https://forum.pjrc.com)
|
|
*/
|
|
|
|
#include "VGA_t4.h"
|
|
#include "VGA_font8x8.h"
|
|
#include "pico/stdlib.h"
|
|
#include "pico/multicore.h"
|
|
#include "scanvideo.h"
|
|
#include "composable_scanline.h"
|
|
#include "pico/sync.h"
|
|
#include "hardware/irq.h"
|
|
#include <string.h>
|
|
|
|
#include "platform_config.h"
|
|
#include "iopins.h"
|
|
|
|
#ifdef USE_VGA
|
|
|
|
#define R16(rgb) ((rgb>>8)&0xf8)
|
|
#define G16(rgb) ((rgb>>3)&0xfc)
|
|
#define B16(rgb) ((rgb<<3)&0xf8)
|
|
|
|
#define PIO_FB_OFFSET 4
|
|
|
|
// 8 bits 320x240 frame buffer => 64K
|
|
static vga_pixel * visible_framebuffer = NULL;
|
|
static vga_pixel * framebuffer = NULL;
|
|
static vga_pixel * fb0 = NULL;
|
|
|
|
uint32_t * pio_fb = NULL;
|
|
int pio_fbwidth=0;
|
|
|
|
|
|
static vga_pixel * fb1 = NULL;
|
|
|
|
static int fb_width;
|
|
static int fb_height;
|
|
static int fb_stride;
|
|
static int left_border;
|
|
static int top_border;
|
|
|
|
static scanvideo_mode_t vga_mode;
|
|
|
|
static semaphore_t core1_initted;
|
|
static void core1_func();
|
|
|
|
PolyDef PolySet; // will contain a polygon data
|
|
|
|
#define RGBVAL16(r,g,b) ( (((b>>3)&0x1f)<<11) | (((g>>2)&0x3f)<<5) | (((r>>3)&0x1f)<<0) )
|
|
#define PICO_SCANVIDEO_PIXEL_FROM_RGBVAL8(rgb) (((rgb&0x3)<<(PICO_SCANVIDEO_PIXEL_BSHIFT))|(((rgb&0x1C)>>2)<<(PICO_SCANVIDEO_PIXEL_GSHIFT))|(((rgb&0xE0)>>5)<<(PICO_SCANVIDEO_PIXEL_RSHIFT)))
|
|
|
|
|
|
|
|
static void core1_sio_irq();
|
|
|
|
|
|
static void core1_func() {
|
|
// initialize video
|
|
scanvideo_setup(&vga_mode);
|
|
scanvideo_timing_enable(true);
|
|
|
|
multicore_fifo_clear_irq();
|
|
irq_set_exclusive_handler(SIO_IRQ_PROC1,core1_sio_irq);
|
|
//irq_set_priority (SIO_IRQ_PROC1, 129);
|
|
irq_set_enabled(SIO_IRQ_PROC1,true);
|
|
|
|
sem_release(&core1_initted);
|
|
|
|
while (true) {
|
|
tight_loop_contents();
|
|
}
|
|
}
|
|
|
|
static void init_pio_framebuffer(uint8_t * fb) {
|
|
// Code for the PIO + buffer data initialized with random
|
|
//memset((void*)fb,0, fb_stride*fb_height*sizeof(vga_pixel));
|
|
uint8_t col = 0;
|
|
for( int i=0; i<fb_height; i++)
|
|
{
|
|
uint8_t * p8 = &fb[fb_stride*i];
|
|
uint16_t * p = (uint16_t *)p8;
|
|
*p++ = COMPOSABLE_RAW_RUN;
|
|
*p++ = fb_width;
|
|
for (uint y = 0; y < fb_width/2; y++) {
|
|
col = VGA_RGB(rand() % 255,rand() % 255,rand() % 255);
|
|
*p++ = (col << 8) + col;
|
|
}
|
|
// black pixel to end line
|
|
//*p++ = COMPOSABLE_RAW_1P;
|
|
//*p++ = 0;
|
|
// end of line with alignment padding
|
|
*p++ = COMPOSABLE_EOL_SKIP_ALIGN;
|
|
*p++ = 0;
|
|
//pio_fbwidth = ((uint32_t *) p) - (uint32_t *)&fb[fb_stride*i];
|
|
}
|
|
}
|
|
|
|
|
|
VGA_T4::VGA_T4()
|
|
{
|
|
}
|
|
|
|
void VGA_T4::tweak_video(int shiftdelta, int numdelta, int denomdelta)
|
|
{
|
|
}
|
|
|
|
// display VGA image
|
|
vga_error_t VGA_T4::begin(vga_mode_t mode)
|
|
{
|
|
switch(mode) {
|
|
case VGA_MODE_320x240:
|
|
vga_mode = vga_mode_320x240_60;
|
|
fb_width = 320;
|
|
fb_height = 240;
|
|
left_border = 0;
|
|
top_border = 0;
|
|
fb_stride = fb_width+16;
|
|
break;
|
|
case VGA_MODE_352x240:
|
|
break;
|
|
case VGA_MODE_400x240:
|
|
vga_mode = vga_mode_tft_400x240_50;
|
|
fb_width = 400;
|
|
fb_height = 240;
|
|
left_border = 0;
|
|
top_border = 0;
|
|
fb_stride = fb_width+16;
|
|
break;
|
|
}
|
|
|
|
|
|
/* initialize gfx buffer */
|
|
if (fb0 == NULL) {
|
|
void *mallocpt = malloc(fb_stride*fb_height*sizeof(vga_pixel)+4);
|
|
fb0 = (vga_pixel *)((void*)((intptr_t)mallocpt & ~3));
|
|
}
|
|
|
|
|
|
init_pio_framebuffer(fb0);
|
|
pio_fb = (uint32_t*)fb0;
|
|
pio_fbwidth = (fb_width + 2*6)/4;
|
|
visible_framebuffer = fb0+PIO_FB_OFFSET;
|
|
framebuffer = fb0+PIO_FB_OFFSET;
|
|
|
|
|
|
// create a semaphore to be posted when audio init is complete
|
|
sem_init(&core1_initted, 0, 1);
|
|
|
|
multicore_launch_core1(core1_func);
|
|
|
|
// wait for initialization of audio to be complete
|
|
sem_acquire_blocking(&core1_initted);
|
|
|
|
return(VGA_OK);
|
|
}
|
|
|
|
void VGA_T4::end()
|
|
{
|
|
}
|
|
|
|
void VGA_T4::debug()
|
|
{
|
|
}
|
|
|
|
// retrieve size of the frame buffer
|
|
int VGA_T4::get_frame_buffer_size(int *width, int *height)
|
|
{
|
|
if (width != nullptr) *width = fb_width;
|
|
if (height != nullptr) *height = fb_height;
|
|
return fb_stride;
|
|
}
|
|
|
|
void VGA_T4::waitSync()
|
|
{
|
|
scanvideo_wait_for_vblank();
|
|
}
|
|
|
|
void VGA_T4::waitLine(int line)
|
|
{
|
|
// while (currentLine != line) {};
|
|
}
|
|
|
|
void VGA_T4::clear(vga_pixel color) {
|
|
int i,j;
|
|
for (j=0; j<fb_height; j++)
|
|
{
|
|
vga_pixel * dst=&framebuffer[j*fb_stride];
|
|
for (i=0; i<fb_width; i++)
|
|
{
|
|
*dst++ = color;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void VGA_T4::drawPixel(int x, int y, vga_pixel color){
|
|
if((x>=0) && (x<=fb_width) && (y>=0) && (y<=fb_height))
|
|
framebuffer[y*fb_stride+x] = color;
|
|
}
|
|
|
|
vga_pixel VGA_T4::getPixel(int x, int y){
|
|
return(framebuffer[y*fb_stride+x]);
|
|
}
|
|
|
|
vga_pixel * VGA_T4::getLineBuffer(int j) {
|
|
return (&framebuffer[j*fb_stride]);
|
|
}
|
|
|
|
void VGA_T4::drawRect(int16_t x, int16_t y, int16_t w, int16_t h, vga_pixel color) {
|
|
int i,j,l=y;
|
|
for (j=0; j<h; j++)
|
|
{
|
|
vga_pixel * dst=&framebuffer[l*fb_stride+x];
|
|
for (i=0; i<w; i++)
|
|
{
|
|
*dst++ = color;
|
|
}
|
|
l++;
|
|
}
|
|
}
|
|
|
|
void VGA_T4::drawText(int16_t x, int16_t y, const char * text, vga_pixel fgcolor, vga_pixel bgcolor, bool doublesize) {
|
|
vga_pixel c;
|
|
vga_pixel * dst;
|
|
|
|
while ((c = *text++)) {
|
|
const unsigned char * charpt=&font8x8[c][0];
|
|
|
|
int l=y;
|
|
for (int i=0;i<8;i++)
|
|
{
|
|
unsigned char bits;
|
|
if (doublesize) {
|
|
dst=&framebuffer[l*fb_stride+x];
|
|
bits = *charpt;
|
|
if (bits&0x01) *dst++=fgcolor;
|
|
else *dst++=bgcolor;
|
|
bits = bits >> 1;
|
|
if (bits&0x01) *dst++=fgcolor;
|
|
else *dst++=bgcolor;
|
|
bits = bits >> 1;
|
|
if (bits&0x01) *dst++=fgcolor;
|
|
else *dst++=bgcolor;
|
|
bits = bits >> 1;
|
|
if (bits&0x01) *dst++=fgcolor;
|
|
else *dst++=bgcolor;
|
|
bits = bits >> 1;
|
|
if (bits&0x01) *dst++=fgcolor;
|
|
else *dst++=bgcolor;
|
|
bits = bits >> 1;
|
|
if (bits&0x01) *dst++=fgcolor;
|
|
else *dst++=bgcolor;
|
|
bits = bits >> 1;
|
|
if (bits&0x01) *dst++=fgcolor;
|
|
else *dst++=bgcolor;
|
|
bits = bits >> 1;
|
|
if (bits&0x01) *dst++=fgcolor;
|
|
else *dst++=bgcolor;
|
|
l++;
|
|
}
|
|
dst=&framebuffer[l*fb_stride+x];
|
|
bits = *charpt++;
|
|
if (bits&0x01) *dst++=fgcolor;
|
|
else *dst++=bgcolor;
|
|
bits = bits >> 1;
|
|
if (bits&0x01) *dst++=fgcolor;
|
|
else *dst++=bgcolor;
|
|
bits = bits >> 1;
|
|
if (bits&0x01) *dst++=fgcolor;
|
|
else *dst++=bgcolor;
|
|
bits = bits >> 1;
|
|
if (bits&0x01) *dst++=fgcolor;
|
|
else *dst++=bgcolor;
|
|
bits = bits >> 1;
|
|
if (bits&0x01) *dst++=fgcolor;
|
|
else *dst++=bgcolor;
|
|
bits = bits >> 1;
|
|
if (bits&0x01) *dst++=fgcolor;
|
|
else *dst++=bgcolor;
|
|
bits = bits >> 1;
|
|
if (bits&0x01) *dst++=fgcolor;
|
|
else *dst++=bgcolor;
|
|
bits = bits >> 1;
|
|
if (bits&0x01) *dst++=fgcolor;
|
|
else *dst++=bgcolor;
|
|
l++;
|
|
}
|
|
x +=8;
|
|
}
|
|
}
|
|
|
|
void VGA_T4::drawSprite(int16_t x, int16_t y, const int16_t *bitmap) {
|
|
drawSprite(x,y,bitmap, 0,0,0,0);
|
|
}
|
|
|
|
void VGA_T4::drawSprite(int16_t x, int16_t y, const int16_t *bitmap, uint16_t arx, uint16_t ary, uint16_t arw, uint16_t arh)
|
|
{
|
|
int bmp_offx = 0;
|
|
int bmp_offy = 0;
|
|
int16_t *bmp_ptr;
|
|
|
|
int w =*bitmap++;
|
|
int h = *bitmap++;
|
|
|
|
|
|
if ( (arw == 0) || (arh == 0) ) {
|
|
// no crop window
|
|
arx = x;
|
|
ary = y;
|
|
arw = w;
|
|
arh = h;
|
|
}
|
|
else {
|
|
if ( (x>(arx+arw)) || ((x+w)<arx) || (y>(ary+arh)) || ((y+h)<ary) ) {
|
|
return;
|
|
}
|
|
|
|
// crop area
|
|
if ( (x > arx) && (x<(arx+arw)) ) {
|
|
arw = arw - (x-arx);
|
|
arx = arx + (x-arx);
|
|
} else {
|
|
bmp_offx = arx;
|
|
}
|
|
if ( ((x+w) > arx) && ((x+w)<(arx+arw)) ) {
|
|
arw -= (arx+arw-x-w);
|
|
}
|
|
if ( (y > ary) && (y<(ary+arh)) ) {
|
|
arh = arh - (y-ary);
|
|
ary = ary + (y-ary);
|
|
} else {
|
|
bmp_offy = ary;
|
|
}
|
|
if ( ((y+h) > ary) && ((y+h)<(ary+arh)) ) {
|
|
arh -= (ary+arh-y-h);
|
|
}
|
|
}
|
|
|
|
|
|
int l=ary;
|
|
bitmap = bitmap + bmp_offy*w + bmp_offx;
|
|
for (int row=0;row<arh; row++)
|
|
{
|
|
vga_pixel * dst=&framebuffer[l*fb_stride+arx];
|
|
bmp_ptr = (int16_t *)bitmap;
|
|
for (int col=0;col<arw; col++)
|
|
{
|
|
uint16_t pix= *bmp_ptr++;
|
|
*dst++ = VGA_RGB(R16(pix),G16(pix),B16(pix));
|
|
}
|
|
bitmap += w;
|
|
l++;
|
|
}
|
|
}
|
|
|
|
void VGA_T4::writeLine(int width, int height, int y, uint8_t *buf, vga_pixel *palette) {
|
|
if ( (height<fb_height) && (height > 2) ) y += (fb_height-height)/2;
|
|
vga_pixel * dst=&framebuffer[y*fb_stride];
|
|
if (width > fb_width) {
|
|
#ifdef TFT_LINEARINT
|
|
int delta = (width/(width-fb_width))-1;
|
|
int pos = delta;
|
|
for (int i=0; i<fb_width; i++)
|
|
{
|
|
uint16_t val = palette[*buf++];
|
|
pos--;
|
|
if (pos == 0) {
|
|
#ifdef LINEARINT_HACK
|
|
val = ((uint32_t)palette[*buf++] + val)/2;
|
|
#else
|
|
uint16_t val2 = *buf++;
|
|
val = RGBVAL16((R16(val)+R16(val2))/2,(G16(val)+G16(val2))/2,(B16(val)+B16(val2))/2);
|
|
#endif
|
|
pos = delta;
|
|
}
|
|
*dst++=val;
|
|
}
|
|
#else
|
|
int step = ((width << 8)/fb_width);
|
|
int pos = 0;
|
|
for (int i=0; i<fb_width; i++)
|
|
{
|
|
*dst++=palette[buf[pos >> 8]];
|
|
pos +=step;
|
|
}
|
|
#endif
|
|
}
|
|
else if ((width*2) == fb_width) {
|
|
for (int i=0; i<width; i++)
|
|
{
|
|
*dst++=palette[*buf];
|
|
*dst++=palette[*buf++];
|
|
}
|
|
}
|
|
else {
|
|
if (width <= fb_width) {
|
|
dst += (fb_width-width)/2;
|
|
}
|
|
for (int i=0; i<width; i++)
|
|
{
|
|
*dst++=palette[*buf++];
|
|
}
|
|
}
|
|
}
|
|
|
|
void VGA_T4::writeLine(int width, int height, int y, vga_pixel *buf) {
|
|
if ( (height<fb_height) && (height > 2) ) y += (fb_height-height)/2;
|
|
uint8_t * dst=&framebuffer[y*fb_stride];
|
|
if (width > fb_width) {
|
|
int step = ((width << 8)/fb_width);
|
|
int pos = 0;
|
|
for (int i=0; i<fb_width; i++)
|
|
{
|
|
*dst++ = buf[pos >> 8];
|
|
pos +=step;
|
|
}
|
|
}
|
|
else if ((width*2) == fb_width) {
|
|
for (int i=0; i<width; i++)
|
|
{
|
|
*dst++=*buf;
|
|
*dst++=*buf++;
|
|
}
|
|
}
|
|
else {
|
|
if (width <= fb_width) {
|
|
dst += (fb_width-width)/2;
|
|
}
|
|
for (int i=0; i<width; i++)
|
|
{
|
|
*dst++=*buf++;
|
|
}
|
|
}
|
|
}
|
|
|
|
void VGA_T4::writeLine16(int width, int height, int y, uint16_t *buf) {
|
|
if ( (height<fb_height) && (height > 2) ) y += (fb_height-height)/2;
|
|
uint8_t * dst=&framebuffer[y*fb_stride];
|
|
if (width > fb_width) {
|
|
int step = ((width << 8)/fb_width);
|
|
int pos = 0;
|
|
for (int i=0; i<fb_width; i++)
|
|
{
|
|
uint16_t pix = buf[pos >> 8];
|
|
*dst++ = VGA_RGB(R16(pix),G16(pix),B16(pix));
|
|
pos +=step;
|
|
}
|
|
}
|
|
else if ((width*2) == fb_width) {
|
|
for (int i=0; i<width; i++)
|
|
{
|
|
uint16_t pix = *buf++;
|
|
uint8_t col = VGA_RGB(R16(pix),G16(pix),B16(pix));
|
|
*dst++= col;
|
|
*dst++= col;
|
|
}
|
|
}
|
|
else {
|
|
if (width <= fb_width) {
|
|
dst += (fb_width-width)/2;
|
|
}
|
|
for (int i=0; i<width; i++)
|
|
{
|
|
uint16_t pix = *buf++;
|
|
*dst++=VGA_RGB(R16(pix),G16(pix),B16(pix));
|
|
}
|
|
}
|
|
}
|
|
|
|
void VGA_T4::writeScreen(int width, int height, int stride, uint8_t *buf, vga_pixel *palette) {
|
|
uint8_t *buffer=buf;
|
|
uint8_t *src;
|
|
|
|
int i,j,y=0;
|
|
if (width*2 <= fb_width) {
|
|
for (j=0; j<height; j++)
|
|
{
|
|
vga_pixel * dst=&framebuffer[y*fb_stride];
|
|
src=buffer;
|
|
for (i=0; i<width; i++)
|
|
{
|
|
vga_pixel val = palette[*src++];
|
|
*dst++ = val;
|
|
*dst++ = val;
|
|
}
|
|
y++;
|
|
if (height*2 <= fb_height) {
|
|
dst=&framebuffer[y*fb_stride];
|
|
src=buffer;
|
|
for (i=0; i<width; i++)
|
|
{
|
|
vga_pixel val = palette[*src++];
|
|
*dst++ = val;
|
|
*dst++ = val;
|
|
}
|
|
y++;
|
|
}
|
|
buffer += stride;
|
|
}
|
|
}
|
|
else if (width <= fb_width) {
|
|
//dst += (fb_width-width)/2;
|
|
for (j=0; j<height; j++)
|
|
{
|
|
vga_pixel * dst=&framebuffer[y*fb_stride+(fb_width-width)/2];
|
|
src=buffer;
|
|
for (i=0; i<width; i++)
|
|
{
|
|
vga_pixel val = palette[*src++];
|
|
*dst++ = val;
|
|
}
|
|
y++;
|
|
if (height*2 <= fb_height) {
|
|
dst=&framebuffer[y*fb_stride+(fb_width-width)/2];
|
|
src=buffer;
|
|
for (i=0; i<width; i++)
|
|
{
|
|
vga_pixel val = palette[*src++];
|
|
*dst++ = val;
|
|
}
|
|
y++;
|
|
}
|
|
buffer += stride;
|
|
}
|
|
}
|
|
}
|
|
|
|
void VGA_T4::copyLine(int width, int height, int ysrc, int ydst) {
|
|
if ( (height<fb_height) && (height > 2) ) {
|
|
ysrc += (fb_height-height)/2;
|
|
ydst += (fb_height-height)/2;
|
|
}
|
|
uint8_t * src=&framebuffer[ysrc*fb_stride];
|
|
uint8_t * dst=&framebuffer[ydst*fb_stride];
|
|
memcpy(dst,src,width);
|
|
}
|
|
|
|
|
|
//--------------------------------------------------------------
|
|
// Draw a line between 2 points
|
|
// x1,y1 : 1st point
|
|
// x2,y2 : 2nd point
|
|
// Color : 16bits color
|
|
//--------------------------------------------------------------
|
|
void VGA_T4::drawline(int16_t x1, int16_t y1, int16_t x2, int16_t y2, vga_pixel color){
|
|
uint8_t yLonger = 0;
|
|
int incrementVal, endVal;
|
|
int shortLen = y2-y1;
|
|
int longLen = x2-x1;
|
|
int decInc;
|
|
int j = 0, i = 0;
|
|
|
|
if(ABS(shortLen) > ABS(longLen)) {
|
|
int swap = shortLen;
|
|
shortLen = longLen;
|
|
longLen = swap;
|
|
yLonger = 1;
|
|
}
|
|
|
|
endVal = longLen;
|
|
|
|
if(longLen < 0) {
|
|
incrementVal = -1;
|
|
longLen = -longLen;
|
|
endVal--;
|
|
} else {
|
|
incrementVal = 1;
|
|
endVal++;
|
|
}
|
|
|
|
if(longLen == 0)
|
|
decInc = 0;
|
|
else
|
|
decInc = (shortLen << 16) / longLen;
|
|
|
|
if(yLonger) {
|
|
for(i = 0;i != endVal;i += incrementVal) {
|
|
drawPixel(x1 + (j >> 16),y1 + i,color);
|
|
j += decInc;
|
|
}
|
|
} else {
|
|
for(i = 0;i != endVal;i += incrementVal) {
|
|
drawPixel(x1 + i,y1 + (j >> 16),color);
|
|
j += decInc;
|
|
}
|
|
}
|
|
}
|
|
|
|
//--------------------------------------------------------------
|
|
// Draw a horizontal line
|
|
// x1,y1 : starting point
|
|
// lenght : lenght in pixels
|
|
// color : 16bits color
|
|
//--------------------------------------------------------------
|
|
void VGA_T4::draw_h_line(int16_t x, int16_t y, int16_t lenght, vga_pixel color){
|
|
drawline(x , y , x + lenght , y , color);
|
|
}
|
|
|
|
//--------------------------------------------------------------
|
|
// Draw a vertical line
|
|
// x1,y1 : starting point
|
|
// lenght : lenght in pixels
|
|
// color : 16bits color
|
|
//--------------------------------------------------------------
|
|
void VGA_T4::draw_v_line(int16_t x, int16_t y, int16_t lenght, vga_pixel color){
|
|
drawline(x , y , x , y + lenght , color);
|
|
}
|
|
|
|
//--------------------------------------------------------------
|
|
// Draw a circle.
|
|
// x, y - center of circle.
|
|
// r - radius.
|
|
// color - color of the circle.
|
|
//--------------------------------------------------------------
|
|
void VGA_T4::drawcircle(int16_t x, int16_t y, int16_t radius, vga_pixel color){
|
|
int16_t a, b, P;
|
|
|
|
a = 0;
|
|
b = radius;
|
|
P = 1 - radius;
|
|
|
|
do {
|
|
drawPixel(a+x, b+y, color);
|
|
drawPixel(b+x, a+y, color);
|
|
drawPixel(x-a, b+y, color);
|
|
drawPixel(x-b, a+y, color);
|
|
drawPixel(b+x, y-a, color);
|
|
drawPixel(a+x, y-b, color);
|
|
drawPixel(x-a, y-b, color);
|
|
drawPixel(x-b, y-a, color);
|
|
|
|
if(P < 0)
|
|
P+= 3 + 2*a++;
|
|
else
|
|
P+= 5 + 2*(a++ - b--);
|
|
} while(a <= b);
|
|
}
|
|
|
|
//--------------------------------------------------------------
|
|
// Displays a full circle.
|
|
// x : specifies the X position
|
|
// y : specifies the Y position
|
|
// radius : specifies the Circle Radius
|
|
// fillcolor : specifies the Circle Fill Color
|
|
// bordercolor: specifies the Circle Border Color
|
|
//--------------------------------------------------------------
|
|
void VGA_T4::drawfilledcircle(int16_t x, int16_t y, int16_t radius, vga_pixel fillcolor, vga_pixel bordercolor){
|
|
int32_t D; /* Decision Variable */
|
|
uint32_t CurX;/* Current X Value */
|
|
uint32_t CurY;/* Current Y Value */
|
|
|
|
D = 3 - (radius << 1);
|
|
|
|
CurX = 0;
|
|
CurY = radius;
|
|
|
|
while (CurX <= CurY)
|
|
{
|
|
if(CurY > 0)
|
|
{
|
|
draw_v_line(x - CurX, y - CurY, 2*CurY, fillcolor);
|
|
draw_v_line(x + CurX, y - CurY, 2*CurY, fillcolor);
|
|
}
|
|
|
|
if(CurX > 0)
|
|
{
|
|
draw_v_line(x - CurY, y - CurX, 2*CurX, fillcolor);
|
|
draw_v_line(x + CurY, y - CurX, 2*CurX, fillcolor);
|
|
}
|
|
if (D < 0)
|
|
{
|
|
D += (CurX << 2) + 6;
|
|
}
|
|
else
|
|
{
|
|
D += ((CurX - CurY) << 2) + 10;
|
|
CurY--;
|
|
}
|
|
CurX++;
|
|
}
|
|
|
|
drawcircle(x, y, radius,bordercolor);
|
|
}
|
|
|
|
//--------------------------------------------------------------
|
|
// Displays an Ellipse.
|
|
// cx: specifies the X position
|
|
// cy: specifies the Y position
|
|
// radius1: minor radius of ellipse.
|
|
// radius2: major radius of ellipse.
|
|
// color: specifies the Color to use for draw the Border from the Ellipse.
|
|
//--------------------------------------------------------------
|
|
void VGA_T4::drawellipse(int16_t cx, int16_t cy, int16_t radius1, int16_t radius2, vga_pixel color){
|
|
int x = -radius1, y = 0, err = 2-2*radius1, e2;
|
|
float K = 0, rad1 = 0, rad2 = 0;
|
|
|
|
rad1 = radius1;
|
|
rad2 = radius2;
|
|
|
|
if (radius1 > radius2)
|
|
{
|
|
do {
|
|
K = (float)(rad1/rad2);
|
|
drawPixel(cx-x,cy+(uint16_t)(y/K),color);
|
|
drawPixel(cx+x,cy+(uint16_t)(y/K),color);
|
|
drawPixel(cx+x,cy-(uint16_t)(y/K),color);
|
|
drawPixel(cx-x,cy-(uint16_t)(y/K),color);
|
|
|
|
e2 = err;
|
|
if (e2 <= y) {
|
|
err += ++y*2+1;
|
|
if (-x == y && e2 <= x) e2 = 0;
|
|
}
|
|
if (e2 > x) err += ++x*2+1;
|
|
}
|
|
while (x <= 0);
|
|
}
|
|
else
|
|
{
|
|
y = -radius2;
|
|
x = 0;
|
|
do {
|
|
K = (float)(rad2/rad1);
|
|
drawPixel(cx-(uint16_t)(x/K),cy+y,color);
|
|
drawPixel(cx+(uint16_t)(x/K),cy+y,color);
|
|
drawPixel(cx+(uint16_t)(x/K),cy-y,color);
|
|
drawPixel(cx-(uint16_t)(x/K),cy-y,color);
|
|
|
|
e2 = err;
|
|
if (e2 <= x) {
|
|
err += ++x*2+1;
|
|
if (-y == x && e2 <= y) e2 = 0;
|
|
}
|
|
if (e2 > y) err += ++y*2+1;
|
|
}
|
|
while (y <= 0);
|
|
}
|
|
}
|
|
|
|
// Draw a filled ellipse.
|
|
// cx: specifies the X position
|
|
// cy: specifies the Y position
|
|
// radius1: minor radius of ellipse.
|
|
// radius2: major radius of ellipse.
|
|
// fillcolor : specifies the Color to use for Fill the Ellipse.
|
|
// bordercolor: specifies the Color to use for draw the Border from the Ellipse.
|
|
void VGA_T4::drawfilledellipse(int16_t cx, int16_t cy, int16_t radius1, int16_t radius2, vga_pixel fillcolor, vga_pixel bordercolor){
|
|
int x = -radius1, y = 0, err = 2-2*radius1, e2;
|
|
float K = 0, rad1 = 0, rad2 = 0;
|
|
|
|
rad1 = radius1;
|
|
rad2 = radius2;
|
|
|
|
if (radius1 > radius2)
|
|
{
|
|
do
|
|
{
|
|
K = (float)(rad1/rad2);
|
|
draw_v_line((cx+x), (cy-(uint16_t)(y/K)), (2*(uint16_t)(y/K) + 1) , fillcolor);
|
|
draw_v_line((cx-x), (cy-(uint16_t)(y/K)), (2*(uint16_t)(y/K) + 1) , fillcolor);
|
|
|
|
e2 = err;
|
|
if (e2 <= y)
|
|
{
|
|
err += ++y*2+1;
|
|
if (-x == y && e2 <= x) e2 = 0;
|
|
}
|
|
if (e2 > x) err += ++x*2+1;
|
|
|
|
}
|
|
while (x <= 0);
|
|
}
|
|
else
|
|
{
|
|
y = -radius2;
|
|
x = 0;
|
|
do
|
|
{
|
|
K = (float)(rad2/rad1);
|
|
draw_h_line((cx-(uint16_t)(x/K)), (cy+y), (2*(uint16_t)(x/K) + 1) , fillcolor);
|
|
draw_h_line((cx-(uint16_t)(x/K)), (cy-y), (2*(uint16_t)(x/K) + 1) , fillcolor);
|
|
|
|
e2 = err;
|
|
if (e2 <= x)
|
|
{
|
|
err += ++x*2+1;
|
|
if (-y == x && e2 <= y) e2 = 0;
|
|
}
|
|
if (e2 > y) err += ++y*2+1;
|
|
}
|
|
while (y <= 0);
|
|
}
|
|
drawellipse(cx,cy,radius1,radius2,bordercolor);
|
|
}
|
|
|
|
//--------------------------------------------------------------
|
|
// Draw a Triangle.
|
|
// ax,ay, bx,by, cx,cy - the triangle points.
|
|
// color - color of the triangle.
|
|
//--------------------------------------------------------------
|
|
void VGA_T4::drawtriangle(int16_t ax, int16_t ay, int16_t bx, int16_t by, int16_t cx, int16_t cy, vga_pixel color){
|
|
drawline(ax , ay , bx , by , color);
|
|
drawline(bx , by , cx , cy , color);
|
|
drawline(cx , cy , ax , ay , color);
|
|
}
|
|
|
|
//--------------------------------------------------------------
|
|
// Draw a Filled Triangle.
|
|
// ax,ay, bx,by, cx,cy - the triangle points.
|
|
// fillcolor - specifies the Color to use for Fill the triangle.
|
|
// bordercolor - specifies the Color to use for draw the Border from the triangle.
|
|
//--------------------------------------------------------------
|
|
void VGA_T4::drawfilledtriangle(int16_t ax, int16_t ay, int16_t bx, int16_t by, int16_t cx, int16_t cy, vga_pixel fillcolor, vga_pixel bordercolor){
|
|
float ma, mb, mc ; //'gradient of the lines
|
|
float start, finish ; //'draw a line from start to finish!
|
|
float tempspace ; //'temporary storage for swapping values...
|
|
double x1,x2,x3 ;
|
|
double y1,y2,y3 ;
|
|
int16_t n ;
|
|
|
|
//' need to sort out ay, by and cy into order.. highest to lowest
|
|
//'
|
|
if(ay < by)
|
|
{
|
|
//'swap x's
|
|
tempspace = ax;
|
|
ax = bx;
|
|
bx = tempspace;
|
|
|
|
//'swap y's
|
|
tempspace = ay;
|
|
ay = by;
|
|
by = tempspace;
|
|
}
|
|
|
|
if(ay < cy)
|
|
{
|
|
//'swap x's
|
|
tempspace = ax;
|
|
ax = cx;
|
|
cx = tempspace;
|
|
|
|
//'swap y's
|
|
tempspace = ay;
|
|
ay = cy;
|
|
cy = tempspace;
|
|
}
|
|
|
|
if(by < cy)
|
|
{
|
|
//'swap x's
|
|
tempspace = bx;
|
|
bx = cx;
|
|
cx = tempspace;
|
|
|
|
//'swap y's
|
|
tempspace = by;
|
|
by = cy;
|
|
cy = tempspace;
|
|
}
|
|
|
|
//' Finally - copy the values in order...
|
|
|
|
x1 = ax; x2 = bx; x3 = cx;
|
|
y1 = ay; y2 = by; y3 = cy;
|
|
|
|
//'bodge if y coordinates are the same
|
|
if(y1 == y2) y2 = y2 + 0.01;
|
|
if(y2 == y3) y3 = y3 + 0.01;
|
|
if(y1 == y3) y3 = y3 + 0.01;
|
|
|
|
ma = (x1 - x2) / (y1 - y2);
|
|
mb = (x3 - x2) / (y2 - y3);
|
|
mc = (x3 - x1) / (y1 - y3);
|
|
|
|
//'from y1 to y2
|
|
for(n = 0;n >= (y2 - y1);n--)
|
|
{
|
|
start = n * mc;
|
|
finish = n * ma;
|
|
drawline((int16_t)(x1 - start), (int16_t)(n + y1), (int16_t)(x1 + finish), (int16_t)(n + y1), fillcolor);
|
|
}
|
|
|
|
|
|
//'and from y2 to y3
|
|
|
|
for(n = 0;n >= (y3 - y2);n--)
|
|
{
|
|
start = n * mc;
|
|
finish = n * mb;
|
|
drawline((int16_t)(x1 - start - ((y2 - y1) * mc)), (int16_t)(n + y2), (int16_t)(x2 - finish), (int16_t)(n + y2), fillcolor);
|
|
}
|
|
|
|
// draw the border color triangle
|
|
drawtriangle(ax,ay,bx,by,cx,cy,bordercolor);
|
|
}
|
|
|
|
|
|
//--------------------------------------------------------------
|
|
// Displays a Rectangle at a given Angle.
|
|
// centerx : specifies the center of the Rectangle.
|
|
// centery
|
|
// w,h : specifies the size of the Rectangle.
|
|
// angle : specifies the angle for drawing the rectangle
|
|
// color : specifies the Color to use for Fill the Rectangle.
|
|
//--------------------------------------------------------------
|
|
void VGA_T4::drawquad(int16_t centerx, int16_t centery, int16_t w, int16_t h, int16_t angle, vga_pixel color){
|
|
int16_t px[4],py[4];
|
|
float l;
|
|
float raddeg = 3.14159 / 180;
|
|
float w2 = w / 2.0;
|
|
float h2 = h / 2.0;
|
|
float vec = (w2*w2)+(h2*h2);
|
|
float w2l;
|
|
float pangle[4];
|
|
|
|
l = sqrtf(vec);
|
|
w2l = w2 / l;
|
|
pangle[0] = acosf(w2l) / raddeg;
|
|
pangle[1] = 180.0 - (acosf(w2l) / raddeg);
|
|
pangle[2] = 180.0 + (acosf(w2l) / raddeg);
|
|
pangle[3] = 360.0 - (acosf(w2l) / raddeg);
|
|
px[0] = (int16_t)(calcco[((int16_t)(pangle[0]) + angle) % 360] * l + centerx);
|
|
py[0] = (int16_t)(calcsi[((int16_t)(pangle[0]) + angle) % 360] * l + centery);
|
|
px[1] = (int16_t)(calcco[((int16_t)(pangle[1]) + angle) % 360] * l + centerx);
|
|
py[1] = (int16_t)(calcsi[((int16_t)(pangle[1]) + angle) % 360] * l + centery);
|
|
px[2] = (int16_t)(calcco[((int16_t)(pangle[2]) + angle) % 360] * l + centerx);
|
|
py[2] = (int16_t)(calcsi[((int16_t)(pangle[2]) + angle) % 360] * l + centery);
|
|
px[3] = (int16_t)(calcco[((int16_t)(pangle[3]) + angle) % 360] * l + centerx);
|
|
py[3] = (int16_t)(calcsi[((int16_t)(pangle[3]) + angle) % 360] * l + centery);
|
|
// here we draw the quad
|
|
drawline(px[0],py[0],px[1],py[1],color);
|
|
drawline(px[1],py[1],px[2],py[2],color);
|
|
drawline(px[2],py[2],px[3],py[3],color);
|
|
drawline(px[3],py[3],px[0],py[0],color);
|
|
}
|
|
|
|
//--------------------------------------------------------------
|
|
// Displays a filled Rectangle at a given Angle.
|
|
// centerx : specifies the center of the Rectangle.
|
|
// centery
|
|
// w,h : specifies the size of the Rectangle.
|
|
// angle : specifies the angle for drawing the rectangle
|
|
// fillcolor : specifies the Color to use for Fill the Rectangle.
|
|
// bordercolor : specifies the Color to use for draw the Border from the Rectangle.
|
|
//--------------------------------------------------------------
|
|
void VGA_T4::drawfilledquad(int16_t centerx, int16_t centery, int16_t w, int16_t h, int16_t angle, vga_pixel fillcolor, vga_pixel bordercolor){
|
|
int16_t px[4],py[4];
|
|
float l;
|
|
float raddeg = 3.14159 / 180;
|
|
float w2 = w / 2.0;
|
|
float h2 = h / 2.0;
|
|
float vec = (w2*w2)+(h2*h2);
|
|
float w2l;
|
|
float pangle[4];
|
|
|
|
l = sqrtf(vec);
|
|
w2l = w2 / l;
|
|
pangle[0] = acosf(w2l) / raddeg;
|
|
pangle[1] = 180.0 - (acosf(w2l) / raddeg);
|
|
pangle[2] = 180.0 + (acosf(w2l) / raddeg);
|
|
pangle[3] = 360.0 - (acosf(w2l) / raddeg);
|
|
px[0] = (int16_t)(calcco[((int16_t)(pangle[0]) + angle) % 360] * l + centerx);
|
|
py[0] = (int16_t)(calcsi[((int16_t)(pangle[0]) + angle) % 360] * l + centery);
|
|
px[1] = (int16_t)(calcco[((int16_t)(pangle[1]) + angle) % 360] * l + centerx);
|
|
py[1] = (int16_t)(calcsi[((int16_t)(pangle[1]) + angle) % 360] * l + centery);
|
|
px[2] = (int16_t)(calcco[((int16_t)(pangle[2]) + angle) % 360] * l + centerx);
|
|
py[2] = (int16_t)(calcsi[((int16_t)(pangle[2]) + angle) % 360] * l + centery);
|
|
px[3] = (int16_t)(calcco[((int16_t)(pangle[3]) + angle) % 360] * l + centerx);
|
|
py[3] = (int16_t)(calcsi[((int16_t)(pangle[3]) + angle) % 360] * l + centery);
|
|
// We draw 2 filled triangle for made the quad
|
|
// To be uniform we have to use only the Fillcolor
|
|
drawfilledtriangle(px[0],py[0],px[1],py[1],px[2],py[2],fillcolor,fillcolor);
|
|
drawfilledtriangle(px[2],py[2],px[3],py[3],px[0],py[0],fillcolor,fillcolor);
|
|
// here we draw the BorderColor from the quad
|
|
drawline(px[0],py[0],px[1],py[1],bordercolor);
|
|
drawline(px[1],py[1],px[2],py[2],bordercolor);
|
|
drawline(px[2],py[2],px[3],py[3],bordercolor);
|
|
drawline(px[3],py[3],px[0],py[0],bordercolor);
|
|
}
|
|
|
|
//--------------------------------------------------------------
|
|
// Displays a Polygon.
|
|
// centerx : are specified with PolySet.Center.x and y.
|
|
// centery
|
|
// cx : Translate the polygon in x direction
|
|
// cy : Translate the polygon in y direction
|
|
// bordercolor : specifies the Color to use for draw the Border from the polygon.
|
|
// polygon points : are specified with PolySet.Pts[n].x and y
|
|
// After the last polygon point , set PolySet.Pts[n + 1].x to 10000
|
|
// Max number of point for the polygon is set by MaxPolyPoint previously defined.
|
|
//--------------------------------------------------------------
|
|
void VGA_T4::drawpolygon(int16_t cx, int16_t cy, vga_pixel bordercolor){
|
|
uint8_t n = 1;
|
|
while((PolySet.Pts[n].x < 10000) && (n < MaxPolyPoint)){
|
|
drawline(PolySet.Pts[n].x + cx,
|
|
PolySet.Pts[n].y + cy,
|
|
PolySet.Pts[n - 1].x + cx ,
|
|
PolySet.Pts[n - 1].y + cy,
|
|
bordercolor);
|
|
n++;
|
|
}
|
|
// close the polygon
|
|
drawline(PolySet.Pts[0].x + cx,
|
|
PolySet.Pts[0].y + cy,
|
|
PolySet.Pts[n - 1].x + cx,
|
|
PolySet.Pts[n - 1].y + cy,
|
|
bordercolor);
|
|
}
|
|
|
|
//--------------------------------------------------------------
|
|
// Displays a filled Polygon.
|
|
// centerx : are specified with PolySet.Center.x and y.
|
|
// centery
|
|
// cx : Translate the polygon in x direction
|
|
// cy : Translate the polygon in y direction
|
|
// fillcolor : specifies the Color to use for filling the polygon.
|
|
// bordercolor : specifies the Color to use for draw the Border from the polygon.
|
|
// polygon points : are specified with PolySet.Pts[n].x and y
|
|
// After the last polygon point , set PolySet.Pts[n + 1].x to 10000
|
|
// Max number of point for the polygon is set by MaxPolyPoint previously defined.
|
|
//--------------------------------------------------------------
|
|
void VGA_T4::drawfullpolygon(int16_t cx, int16_t cy, vga_pixel fillcolor, vga_pixel bordercolor){
|
|
int n,i,j,k,dy,dx;
|
|
int y,temp;
|
|
int a[MaxPolyPoint][2],xi[MaxPolyPoint];
|
|
float slope[MaxPolyPoint];
|
|
|
|
n = 0;
|
|
|
|
while((PolySet.Pts[n].x < 10000) && (n < MaxPolyPoint)){
|
|
a[n][0] = PolySet.Pts[n].x;
|
|
a[n][1] = PolySet.Pts[n].y;
|
|
n++;
|
|
}
|
|
|
|
a[n][0]=PolySet.Pts[0].x;
|
|
a[n][1]=PolySet.Pts[0].y;
|
|
|
|
for(i=0;i<n;i++)
|
|
{
|
|
dy=a[i+1][1]-a[i][1];
|
|
dx=a[i+1][0]-a[i][0];
|
|
|
|
if(dy==0) slope[i]=1.0;
|
|
if(dx==0) slope[i]=0.0;
|
|
|
|
if((dy!=0)&&(dx!=0)) /*- calculate inverse slope -*/
|
|
{
|
|
slope[i]=(float) dx/dy;
|
|
}
|
|
}
|
|
|
|
for(y=0;y< 480;y++)
|
|
{
|
|
k=0;
|
|
for(i=0;i<n;i++)
|
|
{
|
|
|
|
if( ((a[i][1]<=y)&&(a[i+1][1]>y))||
|
|
((a[i][1]>y)&&(a[i+1][1]<=y)))
|
|
{
|
|
xi[k]=(int)(a[i][0]+slope[i]*(y-a[i][1]));
|
|
k++;
|
|
}
|
|
}
|
|
|
|
for(j=0;j<k-1;j++) /*- Arrange x-intersections in order -*/
|
|
for(i=0;i<k-1;i++)
|
|
{
|
|
if(xi[i]>xi[i+1])
|
|
{
|
|
temp=xi[i];
|
|
xi[i]=xi[i+1];
|
|
xi[i+1]=temp;
|
|
}
|
|
}
|
|
|
|
for(i=0;i<k;i+=2)
|
|
{
|
|
drawline(xi[i] + cx,y + cy,xi[i+1]+1 + cx,y + cy, fillcolor);
|
|
}
|
|
|
|
}
|
|
|
|
// Draw the polygon outline
|
|
drawpolygon(cx , cy , bordercolor);
|
|
}
|
|
|
|
//--------------------------------------------------------------
|
|
// Displays a rotated Polygon.
|
|
// centerx : are specified with PolySet.Center.x and y.
|
|
// centery
|
|
// cx : Translate the polygon in x direction
|
|
// ct : Translate the polygon in y direction
|
|
// bordercolor : specifies the Color to use for draw the Border from the polygon.
|
|
// polygon points : are specified with PolySet.Pts[n].x and y
|
|
// After the last polygon point , set PolySet.Pts[n + 1].x to 10000
|
|
// Max number of point for the polygon is set by MaxPolyPoint previously defined.
|
|
//--------------------------------------------------------------
|
|
void VGA_T4::drawrotatepolygon(int16_t cx, int16_t cy, int16_t Angle, vga_pixel fillcolor, vga_pixel bordercolor, uint8_t filled)
|
|
{
|
|
Point2D SavePts[MaxPolyPoint];
|
|
uint16_t n = 0;
|
|
int16_t ctx,cty;
|
|
float raddeg = 3.14159 / 180;
|
|
float angletmp;
|
|
float tosquare;
|
|
float ptsdist;
|
|
|
|
ctx = PolySet.Center.x;
|
|
cty = PolySet.Center.y;
|
|
|
|
while((PolySet.Pts[n].x < 10000) && (n < MaxPolyPoint)){
|
|
// Save Original points coordinates
|
|
SavePts[n] = PolySet.Pts[n];
|
|
// Rotate and save all points
|
|
tosquare = ((PolySet.Pts[n].x - ctx) * (PolySet.Pts[n].x - ctx)) + ((PolySet.Pts[n].y - cty) * (PolySet.Pts[n].y - cty));
|
|
ptsdist = sqrtf(tosquare);
|
|
angletmp = atan2f(PolySet.Pts[n].y - cty,PolySet.Pts[n].x - ctx) / raddeg;
|
|
PolySet.Pts[n].x = (int16_t)((cosf((angletmp + Angle) * raddeg) * ptsdist) + ctx);
|
|
PolySet.Pts[n].y = (int16_t)((sinf((angletmp + Angle) * raddeg) * ptsdist) + cty);
|
|
n++;
|
|
}
|
|
|
|
if(filled != 0)
|
|
drawfullpolygon(cx , cy , fillcolor , bordercolor);
|
|
else
|
|
drawpolygon(cx , cy , bordercolor);
|
|
|
|
// Get the original points back;
|
|
n=0;
|
|
while((PolySet.Pts[n].x < 10000) && (n < MaxPolyPoint)){
|
|
PolySet.Pts[n] = SavePts[n];
|
|
n++;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*******************************************************************
|
|
Experimental GAME engine supporting:
|
|
- Multiple tiles layers with independent scrolling
|
|
- Sprites (MAX_SPRITES)
|
|
- up to 256 redefinable tiles
|
|
- up to 256 redefinable sprites
|
|
*******************************************************************/
|
|
struct Sprite_t {
|
|
int x;
|
|
int y;
|
|
unsigned char index;
|
|
};
|
|
|
|
static vga_pixel * tilesbuffer __attribute__((aligned(32))) = NULL;
|
|
static vga_pixel * spritesbuffer __attribute__((aligned(32))) = NULL;
|
|
static unsigned char * tilesram __attribute__((aligned(32))) = NULL;
|
|
static Sprite_t * spritesdata __attribute__((aligned(32))) = NULL;
|
|
static int nb_layers = 0;
|
|
static int nb_tiles = 0;
|
|
static int nb_sprites = 0;
|
|
static int hscr[TILES_MAX_LAYERS];
|
|
static int vscr[TILES_MAX_LAYERS];
|
|
static int hscr_beg[TILES_MAX_LAYERS]={0,0};
|
|
static int hscr_end[TILES_MAX_LAYERS]={TILES_ROWS-1, TILES_ROWS-1};
|
|
static int hscr_mask=0;
|
|
|
|
|
|
static void drawSpr(unsigned char index, int x, int y) {
|
|
if ((x + SPRITES_W) <= 0) return;
|
|
if (x >= (fb_width-hscr_mask)) return;
|
|
if ((y + SPRITES_H) <= 0) return;
|
|
if (y >= fb_height) return;
|
|
|
|
vga_pixel * src=&spritesbuffer[index*SPRITES_W*SPRITES_H];
|
|
int i,j;
|
|
vga_pixel pix;
|
|
for (j=0; j<SPRITES_H; j++)
|
|
{
|
|
vga_pixel * dst=&framebuffer[((j+y)*fb_stride)+x];
|
|
for (i=0; i<SPRITES_W; i++)
|
|
{
|
|
pix=*src++;
|
|
if ( (!pix) || ((x+i) < 0) || ((x+i) > (fb_width-hscr_mask)) || ((y+j) < 0) || ((y+j) >= fb_height) ) dst++;
|
|
else *dst++ = pix;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
static void drawTile(unsigned char tile, int x, int y) {
|
|
vga_pixel * src=&tilesbuffer[tile*TILES_W*TILES_H];
|
|
int i,j;
|
|
for (j=0; j<TILES_H; j++)
|
|
{
|
|
vga_pixel * dst=&framebuffer[((j+y)*fb_stride)+x];
|
|
for (i=0; i<TILES_W; i++)
|
|
{
|
|
*dst++ = *src++;
|
|
}
|
|
}
|
|
}
|
|
|
|
static void drawTileCropL(unsigned char tile, int x, int y) {
|
|
vga_pixel * src=&tilesbuffer[tile*TILES_W*TILES_H];
|
|
int i,j;
|
|
vga_pixel pix;
|
|
for (j=0; j<TILES_H; j++)
|
|
{
|
|
vga_pixel * dst=&framebuffer[((j+y)*fb_stride)+x];
|
|
for (i=0; i<TILES_W; i++)
|
|
{
|
|
pix=*src++;
|
|
if ((x+i) < 0) dst++;
|
|
else
|
|
*dst++ = pix;
|
|
}
|
|
}
|
|
}
|
|
|
|
static void drawTileCropR(unsigned char tile, int x, int y) {
|
|
vga_pixel * src=&tilesbuffer[tile*TILES_W*TILES_H];
|
|
int i,j;
|
|
vga_pixel pix;
|
|
for (j=0; j<TILES_H; j++)
|
|
{
|
|
vga_pixel * dst=&framebuffer[((j+y)*fb_stride)+x];
|
|
for (i=0; i<TILES_W; i++)
|
|
{
|
|
pix = *src++;
|
|
if ((x+i) > (fb_width-hscr_mask)) *dst++=0;
|
|
else
|
|
*dst++ = pix;
|
|
}
|
|
}
|
|
}
|
|
|
|
static void drawTransTile(unsigned char tile, int x, int y) {
|
|
vga_pixel * src=&tilesbuffer[tile*TILES_W*TILES_H];
|
|
vga_pixel pix;
|
|
int i,j;
|
|
for (j=0; j<TILES_H; j++)
|
|
{
|
|
vga_pixel * dst=&framebuffer[((j+y)*fb_stride)+x];
|
|
for (i=0; i<TILES_W; i++)
|
|
{
|
|
if ((pix=*src++)) *dst++ = pix;
|
|
else dst++;
|
|
}
|
|
}
|
|
}
|
|
|
|
static void drawTransTileCropL(unsigned char tile, int x, int y) {
|
|
vga_pixel * src=&tilesbuffer[tile*TILES_W*TILES_H];
|
|
vga_pixel pix;
|
|
int i,j;
|
|
for (j=0; j<TILES_H; j++)
|
|
{
|
|
vga_pixel * dst=&framebuffer[((j+y)*fb_stride)+x];
|
|
for (i=0; i<TILES_W; i++)
|
|
{
|
|
pix=*src++;
|
|
if ((x+i) < 0) dst++;
|
|
else
|
|
if (pix) *dst++ = pix;
|
|
else dst++;
|
|
}
|
|
}
|
|
}
|
|
|
|
static void drawTransTileCropR(unsigned char tile, int x, int y) {
|
|
vga_pixel * src=&tilesbuffer[tile*TILES_W*TILES_H];
|
|
vga_pixel pix;
|
|
int i,j;
|
|
for (j=0; j<TILES_H; j++)
|
|
{
|
|
vga_pixel * dst=&framebuffer[((j+y)*fb_stride)+x];
|
|
for (i=0; i<TILES_W; i++)
|
|
{
|
|
if ((x+i) > (fb_width-hscr_mask)) src++;
|
|
else
|
|
if ((pix=*src++)) *dst++ = pix;
|
|
else *dst++;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
static void tileText(unsigned char index, int16_t x, int16_t y, const char * text, vga_pixel fgcolor, vga_pixel bgcolor, vga_pixel *dstbuffer, int dstwidth, int dstheight) {
|
|
vga_pixel c;
|
|
vga_pixel * dst;
|
|
|
|
while ((c = *text++)) {
|
|
const unsigned char * charpt=&font8x8[c][0];
|
|
int l=y;
|
|
for (int i=0;i<8;i++)
|
|
{
|
|
unsigned char bits;
|
|
dst=&dstbuffer[(index*dstheight+l)*dstwidth+x];
|
|
bits = *charpt++;
|
|
if (bits&0x01) *dst++=fgcolor;
|
|
else *dst++=bgcolor;
|
|
bits = bits >> 1;
|
|
if (bits&0x01) *dst++=fgcolor;
|
|
else *dst++=bgcolor;
|
|
bits = bits >> 1;
|
|
if (bits&0x01) *dst++=fgcolor;
|
|
else *dst++=bgcolor;
|
|
bits = bits >> 1;
|
|
if (bits&0x01) *dst++=fgcolor;
|
|
else *dst++=bgcolor;
|
|
bits = bits >> 1;
|
|
if (bits&0x01) *dst++=fgcolor;
|
|
else *dst++=bgcolor;
|
|
bits = bits >> 1;
|
|
if (bits&0x01) *dst++=fgcolor;
|
|
else *dst++=bgcolor;
|
|
bits = bits >> 1;
|
|
if (bits&0x01) *dst++=fgcolor;
|
|
else *dst++=bgcolor;
|
|
bits = bits >> 1;
|
|
if (bits&0x01) *dst++=fgcolor;
|
|
else *dst++=bgcolor;
|
|
l++;
|
|
}
|
|
x +=8;
|
|
}
|
|
}
|
|
|
|
static void tileTextOverlay(int16_t x, int16_t y, const char * text, vga_pixel fgcolor) {
|
|
vga_pixel c;
|
|
vga_pixel * dst;
|
|
|
|
while ((c = *text++)) {
|
|
const unsigned char * charpt=&font8x8[c][0];
|
|
int l=y;
|
|
for (int i=0;i<8;i++)
|
|
{
|
|
unsigned char bits;
|
|
dst=&framebuffer[+l*fb_stride+x];
|
|
bits = *charpt++;
|
|
if (bits&0x01) *dst++=fgcolor;
|
|
else dst++;
|
|
bits = bits >> 1;
|
|
if (bits&0x01) *dst++=fgcolor;
|
|
else dst++;
|
|
bits = bits >> 1;
|
|
if (bits&0x01) *dst++=fgcolor;
|
|
else dst++;
|
|
bits = bits >> 1;
|
|
if (bits&0x01) *dst++=fgcolor;
|
|
else dst++;
|
|
bits = bits >> 1;
|
|
if (bits&0x01) *dst++=fgcolor;
|
|
else dst++;
|
|
bits = bits >> 1;
|
|
if (bits&0x01) *dst++=fgcolor;
|
|
else dst++;
|
|
bits = bits >> 1;
|
|
if (bits&0x01) *dst++=fgcolor;
|
|
else dst++;
|
|
bits = bits >> 1;
|
|
if (bits&0x01) *dst++=fgcolor;
|
|
else dst++;
|
|
l++;
|
|
}
|
|
x +=8;
|
|
}
|
|
}
|
|
static const char * hex = "0123456789ABCDEF";
|
|
|
|
void VGA_T4::begin_gfxengine(int nblayers, int nbtiles, int nbsprites)
|
|
{
|
|
// Try double buffering
|
|
|
|
if (fb1 == NULL) {
|
|
void *mallocpt = malloc(fb_stride*fb_height*sizeof(vga_pixel)+4);
|
|
fb1 = (vga_pixel *)((void*)((intptr_t)mallocpt & ~3));
|
|
}
|
|
if (fb1 != NULL) {
|
|
init_pio_framebuffer(fb1);
|
|
framebuffer = fb1+PIO_FB_OFFSET;
|
|
}
|
|
|
|
|
|
nb_layers = nblayers;
|
|
nb_tiles = nbtiles;
|
|
nb_sprites = nbsprites;
|
|
|
|
if (spritesbuffer == NULL) spritesbuffer = (vga_pixel*)malloc(SPRITES_W*SPRITES_H*sizeof(vga_pixel)*nb_sprites);
|
|
if (tilesbuffer == NULL) tilesbuffer = (vga_pixel*)malloc(TILES_W*TILES_H*sizeof(vga_pixel)*nb_tiles);
|
|
if (tilesram == NULL) tilesram = (unsigned char*)malloc(TILES_COLS*TILES_ROWS*nb_layers);
|
|
if (spritesdata == NULL) spritesdata = (Sprite_t *)malloc(SPRITES_MAX*sizeof(Sprite_t));
|
|
|
|
memset((void*)spritesbuffer,0, SPRITES_W*SPRITES_H*sizeof(vga_pixel)*nb_sprites);
|
|
memset((void*)tilesbuffer,0, TILES_W*TILES_H*sizeof(vga_pixel)*nb_tiles);
|
|
memset((void*)tilesram,0,TILES_COLS*TILES_ROWS*nb_layers);
|
|
|
|
/* Random test tiles */
|
|
char numhex[3];
|
|
for (int i=0; i<nb_tiles; i++)
|
|
{
|
|
int r = rand() % 0xBF + 0x40;
|
|
int g = rand() % 0xBF + 0x40;
|
|
int b = rand() % 0xBF + 0x40;
|
|
if (i==0) {
|
|
memset((void*)&tilesbuffer[TILES_W*TILES_H*sizeof(vga_pixel)*i],0, TILES_W*TILES_H*sizeof(vga_pixel));
|
|
}
|
|
else {
|
|
memset((void*)&tilesbuffer[TILES_W*TILES_H*sizeof(vga_pixel)*i],VGA_RGB(r,g,b), TILES_W*TILES_H*sizeof(vga_pixel));
|
|
numhex[0] = hex[(i>>4) & 0xf];
|
|
numhex[1] = hex[i & 0xf];
|
|
numhex[2] = 0;
|
|
if (TILES_W == 16 )tileText(i, 0, 0, numhex, VGA_RGB(0xff,0xff,0xff), VGA_RGB(0x40,0x40,0x40), tilesbuffer,TILES_W,TILES_H);
|
|
}
|
|
}
|
|
/* Random test sprites */
|
|
for (int i=0; i<nb_sprites; i++)
|
|
{
|
|
int r = rand() % 0xBF + 0x40;
|
|
int g = rand() % 0xBF + 0x40;
|
|
int b = rand() % 0xBF + 0x40;
|
|
if (i==0) {
|
|
memset((void*)&spritesbuffer[SPRITES_W*SPRITES_H*sizeof(vga_pixel)*i],0, SPRITES_W*SPRITES_H*sizeof(vga_pixel));
|
|
}
|
|
else {
|
|
memset((void*)&spritesbuffer[SPRITES_W*SPRITES_H*sizeof(vga_pixel)*i],VGA_RGB(r,g,b), SPRITES_W*SPRITES_H*sizeof(vga_pixel));
|
|
numhex[0] = hex[(i>>4) & 0xf];
|
|
numhex[1] = hex[i & 0xf];
|
|
numhex[2] = 0;
|
|
tileText(i, 0, 0, numhex, VGA_RGB(0xff,0xff,0x00), VGA_RGB(0x00,0x00,0x00),spritesbuffer,SPRITES_W,SPRITES_H);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void VGA_T4::run_gfxengine()
|
|
{
|
|
waitSync();
|
|
|
|
if (fb1 != NULL) {
|
|
if (pio_fb == (uint32_t*)fb0) {
|
|
pio_fb = (uint32_t*)fb1;
|
|
visible_framebuffer = fb1+PIO_FB_OFFSET;
|
|
framebuffer = fb0+PIO_FB_OFFSET;
|
|
}
|
|
else {
|
|
pio_fb = (uint32_t*)fb0;
|
|
visible_framebuffer = fb0+PIO_FB_OFFSET;
|
|
framebuffer = fb1+PIO_FB_OFFSET;
|
|
}
|
|
}
|
|
|
|
unsigned char * tilept;
|
|
|
|
// Layer 0
|
|
for (int j=0; j<TILES_ROWS; j++)
|
|
{
|
|
tilept = &tilesram[j*TILES_COLS];
|
|
if ( (j>=hscr_beg[0]) && (j<=hscr_end[0]) ) {
|
|
int modcol = (hscr[0] >> TILES_HBITS) % TILES_COLS;
|
|
for (int i=0; i<TILES_COLS; i++)
|
|
{
|
|
(i == 0) ? drawTileCropL(tilept[modcol], (i<<TILES_HBITS) - (hscr[0] & TILES_HMASK), j*TILES_H) :
|
|
(i == (TILES_COLS-1))?drawTileCropR(tilept[modcol], (i<<TILES_HBITS) - (hscr[0] & TILES_HMASK), j*TILES_H) :
|
|
drawTile(tilept[modcol], (i<<TILES_HBITS) - (hscr[0] & TILES_HMASK), j*TILES_H);
|
|
modcol++;
|
|
modcol = modcol % TILES_COLS;
|
|
}
|
|
}
|
|
else {
|
|
for (int i=0; i<TILES_COLS; i++)
|
|
{
|
|
(i == (TILES_COLS-1)) ? drawTileCropR(tilept[i], (i<<TILES_HBITS), j*TILES_H) :
|
|
drawTile(tilept[i], (i<<TILES_HBITS), j*TILES_H);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Other layers
|
|
if (nb_layers > 1) {
|
|
int lcount = 1;
|
|
while (lcount < nb_layers) {
|
|
for (int j=0; j<TILES_ROWS; j++)
|
|
{
|
|
tilept = &tilesram[(j+lcount*TILES_ROWS)*TILES_COLS];
|
|
if ( (j>=hscr_beg[lcount]) && (j<=hscr_end[lcount]) ) {
|
|
int modcol = (hscr[lcount] >> TILES_HBITS) % TILES_COLS;
|
|
for (int i=0; i<TILES_COLS; i++)
|
|
{
|
|
(i == 0) ? drawTransTileCropL(tilept[modcol], (i<<TILES_HBITS) - (hscr[lcount] & TILES_HMASK), j*TILES_H) :
|
|
(i == (TILES_COLS-1))?drawTransTileCropR(tilept[modcol], (i<<TILES_HBITS) - (hscr[lcount] & TILES_HMASK), j*TILES_H) :
|
|
drawTransTile(tilept[modcol], (i<<TILES_HBITS) - (hscr[lcount] & TILES_HMASK), j*TILES_H);
|
|
modcol++;
|
|
modcol = modcol % TILES_COLS;
|
|
}
|
|
}
|
|
else {
|
|
for (int i=0; i<TILES_COLS; i++)
|
|
{
|
|
drawTransTile(tilept[i], (i<<TILES_HBITS), j*TILES_H);
|
|
}
|
|
}
|
|
}
|
|
lcount++;
|
|
}
|
|
}
|
|
|
|
/*
|
|
static char * ro="01234567890123456789012345678901234567";
|
|
for (int i=0; i<TILES_ROWS*2; i++)
|
|
{
|
|
tileTextOverlay(0, i*8, ro, VGA_RGB(0x00,0xff,0x00));
|
|
}
|
|
*/
|
|
|
|
for (int i=0; i<SPRITES_MAX; i++)
|
|
{
|
|
drawSpr(spritesdata[i].index, spritesdata[i].x, spritesdata[i].y);
|
|
}
|
|
|
|
}
|
|
|
|
void VGA_T4::tile_data(unsigned char index, vga_pixel * data, int len)
|
|
{
|
|
memcpy((void*)&tilesbuffer[index*TILES_W*TILES_H],(void*)data,len);
|
|
}
|
|
|
|
void VGA_T4::sprite_data(unsigned char index, vga_pixel * data, int len)
|
|
{
|
|
memcpy((void*)&spritesbuffer[index*SPRITES_W*SPRITES_H],(void*)data,len);
|
|
}
|
|
|
|
void VGA_T4::sprite(int id , int x, int y, unsigned char index)
|
|
{
|
|
if (id < SPRITES_MAX) {
|
|
spritesdata[id].x = x;
|
|
spritesdata[id].y = y;
|
|
spritesdata[id].index = index;
|
|
}
|
|
}
|
|
|
|
|
|
void VGA_T4::sprite_hide(int id)
|
|
{
|
|
if (id < SPRITES_MAX) {
|
|
spritesdata[id].x = -16;
|
|
spritesdata[id].y = -16;
|
|
spritesdata[id].index = 0;
|
|
}
|
|
}
|
|
|
|
void VGA_T4::tile_draw(int layer, int x, int y, unsigned char index)
|
|
{
|
|
tilesram[(y+layer*TILES_ROWS)*TILES_COLS+x] = index;
|
|
}
|
|
|
|
void VGA_T4::tile_draw_row(int layer, int x, int y, unsigned char * data, int len)
|
|
{
|
|
while (len--)
|
|
{
|
|
tilesram[(y+layer*TILES_ROWS)*TILES_COLS+x++] = *data++;
|
|
}
|
|
}
|
|
|
|
void VGA_T4::tile_draw_col(int layer, int x, int y, unsigned char * data, int len)
|
|
{
|
|
while (len--)
|
|
{
|
|
tilesram[(y++ +layer*TILES_ROWS)*TILES_COLS+x] = *data++;
|
|
}
|
|
}
|
|
|
|
void VGA_T4::hscroll(int layer, int value)
|
|
{
|
|
hscr[layer] = value;
|
|
}
|
|
|
|
void VGA_T4::vscroll(int layer, int value)
|
|
{
|
|
vscr[layer] = value;
|
|
}
|
|
|
|
void VGA_T4::set_hscroll(int layer, int rowbeg, int rowend, int mask)
|
|
{
|
|
hscr_beg[layer] = rowbeg;
|
|
hscr_end[layer] = rowend;
|
|
hscr_mask = mask+1;
|
|
}
|
|
|
|
void VGA_T4::set_vscroll(int layer, int colbeg, int colend, int mask)
|
|
{
|
|
hscr_beg[layer] = colbeg;
|
|
hscr_end[layer] = colend;
|
|
hscr_mask = mask+1;
|
|
}
|
|
|
|
|
|
/*******************************************************************
|
|
Experimental PWM interrupt based sound driver !!!
|
|
*******************************************************************/
|
|
#include "hardware/irq.h"
|
|
#include "hardware/pwm.h"
|
|
|
|
static bool fillfirsthalf = true;
|
|
static uint16_t cnt = 0;
|
|
static uint16_t sampleBufferSize = 0;
|
|
|
|
static void (*fillsamples)(short * stream, int len) = nullptr;
|
|
|
|
static uint32_t * i2s_tx_buffer;
|
|
static short * i2s_tx_buffer16;
|
|
|
|
static void SOFTWARE_isr() {
|
|
if (fillfirsthalf) {
|
|
fillsamples((short *)i2s_tx_buffer, sampleBufferSize);
|
|
}
|
|
else {
|
|
fillsamples((short *)&i2s_tx_buffer[sampleBufferSize/2], sampleBufferSize);
|
|
}
|
|
}
|
|
|
|
static void AUDIO_isr() {
|
|
pwm_clear_irq(pwm_gpio_to_slice_num(AUDIO_PIN));
|
|
long s = i2s_tx_buffer16[cnt++];
|
|
s += i2s_tx_buffer16[cnt++];
|
|
s = s/2 + 32767;
|
|
pwm_set_gpio_level(AUDIO_PIN, s >> 8);
|
|
cnt = cnt & (sampleBufferSize*2-1);
|
|
|
|
if (cnt == 0) {
|
|
fillfirsthalf = false;
|
|
//irq_set_pending(RTC_IRQ+1);
|
|
multicore_fifo_push_blocking(0);
|
|
}
|
|
else if (cnt == sampleBufferSize) {
|
|
fillfirsthalf = true;
|
|
//irq_set_pending(RTC_IRQ+1);
|
|
multicore_fifo_push_blocking(0);
|
|
}
|
|
}
|
|
|
|
static void core1_sio_irq() {
|
|
irq_clear(SIO_IRQ_PROC1);
|
|
while(multicore_fifo_rvalid()) {
|
|
uint16_t raw = multicore_fifo_pop_blocking();
|
|
SOFTWARE_isr();
|
|
}
|
|
multicore_fifo_clear_irq();
|
|
}
|
|
|
|
|
|
void VGA_T4::begin_audio(int samplesize, void (*callback)(short * stream, int len))
|
|
{
|
|
fillsamples = callback;
|
|
i2s_tx_buffer = (uint32_t*)malloc(samplesize*sizeof(uint32_t));
|
|
|
|
if (i2s_tx_buffer == NULL) {
|
|
printf("sound buffer could not be allocated!!!!!\n");
|
|
return;
|
|
}
|
|
memset((void*)i2s_tx_buffer,0, samplesize*sizeof(uint32_t));
|
|
printf("sound buffer allocated\n");
|
|
|
|
i2s_tx_buffer16 = (short*)i2s_tx_buffer;
|
|
|
|
sampleBufferSize = samplesize;
|
|
|
|
gpio_set_function(AUDIO_PIN, GPIO_FUNC_PWM);
|
|
int audio_pin_slice = pwm_gpio_to_slice_num(AUDIO_PIN);
|
|
// Setup PWM interrupt to fire when PWM cycle is complete
|
|
pwm_clear_irq(audio_pin_slice);
|
|
pwm_set_irq_enabled(audio_pin_slice, true);
|
|
irq_set_exclusive_handler(PWM_IRQ_WRAP, AUDIO_isr);
|
|
irq_set_priority (PWM_IRQ_WRAP, 128);
|
|
irq_set_enabled(PWM_IRQ_WRAP, true);
|
|
|
|
//irq_set_exclusive_handler(RTC_IRQ+1,SOFTWARE_isr);
|
|
//irq_set_priority (RTC_IRQ+1, 120);
|
|
//irq_set_enabled(RTC_IRQ+1,true);
|
|
|
|
|
|
// Setup PWM for audio output
|
|
pwm_config config = pwm_get_default_config();
|
|
// pwm_config_set_clkdiv(&config, 5.5f);
|
|
pwm_config_set_clkdiv(&config, 50.0f);
|
|
pwm_config_set_wrap(&config, 254);
|
|
pwm_init(audio_pin_slice, &config, true);
|
|
|
|
pwm_set_gpio_level(AUDIO_PIN, 0);
|
|
printf("sound initialized\n");
|
|
}
|
|
|
|
void VGA_T4::end_audio()
|
|
{
|
|
if (i2s_tx_buffer != NULL) {
|
|
free(i2s_tx_buffer);
|
|
}
|
|
}
|
|
|
|
#endif
|
|
|
|
|