mirror of
https://github.com/RaySollium99/picodrive.git
synced 2025-09-05 15:27:46 -04:00
1017 lines
26 KiB
C++
1017 lines
26 KiB
C++
// EmuScan routines for Pico, also simple text and shape drawing routines.
|
|
|
|
// (c) Copyright 2006, notaz
|
|
// All Rights Reserved
|
|
|
|
#include "vid.h"
|
|
#include "ClientServer.h"
|
|
#include "SimpleServer.h"
|
|
#include "pico\picoInt.h"
|
|
#include "blit.h"
|
|
#include "debug.h"
|
|
|
|
// global stuff
|
|
extern TPicoConfig currentConfig;
|
|
extern TPicoKeyConfigEntry *keyConfig;
|
|
extern TPicoAreaConfigEntry areaConfig[];
|
|
extern const char * actionNames[];
|
|
|
|
// main framebuffer
|
|
static void *screenbuff = 0; // pointer to real device video memory
|
|
//static
|
|
unsigned short *framebuff = 0; // temporary buffer in sega native BGR format
|
|
const int framebuffsize = (8+320)*(8+208+8)*2; // actual framebuffer size (in bytes+to support new rendering mode)
|
|
static int framebuff_offs = 0; // where to start copying (in pixels)
|
|
static int framebuff_len = 0; // how much of the framebuffer actually needs to be copied (in pixels)
|
|
static int fc_lines, fc_inc; // lines, inc for "0 fit2" mode
|
|
|
|
// drawer function pointers
|
|
void (*drawTextFps)(const char *text) = 0;
|
|
void (*drawTextNotice)(const char *text) = 0;
|
|
|
|
// blitter
|
|
void (*vidBlit)(int full) = 0;
|
|
void (*vidBlitKeyCfg)(int full) = 0;
|
|
|
|
// stuff for rendermode2
|
|
static unsigned short cram_high[0x40];
|
|
static unsigned short dt_dmask=0x0333;
|
|
unsigned short color_redM2 = 0x022F;
|
|
|
|
// colors
|
|
const unsigned short color_red = 0x022F;
|
|
const unsigned short color_red_dim = 0x0004;
|
|
const unsigned short color_green = 0x01F1;
|
|
const unsigned short color_blue = 0x0F11;
|
|
const unsigned short color_grey = 0x0222;
|
|
|
|
// other
|
|
int txtheight_fit = 138;
|
|
|
|
// bitmasks
|
|
static const unsigned long mask_numbers[] = {
|
|
0x12244800, // 47 2F /
|
|
0x69999600, // 48 30 0
|
|
0x26222200, // 49 31 1
|
|
0x69168F00, // 50 32 2
|
|
0x69219600, // 51 33 3
|
|
0x266AF200, // 52 34 4
|
|
0xF8E11E00, // 53 35 5
|
|
0x68E99600, // 54 36 6
|
|
0x71222200, // 55 37 7
|
|
0x69699600, // 56 38 8
|
|
0x69719600, // 57 39 9
|
|
0x04004000, // 58 3A :
|
|
0x04004400, // 59 3B ;
|
|
0x01242100, // 60 3C <
|
|
0x00707000, // 61 3D =
|
|
0x04212400, // 62 3E >
|
|
0x69240400, // 63 3F ?
|
|
0x00000000, // 64 40 @ [used instead of space for now]
|
|
0x22579900, // 65 41 A
|
|
0xE9E99E00, // 66 42 B
|
|
0x69889600, // 67 43 C
|
|
0xE9999E00, // 68 44 D
|
|
0xF8E88F00, // 69 45 E
|
|
0xF8E88800, // 70 46 F
|
|
0x698B9700, // 71 47 G
|
|
0x99F99900, // 72 48 H
|
|
0x44444400, // 73 49 I
|
|
0x11119600, // 74 4A J
|
|
0x9ACCA900, // 75 4B K
|
|
0x88888F00, // 76 4C L
|
|
0x9F999900, // 77 4D M
|
|
0x9DDBB900, // 78 4E N
|
|
0x69999600, // 79 4F O
|
|
0xE99E8800, // 80 50 P
|
|
0x6999A500, // 81 51 Q
|
|
0xE99E9900, // 82 52 R
|
|
0x69429600, // 83 53 S
|
|
0x72222200, // 84 54 T
|
|
0x99999600, // 85 55 U
|
|
0x55552200, // 86 56 V
|
|
0x9999F900, // 87 57 W
|
|
0x55225500, // 88 58 X
|
|
0x55222200, // 89 59 Y
|
|
0xF1248F00, // 90 5A Z
|
|
};
|
|
|
|
|
|
////////////////////////////////
|
|
// Cram functions
|
|
|
|
inline int EmuCramNull(int cram)
|
|
{
|
|
User::Panic(_L("Cram called!!"), 0);
|
|
return cram;
|
|
}
|
|
|
|
|
|
////////////////////////////////
|
|
// PicoScan functions in "center" mode
|
|
|
|
int EmuScanCenter0(unsigned int num, unsigned short *sdata)
|
|
{
|
|
//unsigned short *vidmem=framebuff;
|
|
|
|
//unsigned short *sp, *sto;
|
|
//sp=sdata+56; sto=sdata+264; vidmem += num*208;
|
|
|
|
//do { *vidmem++ = *sp++; } while(sp < sto);
|
|
memcpy(framebuff + num*208, sdata+56, 208*2); // memcpy gives ~1 fps (~2 with optimized memcpy)
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
int EmuScanCenter90(unsigned int num, unsigned short *sdata)
|
|
{
|
|
// ignore top and bottom lines
|
|
if(num < 8) return 7-num; // skip first 8 lines
|
|
if(num > 215) return 223+8-num; // this should not happen, just in case
|
|
|
|
num -= 8;
|
|
if(!num) {
|
|
if(Pico.video.reg[12]&1) { // copy less in 32-col mode
|
|
framebuff_offs= 0;
|
|
framebuff_len = 208*320;
|
|
} else {
|
|
framebuff_offs= 208*32;
|
|
framebuff_len = 208*256;
|
|
}
|
|
}
|
|
|
|
unsigned short *vidmem=framebuff;
|
|
vidmem += 207-num; // adjust x
|
|
|
|
// do less copy in 32-column mode
|
|
unsigned short *sp, *sto;
|
|
int pixels;
|
|
if(!(Pico.video.reg[12]&1))
|
|
{ sp=sdata+32; sto=sdata+288; pixels = 288; vidmem += 32*208; }
|
|
else { sp=sdata; sto=sdata+320; pixels = 320; }
|
|
|
|
do { *vidmem = *sp++; vidmem+=208; } while(sp < sto);
|
|
|
|
if(num == 207) return 16; // skip bottom of this frame and top of next
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
int EmuScanCenter180(unsigned int num, unsigned short *sdata)
|
|
{
|
|
unsigned short *vidmem=framebuff;
|
|
|
|
unsigned short *sp, *sto;
|
|
sp=sdata+56; sto=sdata+264; vidmem += (224-num)*208;
|
|
|
|
do { *(--vidmem) = *sp++; } while(sp < sto); // reversed
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
int EmuScanCenter270(unsigned int num, unsigned short *sdata)
|
|
{
|
|
// ignore top and bottom lines
|
|
if(num < 8) return 7-num; // skip first 8 lines
|
|
if(num > 215) return 223-num+8;
|
|
|
|
num -= 8;
|
|
if(num > 207) return 0;
|
|
if(!num) {
|
|
if(Pico.video.reg[12]&1) {
|
|
framebuff_offs= 0;
|
|
framebuff_len = 208*320;
|
|
} else {
|
|
framebuff_offs= 208*32;
|
|
framebuff_len = 208*256;
|
|
}
|
|
}
|
|
|
|
unsigned short *vidmem=framebuff+320*208;
|
|
vidmem -= 208-num; // adjust x
|
|
|
|
// do less copy in 32-column mode
|
|
unsigned short *sp, *sto;
|
|
if(!(Pico.video.reg[12]&1))
|
|
{ sp=sdata+32; sto=sdata+288; vidmem -= 32*208; }
|
|
else { sp=sdata; sto=sdata+320; }
|
|
|
|
do { *vidmem = *sp++; vidmem-=208; } while(sp < sto);
|
|
|
|
if(num == 207) return 16; // skip bottom of this frame and top of next
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
|
|
////////////////////////////////
|
|
// PicoScan functions in "fit" mode
|
|
|
|
static int EmuScanFit0(unsigned int num, unsigned short *sdata)
|
|
{
|
|
// 0.65, 145 lines in normal mode; 0.8125, 182 lines in 32-column mode
|
|
|
|
// draw this line? (ARM4s don't support division, so do some tricks here)
|
|
static int u = 0, num2 = 0;
|
|
if(!num) {
|
|
u = num2 = 0;
|
|
if(currentConfig.iScreenMode == TPicoConfig::PMFit) {
|
|
if(Pico.video.reg[12]&1) { // 32 col mode? This can change on any frame
|
|
fc_inc = 6500;
|
|
txtheight_fit = 138;
|
|
framebuff_len = 208*145;
|
|
memset(framebuff+208*145, 0, 208*37*2);
|
|
} else {
|
|
fc_inc = 8125;
|
|
txtheight_fit = 175;
|
|
framebuff_len = 208*182;
|
|
}
|
|
}
|
|
}
|
|
u += fc_inc;
|
|
if(u < 10000) return 0;
|
|
u -= 10000;
|
|
|
|
unsigned short *vidmem=framebuff;
|
|
|
|
int slen;
|
|
unsigned short *sp;
|
|
if(!(Pico.video.reg[12]&1))
|
|
{ sp=sdata+32; slen=256; }
|
|
else { sp=sdata; slen=320; }
|
|
|
|
vidmem += num2*208;
|
|
/*
|
|
int i=0;
|
|
while(sp < sto) {
|
|
i += inc;
|
|
if(i >= 10000) {
|
|
*vidmem++ = *sp;
|
|
i -= 10000;
|
|
}
|
|
sp++;
|
|
}
|
|
*/
|
|
PicuShrink(vidmem, 208, sp, slen);
|
|
|
|
num2++;
|
|
|
|
// calculate how many lines pico engine should skip,
|
|
// making sure this func will be called on scanline 0
|
|
int skip = 0;
|
|
while(u+fc_inc < 10000 && num+skip != 223) { u+=fc_inc; skip++; }
|
|
|
|
return skip;
|
|
}
|
|
|
|
int EmuScanFit90(unsigned int num, unsigned short *sdata)
|
|
{
|
|
// 0.9285
|
|
|
|
// draw this line?
|
|
static int u = 0, num2 = 0;
|
|
if(!num) {
|
|
u = num2 = 0;
|
|
if(Pico.video.reg[12]&1) {
|
|
framebuff_offs= 0;
|
|
framebuff_len = 208*320;
|
|
} else {
|
|
framebuff_offs= 208*32;
|
|
framebuff_len = 208*256;
|
|
}
|
|
}
|
|
u += 9285;
|
|
if(u < 10000) return 0;
|
|
u -= 10000;
|
|
|
|
unsigned short *vidmem=framebuff;
|
|
vidmem += 207-num2; // adjust x
|
|
|
|
// do less copy in 32-column mode
|
|
unsigned short *sp, *sto;
|
|
if(!(Pico.video.reg[12]&1))
|
|
{ sp=sdata+32; sto=sdata+288; vidmem += 32*208; }
|
|
else { sp=sdata; sto=sdata+320; }
|
|
|
|
do { *vidmem = *sp++; vidmem+=208; } while(sp < sto);
|
|
|
|
num2++;
|
|
|
|
// skip next line?
|
|
if(u+9285 < 10000 && num != 223) { u+=9285; return 1; }
|
|
|
|
return 0;
|
|
}
|
|
|
|
int EmuScanFit180(unsigned int num, unsigned short *sdata)
|
|
{
|
|
// 0.65, 145 lines in normal mode; 0.8125, 182 lines in 32-column mode
|
|
|
|
// draw this line? (ARM4s don't support division)
|
|
static int u = 0, num2 = 0;
|
|
if(!num) {
|
|
u = num2 = 0;
|
|
if(currentConfig.iScreenMode == TPicoConfig::PMFit) {
|
|
if(Pico.video.reg[12]&1) { // 32 col mode? This can change on any frame
|
|
fc_lines = 145;
|
|
fc_inc = 6500;
|
|
txtheight_fit = 138;
|
|
framebuff_len = 208*145;
|
|
memset(framebuff+208*145, 0, 208*37*2);
|
|
} else {
|
|
fc_lines = 182;
|
|
fc_inc = 8125;
|
|
txtheight_fit = 175;
|
|
framebuff_len = 208*182;
|
|
}
|
|
}
|
|
}
|
|
u += fc_inc;
|
|
if(u < 10000) return 0;
|
|
u -= 10000;
|
|
|
|
unsigned short *vidmem=framebuff;
|
|
|
|
int slen;
|
|
unsigned short *sp;
|
|
if(!(Pico.video.reg[12]&1))
|
|
{ sp=sdata+32; slen=256; }
|
|
else { sp=sdata; slen=320; }
|
|
|
|
vidmem += (fc_lines-num2)*208;
|
|
|
|
PicuShrinkReverse(vidmem, 208, sp, slen);
|
|
|
|
num2++;
|
|
|
|
// skip some lines?
|
|
int skip = 0;
|
|
while(u+fc_inc < 10000 && num+skip != 223) { u+=fc_inc; skip++; }
|
|
|
|
return skip;
|
|
}
|
|
|
|
int EmuScanFit270(unsigned int num, unsigned short *sdata)
|
|
{
|
|
// 0.9285
|
|
|
|
// draw this line?
|
|
static int u = 0, num2 = 0;
|
|
if(!num) {
|
|
u = num2 = 0;
|
|
if(Pico.video.reg[12]&1) {
|
|
framebuff_offs= 0;
|
|
framebuff_len = 208*320;
|
|
} else {
|
|
framebuff_offs= 208*32;
|
|
framebuff_len = 208*256;
|
|
}
|
|
}
|
|
u += 9285;
|
|
if(u < 10000) return 0;
|
|
u -= 10000;
|
|
|
|
unsigned short *vidmem=framebuff+320*208;
|
|
vidmem -= 208-num2; // adjust x
|
|
|
|
// do less copy in 32-column mode
|
|
unsigned short *sp, *sto;
|
|
if(!(Pico.video.reg[12]&1))
|
|
{ sp=sdata+32; sto=sdata+288; vidmem -= 32*208; }
|
|
else { sp=sdata; sto=sdata+320; }
|
|
|
|
do { *vidmem = *sp++; vidmem-=208; } while(sp < sto);
|
|
|
|
num2++;
|
|
|
|
// skip next line?
|
|
if(u+9285 < 10000 && num != 223) { u+=9285; return 1; }
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
////////////////////////////////
|
|
// text drawers
|
|
// warning: text must be at least 1px away from screen borders
|
|
|
|
void drawTextM2(int x, int y, const char *text, long color)
|
|
{
|
|
unsigned short *vidmem=framebuff;
|
|
int charmask, i, cx = x, cy;
|
|
unsigned short *l, *le;
|
|
|
|
// darken the background (left border)
|
|
for(l=vidmem+(cx-1)+(y-1)*328, le=vidmem+(cx-1)+(y+7)*328; l < le; l+=328)
|
|
*l = (*l >> 2) & dt_dmask;
|
|
|
|
for(const char *p=text; *p; p++) {
|
|
cy = y;
|
|
charmask = *(mask_numbers + (*p - 0x2F));
|
|
|
|
for(l = vidmem+cx+(y-1)*328, le = vidmem+cx+(y+7)*328; l < le; l+=328-4) {
|
|
*l = (*l >> 2) & dt_dmask; l++; *l = (*l >> 2) & dt_dmask; l++;
|
|
*l = (*l >> 2) & dt_dmask; l++; *l = (*l >> 2) & dt_dmask; l++;
|
|
*l = (*l >> 2) & dt_dmask;
|
|
}
|
|
|
|
for(i=0; i < 24; i++) {
|
|
// draw dot. Is this fast?
|
|
if(charmask&0x80000000) *( vidmem + (cx+(i&3)) + (cy+(i>>2))*328 ) = color;
|
|
charmask <<= 1;
|
|
}
|
|
cx += 5;
|
|
}
|
|
}
|
|
|
|
void drawText0(int x, int y, const char *text, long color)
|
|
{
|
|
unsigned short *vidmem=framebuff;
|
|
int charmask, i, cx = x, cy;
|
|
unsigned short *l, *le, dmask=0x0333;
|
|
|
|
// darken the background (left border)
|
|
for(l=vidmem+(cx-1)+(y-1)*208, le=vidmem+(cx-1)+(y+7)*208; l < le; l+=208)
|
|
*l = (*l >> 2) & dmask;
|
|
|
|
for(const char *p=text; *p; p++) {
|
|
cy = y;
|
|
charmask = *(mask_numbers + (*p - 0x2F));
|
|
|
|
for(l = vidmem+cx+(y-1)*208, le = vidmem+cx+(y+7)*208; l < le; l+=208-4) {
|
|
*l = (*l >> 2) & dmask; l++; *l = (*l >> 2) & dmask; l++;
|
|
*l = (*l >> 2) & dmask; l++; *l = (*l >> 2) & dmask; l++;
|
|
*l = (*l >> 2) & dmask;
|
|
}
|
|
|
|
for(i=0; i < 24; i++) {
|
|
// draw dot. Is this fast?
|
|
if(charmask&0x80000000) *( vidmem + (cx+(i&3)) + (cy+(i>>2))*208 ) = color;
|
|
charmask <<= 1;
|
|
}
|
|
cx += 5;
|
|
}
|
|
}
|
|
|
|
void drawText90(int x, int y, const char *text, long color)
|
|
{
|
|
unsigned short *vidmem=framebuff;
|
|
unsigned short *l, *le, dmask=0x0333;
|
|
int charmask, i, cx, cy = y;
|
|
|
|
for(l=vidmem+(x+1)+(cy-1)*208, le=vidmem+(x-7)+(cy-1)*208; l > le; l--)
|
|
*l = (*l >> 2) & dmask;
|
|
|
|
for(const char *p=text; *p; p++) {
|
|
cx = x;
|
|
charmask = *(mask_numbers + (*p - 0x2F));
|
|
|
|
for(l = vidmem+(x+1)+(cy)*208, le = vidmem+(x+1)+(cy+5)*208; l < le; l+=208+7) {
|
|
*l = (*l >> 2) & dmask; l--; *l = (*l >> 2) & dmask; l--;
|
|
*l = (*l >> 2) & dmask; l--; *l = (*l >> 2) & dmask; l--;
|
|
*l = (*l >> 2) & dmask; l--; *l = (*l >> 2) & dmask; l--;
|
|
*l = (*l >> 2) & dmask; l--; *l = (*l >> 2) & dmask;
|
|
}
|
|
|
|
for(i=0; i < 24; i++) {
|
|
if(charmask&0x80000000) *( vidmem + (cy+(i&3))*208 + (cx-(i>>2)) ) = color;
|
|
charmask <<= 1;
|
|
}
|
|
cy += 5;
|
|
}
|
|
}
|
|
|
|
void drawText180(int x, int y, const char *text, long color)
|
|
{
|
|
unsigned short *vidmem=framebuff;
|
|
int charmask, i, cx = x, cy;
|
|
unsigned short *l, *le, dmask=0x0333;
|
|
|
|
for(l=vidmem+(cx+1)+(y+1)*208, le=vidmem+(cx+1)+(y-7)*208; l > le; l-=208)
|
|
*l = (*l >> 2) & dmask;
|
|
|
|
for(const char *p=text; *p; p++) {
|
|
cy = y;
|
|
charmask = *(mask_numbers + (*p - 0x2F));
|
|
|
|
for(l = vidmem+cx+(y+1)*208, le = vidmem+cx+(y-8)*208; l > le; l-=208-4) {
|
|
*l = (*l >> 2) & dmask; l--; *l = (*l >> 2) & dmask; l--;
|
|
*l = (*l >> 2) & dmask; l--; *l = (*l >> 2) & dmask; l--;
|
|
*l = (*l >> 2) & dmask;
|
|
}
|
|
|
|
for(i=0; i < 24; i++) {
|
|
if(charmask&0x80000000) *( vidmem + (cx-(i&3)) + (cy-(i>>2))*208 ) = color;
|
|
charmask <<= 1;
|
|
}
|
|
cx -= 5;
|
|
}
|
|
}
|
|
|
|
void drawText270(int x, int y, const char *text, long color)
|
|
{
|
|
unsigned short *vidmem=framebuff;
|
|
int charmask, i, cx, cy = y;
|
|
unsigned short *l, *le, dmask=0x0333;
|
|
|
|
for(l=vidmem+(x-1)+(cy+1)*208, le=vidmem+(x+7)+(cy+1)*208; l < le; l++)
|
|
*l = (*l >> 2) & dmask;
|
|
|
|
for(const char *p=text; *p; p++) {
|
|
cx = x;
|
|
charmask = *(mask_numbers + (*p - 0x2F));
|
|
|
|
for(l = vidmem+(x-1)+(cy)*208, le = vidmem+(x-1)+(cy-5)*208; l > le; l-=208+7) {
|
|
*l = (*l >> 2) & dmask; l++; *l = (*l >> 2) & dmask; l++;
|
|
*l = (*l >> 2) & dmask; l++; *l = (*l >> 2) & dmask; l++;
|
|
*l = (*l >> 2) & dmask; l++; *l = (*l >> 2) & dmask; l++;
|
|
*l = (*l >> 2) & dmask; l++; *l = (*l >> 2) & dmask;
|
|
}
|
|
|
|
for(i=0; i < 24; i++) {
|
|
if(charmask&0x80000000) *( vidmem + (cy-(i&3))*208 + (cx+(i>>2)) ) = color;
|
|
charmask <<= 1;
|
|
}
|
|
cy -= 5;
|
|
}
|
|
}
|
|
|
|
void drawTextFpsM2(const char *text)
|
|
{
|
|
if(!text) return;
|
|
drawTextM2((Pico.video.reg[12]&1) ? 256 : 224, 200, text, color_redM2);
|
|
}
|
|
|
|
void drawTextFps0(const char *text)
|
|
{
|
|
if(!text) return;
|
|
drawText0(176, 216, text, color_red);
|
|
}
|
|
|
|
void drawTextFpsFit0(const char *text)
|
|
{
|
|
if(!text) return;
|
|
drawText0(176, txtheight_fit, text, color_red);
|
|
}
|
|
|
|
void drawTextFps90(const char *text)
|
|
{
|
|
if(!text) return;
|
|
drawText90(10, 256, text, color_red);
|
|
}
|
|
|
|
void drawTextFps180(const char *text)
|
|
{
|
|
if(!text) return;
|
|
drawText180(32, 8, text, color_red);
|
|
}
|
|
|
|
void drawTextFps270(const char *text)
|
|
{
|
|
if(!text) return;
|
|
drawText270(200, 64, text, color_red);
|
|
}
|
|
|
|
void drawTextNoticeM2(const char *text)
|
|
{
|
|
if(!text) return;
|
|
drawTextM2(20, 200, text, color_redM2);
|
|
}
|
|
|
|
void drawTextNotice0(const char *text)
|
|
{
|
|
if(!text) return;
|
|
drawText0(2, 216, text, color_red);
|
|
}
|
|
|
|
void drawTextNoticeFit0(const char *text)
|
|
{
|
|
if(!text) return;
|
|
drawText0(2, txtheight_fit, text, color_red);
|
|
}
|
|
|
|
void drawTextNotice90(const char *text)
|
|
{
|
|
if(!text) return;
|
|
drawText90(10, 34, text, color_red);
|
|
}
|
|
|
|
void drawTextNotice180(const char *text)
|
|
{
|
|
if(!text) return;
|
|
drawText180(206, 8, text, color_red);
|
|
}
|
|
|
|
void drawTextNotice270(const char *text)
|
|
{
|
|
if(!text) return;
|
|
drawText270(200, 286, text, color_red);
|
|
}
|
|
|
|
|
|
////////////////////////////////
|
|
// misc drawers
|
|
|
|
// draws rect with width - 1 and height - 1
|
|
void drawRect(const TRect &rc, unsigned short color)
|
|
{
|
|
if(rc.iTl.iX - rc.iBr.iX && rc.iTl.iY - rc.iBr.iY) {
|
|
int stepX = rc.iTl.iX < rc.iBr.iX ? 1 : -1;
|
|
int stepY = rc.iTl.iY < rc.iBr.iY ? 1 : -1;
|
|
|
|
for(int x = rc.iTl.iX;; x += stepX) {
|
|
*(framebuff + rc.iTl.iY*208 + x) = *(framebuff + (rc.iBr.iY - stepY)*208 + x) = color;
|
|
if(x == rc.iBr.iX - stepX) break;
|
|
}
|
|
|
|
for(int y = rc.iTl.iY;; y += stepY) {
|
|
*(framebuff + y*208 + rc.iTl.iX) = *(framebuff + y*208 + rc.iBr.iX - stepX) = color;
|
|
if(y == rc.iBr.iY - stepY) break;
|
|
}
|
|
}
|
|
}
|
|
|
|
// draws fullsize filled rect
|
|
void drawRectFilled(const TRect rc, unsigned short color)
|
|
{
|
|
if(rc.iTl.iX - rc.iBr.iX && rc.iTl.iY - rc.iBr.iY) {
|
|
int stepX = rc.iTl.iX < rc.iBr.iX ? 1 : -1;
|
|
int stepY = rc.iTl.iY < rc.iBr.iY ? 1 : -1;
|
|
|
|
for(int y = rc.iTl.iY;; y += stepY) {
|
|
for(int x = rc.iTl.iX;; x += stepX) {
|
|
*(framebuff + y*208 + x) = *(framebuff + y*208 + x) = color;
|
|
if(x == rc.iBr.iX) break;
|
|
}
|
|
if(y == rc.iBr.iY) break;
|
|
}
|
|
}
|
|
}
|
|
|
|
// direction: -1 left, 1 right
|
|
void drawArrow0(TPoint p, int direction, unsigned short color)
|
|
{
|
|
int width = 11;
|
|
int x = p.iX;
|
|
int y = p.iY;
|
|
|
|
for(; width > 0; x+=direction, y++, width -=2)
|
|
for(int i=0; i < width; i++)
|
|
*(framebuff + x + y*208 + i*208) = color;
|
|
}
|
|
|
|
void drawArrow90(TPoint p, int direction, unsigned short color)
|
|
{
|
|
int width = 11;
|
|
int x = p.iX - width;
|
|
int y = p.iY;
|
|
|
|
for(; width > 0; x++, y+=direction, width -=2)
|
|
for(int i=0; i < width; i++)
|
|
*(framebuff + x + y*208 + i) = color;
|
|
}
|
|
|
|
|
|
// copies temporary framebuff to real device framebuffer
|
|
void vidBlitRGB444(int full)
|
|
{
|
|
unsigned short *ps;
|
|
unsigned short *pd;
|
|
int pixels;
|
|
if(full) {
|
|
ps = framebuff;
|
|
pd = (unsigned short *) screenbuff;
|
|
pixels = 208*320;
|
|
} else {
|
|
ps = framebuff + framebuff_offs;
|
|
pd = (unsigned short *) screenbuff + framebuff_offs;
|
|
pixels = framebuff_len;
|
|
}
|
|
|
|
vidConvCpyRGB444(pd, ps, pixels);
|
|
//for(unsigned short *ps_end = ps + pixels; ps < ps_end; ps++)
|
|
// Convert 0000bbb0 ggg0rrr0
|
|
// to 0000rrr0 ggg0bbb0
|
|
// *pd++ = ((*ps&0x000F)<<8) | (*ps&0x00F0) | ((*ps&0x0F00)>>8);
|
|
}
|
|
|
|
void vidBlitRGB565(int full)
|
|
{
|
|
unsigned short *ps;
|
|
unsigned short *pd;
|
|
int pixels;
|
|
if(full) {
|
|
ps = framebuff;
|
|
pd = (unsigned short *) screenbuff;
|
|
pixels = 208*320;
|
|
} else {
|
|
ps = framebuff + framebuff_offs;
|
|
pd = (unsigned short *) screenbuff + framebuff_offs;
|
|
pixels = framebuff_len;
|
|
}
|
|
|
|
vidConvCpyRGB565(pd, ps, pixels);
|
|
//for(; ps < ps_end; ps++)
|
|
// Convert 0000bbb0 ggg0rrr0
|
|
// to rrr00ggg 000bbb00
|
|
// *pd++ = ((*ps&0x000F)<<12) | ((*ps&0x00F0)<<3) | ((*ps&0x0F00)>>7);
|
|
}
|
|
|
|
void vidBlitRGB32(int full)
|
|
{
|
|
unsigned short *ps;
|
|
unsigned long *pd;
|
|
int pixels;
|
|
if(full) {
|
|
ps = framebuff;
|
|
pd = (unsigned long *) screenbuff;
|
|
pixels = 208*320;
|
|
} else {
|
|
ps = framebuff + framebuff_offs;
|
|
pd = (unsigned long *) screenbuff + framebuff_offs;
|
|
pixels = framebuff_len;
|
|
}
|
|
|
|
vidConvCpyRGB32(pd, ps, pixels);
|
|
//for(; ps < ps_end; ps++)
|
|
// Convert 0000bbb0 ggg0rrr0
|
|
// to ..0 rrr00000 ggg00000 bbb00000
|
|
// *pd++ = ((*ps&0x000F)<<20) | ((*ps&0x00F0)<<8) | ((*ps&0x0F00)>>4);
|
|
}
|
|
|
|
// -------- rendermode 2 ---------
|
|
|
|
void vidBlit16M2(int full)
|
|
{
|
|
unsigned short *ps = framebuff+328*8;
|
|
unsigned short *pd = (unsigned short *) screenbuff;
|
|
|
|
if(currentConfig.iScreenRotation == TPicoConfig::PRot90) {
|
|
if(Pico.video.reg[12]&1)
|
|
vidConvCpyM2_16_90(pd, ps, 320/8);
|
|
else {
|
|
if(full) memset(pd, 0, 208*32*2);
|
|
pd += 208*32;
|
|
vidConvCpyM2_16_90(pd, ps, 256/8);
|
|
if(full) memset(pd + 208*256, 0, 208*32*2);
|
|
}
|
|
} else if(currentConfig.iScreenRotation == TPicoConfig::PRot270) {
|
|
if(Pico.video.reg[12]&1)
|
|
vidConvCpyM2_16_270(pd, ps, 320/8);
|
|
else {
|
|
if(full) memset(pd, 0, 208*32*2);
|
|
pd += 208*32;
|
|
ps -= 64; // the blitter starts copying from the right border, so we need to adjust
|
|
vidConvCpyM2_16_270(pd, ps, 256/8);
|
|
if(full) memset(pd + 208*256, 0, 208*32*2);
|
|
}
|
|
}
|
|
/*
|
|
for(int x=0; x < 320; x++)
|
|
for(int y=207; y>=0; y--) {
|
|
*pd++ = *(ps+8+x+y*328);
|
|
}
|
|
*/
|
|
}
|
|
|
|
void vidBlitRGB32M2(int full)
|
|
{
|
|
unsigned short *ps = framebuff+328*8;
|
|
unsigned long *pd = (unsigned long *) screenbuff;
|
|
|
|
if(currentConfig.iScreenRotation == TPicoConfig::PRot90) {
|
|
if(Pico.video.reg[12]&1)
|
|
vidConvCpyM2_RGB32_90(pd, ps, 320/8);
|
|
else {
|
|
if(full) memset(pd, 0, 208*32*4);
|
|
pd += 208*32;
|
|
vidConvCpyM2_RGB32_90(pd, ps, 256/8);
|
|
if(full) memset(pd + 208*256, 0, 208*32*4);
|
|
}
|
|
} else if(currentConfig.iScreenRotation == TPicoConfig::PRot270) {
|
|
if(Pico.video.reg[12]&1)
|
|
vidConvCpyM2_RGB32_270(pd, ps, 320/8);
|
|
else {
|
|
if(full) memset(pd, 0, 208*32*4);
|
|
pd += 208*32;
|
|
ps -= 64; // the blitter starts copying from the right border, so we need to adjust
|
|
vidConvCpyM2_RGB32_270(pd, ps, 256/8);
|
|
if(full) memset(pd + 208*256, 0, 208*32*4);
|
|
}
|
|
}
|
|
}
|
|
|
|
void PrepareCramRGB444M2()
|
|
{
|
|
vidConvCpyRGB444(cram_high, Pico.cram, 0x40);
|
|
}
|
|
|
|
void PrepareCramRGB565M2()
|
|
{
|
|
vidConvCpyRGB565(cram_high, Pico.cram, 0x40);
|
|
}
|
|
|
|
|
|
////////////////////////////////
|
|
// main functions
|
|
|
|
int vidInit(int displayMode, void *vidmem, int p800, int reinit)
|
|
{
|
|
if(!reinit) {
|
|
// prepare framebuffer
|
|
screenbuff = (unsigned short *) vidmem;
|
|
framebuff = (unsigned short *) malloc(framebuffsize);
|
|
|
|
if(!screenbuff) return KErrNotSupported;
|
|
if(!framebuff) return KErrNoMemory;
|
|
|
|
// Cram function: go and hack Pico so it never gets called
|
|
PicoCram = EmuCramNull;
|
|
}
|
|
|
|
// select suitable blitter
|
|
switch(displayMode) {
|
|
case EColor4K: vidBlit = vidBlitKeyCfg = vidBlitRGB444; break;
|
|
case EColor64K: vidBlit = vidBlitKeyCfg = vidBlitRGB565; break;
|
|
case EColor16M: vidBlit = vidBlitKeyCfg = vidBlitRGB32; break;
|
|
default: return KErrNotSupported;
|
|
}
|
|
|
|
memset(framebuff, 0, framebuffsize);
|
|
|
|
// rendermode 2?
|
|
if(PicoOpt&0x10) {
|
|
switch(displayMode) {
|
|
case EColor4K:
|
|
vidBlit = vidBlit16M2;
|
|
PicoPrepareCram = PrepareCramRGB444M2;
|
|
PicoCramHigh = cram_high;
|
|
color_redM2 = 0x0F22;
|
|
dt_dmask = 0x0333;
|
|
break;
|
|
case EColor64K:
|
|
vidBlit = vidBlit16M2;
|
|
PicoPrepareCram = PrepareCramRGB565M2;
|
|
PicoCramHigh = cram_high;
|
|
color_redM2 = 0xF882;
|
|
dt_dmask = 0x39e7;
|
|
break;
|
|
case EColor16M:
|
|
vidBlit = vidBlitRGB32M2;
|
|
break;
|
|
}
|
|
drawTextFps = drawTextFpsM2;
|
|
drawTextNotice = drawTextNoticeM2;
|
|
vidBlit(1);
|
|
return 0;
|
|
}
|
|
|
|
framebuff_offs = 0;
|
|
framebuff_len = 208*320;
|
|
vidBlit(1);
|
|
|
|
// setup all orientation related stuff
|
|
if(currentConfig.iScreenRotation == TPicoConfig::PRot0) {
|
|
if(currentConfig.iScreenMode == TPicoConfig::PMCenter) {
|
|
PicoScan = EmuScanCenter0;
|
|
framebuff_len = 208*224;
|
|
drawTextFps = drawTextFps0;
|
|
drawTextNotice = drawTextNotice0;
|
|
} else {
|
|
if(currentConfig.iScreenMode == TPicoConfig::PMFit2) {
|
|
if(p800) {
|
|
fc_inc = 6518; // 0.651786 (144+2)
|
|
txtheight_fit = 139;
|
|
framebuff_len = 208*146;
|
|
} else {
|
|
fc_inc = 9286; // 0.92857
|
|
txtheight_fit = 201;
|
|
framebuff_len = 208*208;
|
|
}
|
|
}
|
|
PicoScan = EmuScanFit0;
|
|
drawTextFps = drawTextFpsFit0;
|
|
drawTextNotice = drawTextNoticeFit0;
|
|
}
|
|
} else if(currentConfig.iScreenRotation == TPicoConfig::PRot90) {
|
|
if(currentConfig.iScreenMode == TPicoConfig::PMFit)
|
|
PicoScan = EmuScanFit90;
|
|
else PicoScan = EmuScanCenter90;
|
|
drawTextFps = drawTextFps90;
|
|
drawTextNotice = drawTextNotice90;
|
|
} else if(currentConfig.iScreenRotation == TPicoConfig::PRot180) {
|
|
if(currentConfig.iScreenMode == TPicoConfig::PMCenter) {
|
|
PicoScan = EmuScanCenter180;
|
|
framebuff_len = 208*224;
|
|
} else {
|
|
if(currentConfig.iScreenMode == TPicoConfig::PMFit2) {
|
|
if(p800) {
|
|
fc_inc = 6518; // 0.651786
|
|
fc_lines = 146;
|
|
framebuff_len = 208*146;
|
|
} else {
|
|
fc_inc = 9286; // 0.92857
|
|
fc_lines = 208;
|
|
framebuff_len = 208*208;
|
|
}
|
|
}
|
|
PicoScan = EmuScanFit180;
|
|
}
|
|
drawTextFps = drawTextFps180;
|
|
drawTextNotice = drawTextNotice180;
|
|
} else if(currentConfig.iScreenRotation == TPicoConfig::PRot270) {
|
|
if(currentConfig.iScreenMode == TPicoConfig::PMFit)
|
|
PicoScan = EmuScanFit270;
|
|
else PicoScan = EmuScanCenter270;
|
|
drawTextFps = drawTextFps270;
|
|
drawTextNotice = drawTextNotice270;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
void vidFree()
|
|
{
|
|
free(framebuff);
|
|
framebuff = 0;
|
|
}
|
|
|
|
void vidDrawFrame(char *noticeStr, char *fpsStr, int num)
|
|
{
|
|
PicoFrame();
|
|
if(currentConfig.iFlags & 2)
|
|
drawTextFps(fpsStr);
|
|
drawTextNotice(noticeStr);
|
|
|
|
vidBlit(!num); // copy full frame once a second
|
|
}
|
|
|
|
void vidKeyConfigFrame(const TUint whichAction, TInt flipClosed)
|
|
{
|
|
int i;
|
|
char buttonNames[128];
|
|
buttonNames[0] = 0;
|
|
memset(framebuff, 0, framebuffsize);
|
|
|
|
unsigned long currentActCode = 1 << whichAction;
|
|
|
|
if(flipClosed) {
|
|
drawRectFilled(TRect(56, 2, 152, 16), color_grey); // 96x14
|
|
drawArrow0(TPoint(64, 3), -1, color_green);
|
|
drawArrow0(TPoint(144, 3), 1, color_green);
|
|
drawText0(64, 20, "USE@JOG@TO@SELECT", color_red);
|
|
|
|
drawText0(68, 6, actionNames[whichAction], color_red);
|
|
} else {
|
|
// draw all "buttons" in reverse order
|
|
const TPicoAreaConfigEntry *e = areaConfig + 1; i = 0;
|
|
while(e->rect != TRect(0,0,0,0)) { e++; i++; }
|
|
for(e--, i--; e->rect != TRect(0,0,0,0); e--, i--)
|
|
drawRect(e->rect, (currentConfig.iAreaBinds[i] & currentActCode) ? color_red : color_red_dim);
|
|
|
|
// draw config controls
|
|
drawRectFilled(TRect(190, 112, 204, 208), color_grey);
|
|
drawArrow90(TPoint(203, 120), -1, color_green);
|
|
drawArrow90(TPoint(203, 200), 1, color_green);
|
|
|
|
drawText90(200, 124, actionNames[whichAction], color_red);
|
|
}
|
|
|
|
// draw active button names if there are any
|
|
i = 0;
|
|
for(TPicoKeyConfigEntry *e = keyConfig; e->name; e++, i++)
|
|
if(currentConfig.iKeyBinds[i] & currentActCode) {
|
|
if(buttonNames[0]) strcat(buttonNames, ";@");
|
|
strcat(buttonNames, e->name);
|
|
}
|
|
if(buttonNames[0]) {
|
|
if(flipClosed) {
|
|
buttonNames[41] = 0; // only 61 chars fit
|
|
drawText0(2, 138, buttonNames, color_blue);
|
|
} else {
|
|
buttonNames[61] = 0;
|
|
drawText90(12, 10, buttonNames, color_blue);
|
|
}
|
|
}
|
|
|
|
vidBlitKeyCfg(1);
|
|
}
|
|
|
|
void vidDrawFCconfigDone()
|
|
{
|
|
drawText0(64, 20, "USE@JOG@TO@SELECT", 0); // blank prev text
|
|
drawText0(54, 30, "OPEN@FLIP@TO@CONTINUE", color_red);
|
|
vidBlitKeyCfg(1);
|
|
}
|
|
|
|
void vidDrawNotice(const char *txt)
|
|
{
|
|
if(framebuff) {
|
|
drawTextNotice(txt);
|
|
vidBlit(1);
|
|
}
|
|
}
|