/* 3ds81 - Nintendo 3DS ZX81 emulator. Copyright (C) 2021 Ian Cowburn This program 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. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, see $Id: framebuffer.c 43 2007-03-12 00:59:51Z ianc $ */ #include <3ds.h> #include #include #include #include #include "framebuffer.h" #include "cpatrol_inlay_bgr.h" #include "mazogs_inlay_bgr.h" #include "keyb_bgr.h" #include "sabotage_inlay_bgr.h" #include "maze_inlay_bgr.h" #include "splashimg_bgr.h" /* ---------------------------------------- STATIC DATA */ static u16 pal[COL_YELLOW + 1]; static Framebuffer image[IMG_SPLASH + 1]; static u8 font[]= { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x08, 0x08, 0x08, 0x00, 0x08, 0x00, 0x00, 0x24, 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x24, 0x7e, 0x24, 0x24, 0x7e, 0x24, 0x00, 0x00, 0x10, 0x7c, 0x14, 0x7c, 0x50, 0x7c, 0x10, 0x00, 0x46, 0x26, 0x10, 0x08, 0x64, 0x62, 0x00, 0x00, 0x08, 0x14, 0x08, 0x54, 0x22, 0x5c, 0x00, 0x00, 0x10, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x10, 0x10, 0x10, 0x10, 0x20, 0x00, 0x00, 0x04, 0x08, 0x08, 0x08, 0x08, 0x04, 0x00, 0x00, 0x00, 0x28, 0x10, 0x7c, 0x10, 0x28, 0x00, 0x00, 0x00, 0x10, 0x10, 0x7c, 0x10, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x10, 0x08, 0x00, 0x00, 0x00, 0x00, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x40, 0x20, 0x10, 0x08, 0x04, 0x00, 0x00, 0x3c, 0x62, 0x52, 0x4a, 0x46, 0x3c, 0x00, 0x00, 0x18, 0x14, 0x10, 0x10, 0x10, 0x7c, 0x00, 0x00, 0x3c, 0x42, 0x40, 0x3c, 0x02, 0x7e, 0x00, 0x00, 0x3c, 0x42, 0x30, 0x40, 0x42, 0x3c, 0x00, 0x00, 0x10, 0x18, 0x14, 0x12, 0x7e, 0x10, 0x00, 0x00, 0x7e, 0x02, 0x3e, 0x40, 0x42, 0x3c, 0x00, 0x00, 0x3c, 0x02, 0x3e, 0x42, 0x42, 0x3c, 0x00, 0x00, 0x7e, 0x40, 0x20, 0x10, 0x08, 0x08, 0x00, 0x00, 0x3c, 0x42, 0x3c, 0x42, 0x42, 0x3c, 0x00, 0x00, 0x3c, 0x42, 0x42, 0x7c, 0x40, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x08, 0x08, 0x04, 0x00, 0x00, 0x20, 0x10, 0x08, 0x10, 0x20, 0x00, 0x00, 0x00, 0x00, 0x7c, 0x00, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x08, 0x10, 0x20, 0x10, 0x08, 0x00, 0x00, 0x3c, 0x42, 0x20, 0x10, 0x00, 0x10, 0x00, 0x00, 0x3c, 0x52, 0x6a, 0x7a, 0x02, 0x3c, 0x00, 0x00, 0x3c, 0x42, 0x42, 0x7e, 0x42, 0x42, 0x00, 0x00, 0x3e, 0x42, 0x3e, 0x42, 0x42, 0x3e, 0x00, 0x00, 0x3c, 0x42, 0x02, 0x02, 0x42, 0x3c, 0x00, 0x00, 0x1e, 0x22, 0x42, 0x42, 0x22, 0x1e, 0x00, 0x00, 0x7e, 0x02, 0x3e, 0x02, 0x02, 0x7e, 0x00, 0x00, 0x7e, 0x02, 0x3e, 0x02, 0x02, 0x02, 0x00, 0x00, 0x3c, 0x42, 0x02, 0x72, 0x42, 0x3c, 0x00, 0x00, 0x42, 0x42, 0x7e, 0x42, 0x42, 0x42, 0x00, 0x00, 0x7c, 0x10, 0x10, 0x10, 0x10, 0x7c, 0x00, 0x00, 0x40, 0x40, 0x40, 0x42, 0x42, 0x3c, 0x00, 0x00, 0x22, 0x12, 0x0e, 0x12, 0x22, 0x42, 0x00, 0x00, 0x02, 0x02, 0x02, 0x02, 0x02, 0x7e, 0x00, 0x00, 0x42, 0x66, 0x5a, 0x42, 0x42, 0x42, 0x00, 0x00, 0x42, 0x46, 0x4a, 0x52, 0x62, 0x42, 0x00, 0x00, 0x3c, 0x42, 0x42, 0x42, 0x42, 0x3c, 0x00, 0x00, 0x3e, 0x42, 0x42, 0x3e, 0x02, 0x02, 0x00, 0x00, 0x3c, 0x42, 0x42, 0x4a, 0x52, 0x3c, 0x00, 0x00, 0x3e, 0x42, 0x42, 0x3e, 0x22, 0x42, 0x00, 0x00, 0x3c, 0x02, 0x3c, 0x40, 0x42, 0x3c, 0x00, 0x00, 0x7f, 0x08, 0x08, 0x08, 0x08, 0x08, 0x00, 0x00, 0x42, 0x42, 0x42, 0x42, 0x42, 0x3c, 0x00, 0x00, 0x42, 0x42, 0x42, 0x42, 0x24, 0x18, 0x00, 0x00, 0x42, 0x42, 0x42, 0x42, 0x5a, 0x24, 0x00, 0x00, 0x42, 0x24, 0x18, 0x18, 0x24, 0x42, 0x00, 0x00, 0x41, 0x22, 0x14, 0x08, 0x08, 0x08, 0x00, 0x00, 0x7e, 0x20, 0x10, 0x08, 0x04, 0x7e, 0x00, 0x00, 0x70, 0x10, 0x10, 0x10, 0x10, 0x70, 0x00, 0x00, 0x00, 0x02, 0x04, 0x08, 0x10, 0x20, 0x00, 0x00, 0x0e, 0x08, 0x08, 0x08, 0x08, 0x0e, 0x00, 0x00, 0x08, 0x1c, 0x2a, 0x08, 0x08, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x38, 0x44, 0x1e, 0x04, 0x04, 0x7e, 0x00, 0x00, 0x00, 0x1c, 0x20, 0x3c, 0x22, 0x3c, 0x00, 0x00, 0x04, 0x04, 0x3c, 0x44, 0x44, 0x3c, 0x00, 0x00, 0x00, 0x38, 0x04, 0x04, 0x04, 0x38, 0x00, 0x00, 0x20, 0x20, 0x3c, 0x22, 0x22, 0x3c, 0x00, 0x00, 0x00, 0x1c, 0x22, 0x1e, 0x02, 0x3c, 0x00, 0x00, 0x30, 0x08, 0x18, 0x08, 0x08, 0x08, 0x00, 0x00, 0x00, 0x3c, 0x22, 0x22, 0x3c, 0x20, 0x1c, 0x00, 0x02, 0x02, 0x1e, 0x22, 0x22, 0x22, 0x00, 0x00, 0x08, 0x00, 0x0c, 0x08, 0x08, 0x1c, 0x00, 0x00, 0x20, 0x00, 0x20, 0x20, 0x20, 0x24, 0x18, 0x00, 0x04, 0x14, 0x0c, 0x0c, 0x14, 0x24, 0x00, 0x00, 0x08, 0x08, 0x08, 0x08, 0x08, 0x30, 0x00, 0x00, 0x00, 0x16, 0x2a, 0x2a, 0x2a, 0x2a, 0x00, 0x00, 0x00, 0x1e, 0x22, 0x22, 0x22, 0x22, 0x00, 0x00, 0x00, 0x1c, 0x22, 0x22, 0x22, 0x1c, 0x00, 0x00, 0x00, 0x1e, 0x22, 0x22, 0x1e, 0x02, 0x02, 0x00, 0x00, 0x3c, 0x22, 0x22, 0x3c, 0x20, 0x60, 0x00, 0x00, 0x38, 0x04, 0x04, 0x04, 0x04, 0x00, 0x00, 0x00, 0x1c, 0x02, 0x1c, 0x20, 0x1e, 0x00, 0x00, 0x08, 0x1c, 0x08, 0x08, 0x08, 0x30, 0x00, 0x00, 0x00, 0x22, 0x22, 0x22, 0x22, 0x1c, 0x00, 0x00, 0x00, 0x22, 0x22, 0x14, 0x14, 0x08, 0x00, 0x00, 0x00, 0x22, 0x2a, 0x2a, 0x2a, 0x14, 0x00, 0x00, 0x00, 0x22, 0x14, 0x08, 0x14, 0x22, 0x00, 0x00, 0x00, 0x22, 0x22, 0x22, 0x3c, 0x20, 0x1c, 0x00, 0x00, 0x3e, 0x10, 0x08, 0x04, 0x3e, 0x00, 0x00, 0x70, 0x10, 0x0c, 0x10, 0x10, 0x70, 0x00, 0x00, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x00, 0x00, 0x0e, 0x08, 0x30, 0x08, 0x08, 0x0e, 0x00, 0x00, 0x28, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x42, 0x99, 0x85, 0x85, 0x99, 0x42, 0x3c }; /* ---------------------------------------- PRIVATE INTERFACES */ static void LoadImage(Framebuffer *fb, const u8* img, u16 w, u16 h) { int f; int x,y; fb->width = w; fb->height = h; fb->buffer = malloc(sizeof *fb->buffer * w * h); x = 0; y = h - 1; for(f = 0; f < w * h; f++) { u8 b = *img++; u8 g = *img++; u8 r = *img++; FB_ADDR(fb, x, y) = RGB8_to_565(r, g, b); if (++x == w) { x = 0; y--; } } } /* ---------------------------------------- PUBLIC INTERFACES */ void FB_Init(void) { pal[COL_BLACK] = RGB8_to_565(0,0,0); pal[COL_WHITE] = RGB8_to_565(255,255,255); pal[COL_RED] = RGB8_to_565(255,0,0); pal[COL_GREEN] = RGB8_to_565(0,255,0); pal[COL_BLUE] = RGB8_to_565(0,0,255); pal[COL_GUISELECT] = RGB8_to_565(128,128,255); pal[COL_GREY] = RGB8_to_565(128,128,128); pal[COL_LIGHTGREY] = RGB8_to_565(200,200,200); pal[COL_DARKGREY] = RGB8_to_565(64,64,64); pal[COL_YELLOW] = RGB8_to_565(255,255,0); LoadImage(image + IMG_CITY_PATROL_INLAY, cpatrol_inlay_bgr, 126, 192); LoadImage(image + IMG_3D_MONSTER_MAZE_INLAY, cpatrol_inlay_bgr, 126, 192); LoadImage(image + IMG_SABOTAGE_INLAY, sabotage_inlay_bgr, 126, 192); LoadImage(image + IMG_MAZOGS_INLAY, mazogs_inlay_bgr, 126, 192); LoadImage(image + IMG_KEYBOARD, keyb_bgr, 320, 240); LoadImage(image + IMG_SPLASH, splashimg_bgr, 400, 240); } void FB_StartFrame(Framebuffer *upper, Framebuffer *lower) { if (upper) { upper->buffer = (u16*)gfxGetFramebuffer(GFX_TOP, GFX_LEFT, &upper->height, &upper->width); } if (lower) { lower->buffer = (u16*)gfxGetFramebuffer(GFX_BOTTOM, GFX_LEFT, &lower->height, &lower->width); } } void FB_EndFrame(void) { gfxFlushBuffers(); gfxSwapBuffers(); gspWaitForVBlank(); hidScanInput(); } void FB_Print(Framebuffer *fb, const char *text, int x, int y, FB_Colour colour, FB_Colour paper) { int cx,cy; int ch; u16 fg,bg; u16 fg_trans,bg_trans; fg = pal[colour]; bg = pal[paper]; fg_trans = colour == COL_TRANSPARENT; bg_trans = paper == COL_TRANSPARENT; while(*text) { if (*text == 1) { u16 t; t = fg; fg = bg; bg = t; t = fg_trans; fg_trans = bg_trans; bg_trans = t; text++; } else if (*text == '\n') { x = 0; y -= 8; text++; if (y < -7) { return; } } else { ch=((*text)-32)*8; for(cy=0;cy<8;cy++) { for(cx=0;cx<8;cx++) { if (font[ch]&(1< fb->width) { break; } } } } void FB_Centre(Framebuffer *fb, const char *text, int y, FB_Colour colour, FB_Colour paper) { FB_Print(fb, text, fb->width / 2 - strlen(text) * 4, y, colour, paper); } void FB_printf(Framebuffer *fb, int x, int y, FB_Colour colour, FB_Colour paper, const char *format, ...) { char buff[256]; va_list va; va_start(va,format); vsnprintf(buff,sizeof buff,format,va); va_end(va); FB_Print(fb, buff, x, y, colour, paper); } void FB_HLine(Framebuffer *fb, u16 x1, u16 x2, u16 y, FB_Colour colour) { if (x1 > x2) { u16 t; t = x1; x1 = x2; x2 = t; } while(x1<=x2) { FB_PLOT_CLIPPED(fb, x1, y, pal[colour]); x1++; } } void FB_VLine(Framebuffer *fb, u16 x, u16 y1, u16 y2, FB_Colour colour) { if (y1 > y2) { u16 t; t = y1; y1 = y2; y2 = t; } while(y1<=y2) { FB_PLOT_CLIPPED(fb, x, y1, pal[colour]); y1++; } } void FB_Box(Framebuffer *fb, u16 x, u16 y, u16 w, u16 h, FB_Colour colour) { FB_HLine(fb, x, x + w - 1, y, colour); FB_HLine(fb, x, x + w - 1, y - h + 1, colour); FB_VLine(fb, x, y, y - h + 1, colour); FB_VLine(fb, x + w - 1, y, y - h + 1, colour); } void FB_FillBox(Framebuffer *fb, u16 x, u16 y, u16 w, u16 h, FB_Colour colour) { while(h--) { FB_HLine(fb, x, x + w - 1, y--, colour); } } void FB_Clear(Framebuffer *fb, FB_Colour col) { u16 *p; int f; u16 c; f = fb->width * fb->height; p = fb->buffer; c = pal[col]; while(f--) { *p++ = c; } } void FB_Blit(Framebuffer *fb, FB_Image img, u16 x, u16 y) { Framebuffer *src; u16 px,py; src = image + img; if (fb->width == src->width && fb->height == src->height && x == 0 && y == 0) { memcpy(fb->buffer, src->buffer, src->width * src->height * 2); } else { for(px = 0; px < src->width; px++) { u16 dx; dx = x + px; if (dx < fb->width) { u16 dy; dy = y; for(py = 0; dy < fb->height && py < src->height; py++, dy++) { dy = y + py; FB_ADDR(fb, dx, dy) = FB_ADDR(src, px, py); } } } } }