1037 lines
44 KiB
C
1037 lines
44 KiB
C
/////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Name: osd.c
|
|
// Created: May 2021
|
|
// Version: v1.0
|
|
// Author(s): Philip Smart
|
|
// Description: The On Screen Display library.
|
|
// This source file contains the On Screen Display definitions and methods.
|
|
// The OSD is a popup area on the video controller which can be used to display
|
|
// text/menus and accept user input. Notably this is intended to be instantiated
|
|
// inside an I/O processor onboard the FPGA hosting the Sharp MZ Series emulation
|
|
// and provide a user interface in order to configure/interact with the emulation.
|
|
//
|
|
// Credits:
|
|
// Copyright: (c) 2019-2021 Philip Smart <philip.smart@net2net.org>
|
|
//
|
|
// History: v1.0 May 2021 - Initial write of the OSD software.
|
|
//
|
|
// Notes: See Makefile to enable/disable conditional components
|
|
//
|
|
/////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
// This source file 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 source file 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 <http://www.gnu.org/licenses/>.
|
|
/////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
#ifdef __cplusplus
|
|
extern "C" {
|
|
#endif
|
|
|
|
|
|
#if defined(__K64F__)
|
|
#include <stdio.h>
|
|
#include <ctype.h>
|
|
#include <stdint.h>
|
|
#include <string.h>
|
|
#include <unistd.h>
|
|
#include <stdarg.h>
|
|
#include <core_pins.h>
|
|
#include <usb_serial.h>
|
|
#include <Arduino.h>
|
|
#include "k64f_soc.h"
|
|
#include <../../libraries/include/stdmisc.h>
|
|
#elif defined(__ZPU__)
|
|
#include <stdint.h>
|
|
#include <stdio.h>
|
|
#include "zpu_soc.h"
|
|
#include <stdlib.h>
|
|
#include <ctype.h>
|
|
#include <stdmisc.h>
|
|
#else
|
|
#error "Target CPU not defined, use __ZPU__ or __K64F__"
|
|
#endif
|
|
|
|
#include "ff.h" /* Declarations of FatFs API */
|
|
#include "diskio.h"
|
|
#include "utils.h"
|
|
#include <fonts.h>
|
|
#include <bitmaps.h>
|
|
#include <tranzputer.h>
|
|
#include <osd.h>
|
|
|
|
// Debug macros
|
|
#define debugf(a, ...) if(osdWindow.debug) { printf("\033[1;31mOSD: " a "\033[0m\n", ##__VA_ARGS__); }
|
|
#define debugfx(a, ...) if(osdWindow.debug) { printf("\033[1;32mOSD: " a "\033[0m\n", ##__VA_ARGS__); }
|
|
|
|
#ifndef __APP__ // Protected methods which should only reside in the kernel on zOS.
|
|
static t_OSDWindow osdWindow = {.mode=MENU, .params={ {.attr=0, .row=0, .col=0, .maxCol=0, .maxRow=0, .lineWrap=1, .maxX=VC_STATUS_MAX_X_PIXELS, .maxY=VC_STATUS_MAX_Y_PIXELS,
|
|
.cursor={.flashing=0, .enabled=0}
|
|
},
|
|
{.attr=0, .row=0, .col=0, .maxCol=0, .maxRow=0, .lineWrap=1, .maxX=VC_MENU_MAX_X_PIXELS, .maxY=VC_MENU_MAX_Y_PIXELS,
|
|
.cursor={.flashing=0, .enabled=0}
|
|
}
|
|
},
|
|
.debug=0, .inDebug=0, .display=NULL};
|
|
|
|
// Real time millisecond counter, interrupt driven. Needs to be volatile in order to prevent the compiler optimising it away.
|
|
uint32_t volatile *msecs = &systick_millis_count;
|
|
|
|
// Method to get internal public member values. This module ideally should be written in C++ but with the limitations of the GNU C Compiler for the ZPU (v3.4.2) and the performance penalty on
|
|
// an embedded processor, it was decided to write it in C but the methodology and naming conventions (ie. OSDDrawLine = OSD.DrawLine, OSDInit = OSD::OSD constructor) are kept loosly
|
|
// associated with C++. Ideally for this getter method function overloading is required!
|
|
//
|
|
uint32_t OSDGet(enum OSDPARAMS param)
|
|
{
|
|
// Locals.
|
|
//
|
|
uint32_t result;
|
|
|
|
switch(param)
|
|
{
|
|
case ACTIVE_MAX_X:
|
|
result = (uint32_t)osdWindow.params[osdWindow.mode].maxX;
|
|
break;
|
|
|
|
case ACTIVE_MAX_Y:
|
|
result = (uint32_t)osdWindow.params[osdWindow.mode].maxY;
|
|
break;
|
|
|
|
default:
|
|
result = 0xFFFFFFFF;
|
|
break;
|
|
}
|
|
return(result);
|
|
}
|
|
|
|
// Method to retrieve a font definition structure based on the enumeration.
|
|
//
|
|
fontStruct *OSDGetFont(enum FONTS font)
|
|
{
|
|
// Locals.
|
|
//
|
|
fontStruct *fontptr;
|
|
|
|
// Obtain the font structure based on the provided type.
|
|
switch(font)
|
|
{
|
|
case FONT_3X6:
|
|
fontptr = &font3x6;
|
|
break;
|
|
|
|
case FONT_7X8:
|
|
fontptr = &font7x8extended;
|
|
break;
|
|
|
|
case FONT_9X16:
|
|
fontptr = &font9x16;
|
|
break;
|
|
|
|
case FONT_11X16:
|
|
fontptr = &font11x16;
|
|
break;
|
|
|
|
case FONT_5X7:
|
|
default:
|
|
fontptr = &font5x7extended;
|
|
break;
|
|
}
|
|
return(fontptr);
|
|
}
|
|
|
|
// Method to retrieve a bitmap definition structure based on the enumeration.
|
|
//
|
|
bitmapStruct *OSDGetBitmap(enum BITMAPS bitmap)
|
|
{
|
|
// Locals.
|
|
//
|
|
fontStruct *bitmapptr;
|
|
|
|
// Obtain the bitmap structure based on the provided type.
|
|
switch(bitmap)
|
|
{
|
|
case BITMAP_ARGO_SMALL:
|
|
bitmapptr = &argo64x32;
|
|
break;
|
|
|
|
case BITMAP_ARGO_MEDIUM:
|
|
bitmapptr = &argo128x64;
|
|
break;
|
|
|
|
case BITMAP_ARGO:
|
|
default:
|
|
bitmapptr = &argo256x128;
|
|
break;
|
|
}
|
|
return(bitmapptr);
|
|
}
|
|
|
|
// External method to set a pixel in the active framebuffer.
|
|
//
|
|
void OSDSetPixel(uint16_t x, uint16_t y, enum COLOUR colour)
|
|
{
|
|
if(y >= 0 && y < osdWindow.params[osdWindow.mode].maxY && x >= 0 && x < osdWindow.params[osdWindow.mode].maxX)
|
|
{
|
|
for(uint8_t c=0; c < (VC_MENU_RGB_BITS > VC_STATUS_RGB_BITS ? VC_MENU_RGB_BITS : VC_STATUS_RGB_BITS); c++)
|
|
{
|
|
if(colour & (1 << c))
|
|
{
|
|
osdWindow.display[c][((y * osdWindow.params[osdWindow.mode].maxX) + x)/8] |= 0x80 >> x%8;
|
|
}
|
|
}
|
|
}
|
|
return;
|
|
}
|
|
|
|
// External method to clear a pixel in the active framebuffer.
|
|
//
|
|
void OSDClearPixel(uint16_t x, uint16_t y, enum COLOUR colour)
|
|
{
|
|
if(y >= 0 && y < osdWindow.params[osdWindow.mode].maxY && x >= 0 && x < osdWindow.params[osdWindow.mode].maxX)
|
|
{
|
|
for(uint8_t c=0; c < (VC_MENU_RGB_BITS > VC_STATUS_RGB_BITS ? VC_MENU_RGB_BITS : VC_STATUS_RGB_BITS); c++)
|
|
{
|
|
if(colour & (1 << c))
|
|
{
|
|
osdWindow.display[c][((y * osdWindow.params[osdWindow.mode].maxX) + x)/8] &= ~(0x80 >> x%8);
|
|
}
|
|
}
|
|
}
|
|
return;
|
|
}
|
|
|
|
// External method to change the colour of a pixel
|
|
void OSDChangePixelColour(uint16_t x, uint16_t y, enum COLOUR fg, enum COLOUR bg)
|
|
{
|
|
// Locals.
|
|
uint8_t isPixelSet = 0;
|
|
|
|
if(y >= 0 && y < osdWindow.params[osdWindow.mode].maxY && x >= 0 && x < osdWindow.params[osdWindow.mode].maxX)
|
|
{
|
|
// See if a pixel is set at this co-ordinate independent of colour.
|
|
for(uint8_t c=0; c < (VC_MENU_RGB_BITS > VC_STATUS_RGB_BITS ? VC_MENU_RGB_BITS : VC_STATUS_RGB_BITS); c++)
|
|
{
|
|
isPixelSet |= osdWindow.display[c][((y * osdWindow.params[osdWindow.mode].maxX) + x)/8];
|
|
// Clear out the pixel as it will be redefined.
|
|
osdWindow.display[c][((y * osdWindow.params[osdWindow.mode].maxX) + x)/8] &= ~(0x80 >> x%8);
|
|
}
|
|
|
|
// Go through all the colours and set the new colour.
|
|
for(uint8_t c=0; c < (VC_MENU_RGB_BITS > VC_STATUS_RGB_BITS ? VC_MENU_RGB_BITS : VC_STATUS_RGB_BITS); c++)
|
|
{
|
|
// Active pixels take on the foreground colour.
|
|
if(fg & (1 << c) && (isPixelSet & (0x80 >> x%8)))
|
|
{
|
|
osdWindow.display[c][((y * osdWindow.params[osdWindow.mode].maxX) + x)/8] |= 0x80 >> x%8;
|
|
|
|
// Inactive pixels take on the background colour.
|
|
} else if(bg & (1 << c) && !(isPixelSet & (0x80 >> x%8)))
|
|
{
|
|
osdWindow.display[c][((y * osdWindow.params[osdWindow.mode].maxX) + x)/8] |= 0x80 >> x%8;
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
// Internal method to write a single character into the status/menu framebuffer. The X/Y location is specified in font units, also the orientation,
|
|
// colour and required font.
|
|
//
|
|
void _OSDwrite(uint8_t x, uint8_t y, int8_t xoff, int8_t yoff, uint8_t xpad, uint8_t ypad, enum ORIENTATION orientation, uint8_t chr, uint16_t attr, enum COLOUR fg, enum COLOUR bg, fontStruct *font)
|
|
{
|
|
// Locals.
|
|
uint16_t startX;
|
|
uint16_t startY;
|
|
uint16_t addr;
|
|
uint16_t height;
|
|
uint16_t width;
|
|
uint16_t spacing;
|
|
uint8_t vChrRow;
|
|
uint8_t bytesInChar;
|
|
uint8_t bitStartOffset;
|
|
uint8_t bitOffset;
|
|
uint8_t bitPos;
|
|
uint8_t chrByteSize;
|
|
uint8_t chrByteOffset;
|
|
|
|
// Check bounds of character.
|
|
if(chr < font->start || chr > font->end)
|
|
{
|
|
debugf("Character out of bounds:%02x(%d,%d)\n", chr, font->start, font->end);
|
|
return;
|
|
}
|
|
|
|
// Calculate the starting byte.
|
|
switch(orientation)
|
|
{
|
|
case DEG90:
|
|
width = font->height;
|
|
height = font->width;
|
|
spacing = font->spacing;
|
|
startX = osdWindow.params[osdWindow.mode].maxX - ((y+1) * (width + spacing)) - yoff;
|
|
startY = x * (height + spacing) + xoff;
|
|
break;
|
|
|
|
case DEG180:
|
|
width = font->width;
|
|
height = font->height;
|
|
spacing = font->spacing;
|
|
startX = osdWindow.params[osdWindow.mode].maxX - ((x+1) * (width + spacing)) - xoff;
|
|
startY = osdWindow.params[osdWindow.mode].maxY - ((y+1) * (height + spacing)) - yoff;
|
|
break;
|
|
|
|
case DEG270:
|
|
width = font->height;
|
|
height = font->width;
|
|
spacing = font->spacing;
|
|
startX = (y * (width + spacing)) + yoff;
|
|
startY = osdWindow.params[osdWindow.mode].maxY - ((x+1) * (height + spacing)) - xoff;
|
|
break;
|
|
|
|
case NORMAL:
|
|
default:
|
|
width = font->width;
|
|
height = font->height;
|
|
spacing = font->spacing;
|
|
startX = (x * (width + spacing + 2*xpad)) + xpad + xoff;
|
|
startY = (y * (height + spacing + 2*ypad)) + ypad + yoff;
|
|
break;
|
|
}
|
|
|
|
// Cant write if out of bounds.
|
|
if(startX > osdWindow.params[osdWindow.mode].maxX || startY > osdWindow.params[osdWindow.mode].maxY || startX+width > osdWindow.params[osdWindow.mode].maxX || startY+height > osdWindow.params[osdWindow.mode].maxY)
|
|
{
|
|
debugf("Position out of bounds:%d,%d(%d,%d). Max:%d,%d\n", startX, startY, x, y, osdWindow.params[osdWindow.mode].maxX, osdWindow.params[osdWindow.mode].maxY);
|
|
return;
|
|
}
|
|
|
|
// Write according to orientation.
|
|
switch(orientation)
|
|
{
|
|
case DEG90:
|
|
for(int16_t row=-ypad; row < height+ypad; row++)
|
|
{
|
|
for(int16_t col=-xpad; col < width+spacing+xpad; col++)
|
|
{
|
|
for(uint8_t colourMode = 0; colourMode < (VC_MENU_RGB_BITS > VC_STATUS_RGB_BITS ? VC_MENU_RGB_BITS : VC_STATUS_RGB_BITS); colourMode++)
|
|
{
|
|
// Work out positional information.
|
|
bytesInChar = (width/8) + 1;
|
|
bitStartOffset = ((width%8) != 0 ? (8-(width%8)) : 0);
|
|
bitPos = (col+bitStartOffset)/8 == 0 ? bitStartOffset + (col%8) : col%8;
|
|
chrByteSize = (width < 8 ? 1 : width/8);
|
|
chrByteOffset = (abs(width - col - 1)/8);
|
|
|
|
// When 'in the money', get an 8bit part row of the font based on the row/col.
|
|
vChrRow = (row < 0 || row >= height || col < 0 || col >= width) ? 0 : font->bitmap[((chr - font->start) * (height * chrByteSize)) + (row * chrByteSize) + chrByteOffset ];
|
|
|
|
// Calculate destination address.
|
|
// <- Start pos based on Y -> <- Offset to X ->
|
|
addr=((startY + row) * (osdWindow.params[osdWindow.mode].maxX/8)) + (startX + col)/8;
|
|
|
|
// Calculate the bit offset in the targetted byte according to the font width.
|
|
bitOffset = (startX+col)%8;
|
|
|
|
// Test to see if a foreground or background pixel is set and update the framebuffer accordingly.
|
|
if(vChrRow & 0x80 >> bitPos)
|
|
{
|
|
if(((attr & HILIGHT_FG_ACTIVE) && ((attr&~HILIGHT_FG_ACTIVE) & (1 << colourMode))) || (!(attr & HILIGHT_FG_ACTIVE) && (fg & (1 << colourMode))))
|
|
osdWindow.display[colourMode][addr] |= 0x80 >> bitOffset;
|
|
else
|
|
osdWindow.display[colourMode][addr] &= ~(0x80 >> bitOffset);
|
|
if(osdWindow.debug && colourMode == 0) { printf("*"); }
|
|
}
|
|
else if(((attr & HILIGHT_BG_ACTIVE) && ((attr&~HILIGHT_BG_ACTIVE) & (1 << colourMode))) || (!(attr & HILIGHT_BG_ACTIVE) && (bg & (1 << colourMode))))
|
|
{
|
|
osdWindow.display[colourMode][addr] |= 0x80 >> bitOffset;
|
|
if(osdWindow.debug && colourMode == 0) { printf(" "); }
|
|
} else
|
|
{
|
|
osdWindow.display[colourMode][addr] &= ~(0x80 >> bitOffset);
|
|
if(osdWindow.debug && colourMode == 0) { printf(" "); }
|
|
}
|
|
}
|
|
|
|
}
|
|
if(osdWindow.debug)
|
|
printf("\n");
|
|
}
|
|
break;
|
|
|
|
case DEG180:
|
|
for(int16_t row=-ypad; row < height+ypad; row++)
|
|
{
|
|
for(int16_t col=-xpad; col < width+spacing+xpad; col++)
|
|
{
|
|
for(uint8_t colourMode = 0; colourMode < (VC_MENU_RGB_BITS > VC_STATUS_RGB_BITS ? VC_MENU_RGB_BITS : VC_STATUS_RGB_BITS); colourMode++)
|
|
{
|
|
chrByteSize = (height < 8 ? 1 : height/8);
|
|
bitStartOffset = ((height%8) != 0 ? (8-(height%8)) : 0);
|
|
bitPos = (row+bitStartOffset)/8 == 0 ? bitStartOffset + (row%8) : row%8;
|
|
|
|
// Calculate destination address.
|
|
// <- Start pos based on Y -> <- Offset to X ->
|
|
addr=((startY + row) * (osdWindow.params[osdWindow.mode].maxX/8)) + ((startX + width + spacing - col - 1)/8);
|
|
|
|
// When 'in the money', get an 8bit part row of the font based on the row/col.
|
|
vChrRow = (row < 0 || row >= height || col < 0 || col >= width) ? 0 : font->bitmap[((chr - font->start) * (width * chrByteSize)) + (height > 8 ? col*2 : col) + (height-row-1)/8];
|
|
|
|
// Calculate the bit offset in the targetted byte according to the font width.
|
|
bitOffset = 8 - ((startX+(width-col))%8) - 1;
|
|
|
|
if(vChrRow & 0x80 >> bitPos)
|
|
{
|
|
if(((attr & HILIGHT_FG_ACTIVE) && ((attr&~HILIGHT_FG_ACTIVE) & (1 << colourMode))) || (!(attr & HILIGHT_FG_ACTIVE) && (fg & (1 << colourMode))))
|
|
osdWindow.display[colourMode][addr] |= 1 << bitOffset;
|
|
else
|
|
osdWindow.display[colourMode][addr] &= ~(1 << bitOffset);
|
|
if(osdWindow.debug && colourMode == 0) { printf("*"); }
|
|
}
|
|
else if(((attr & HILIGHT_BG_ACTIVE) && ((attr&~HILIGHT_BG_ACTIVE) & (1 << colourMode))) || (!(attr & HILIGHT_BG_ACTIVE) && (bg & (1 << colourMode))))
|
|
{
|
|
osdWindow.display[colourMode][addr] |= 1 << bitOffset;
|
|
if(osdWindow.debug && colourMode == 0) { printf(" "); }
|
|
} else
|
|
{
|
|
osdWindow.display[colourMode][addr] &= ~(1 << bitOffset);
|
|
if(osdWindow.debug && colourMode == 0) { printf(" "); }
|
|
}
|
|
}
|
|
}
|
|
if(osdWindow.debug)
|
|
printf("\n");
|
|
}
|
|
break;
|
|
|
|
case DEG270:
|
|
for(int16_t row=-ypad; row < height+ypad; row++)
|
|
{
|
|
for(int16_t col=-xpad; col < width+spacing+xpad; col++)
|
|
{
|
|
for(uint8_t colourMode = 0; colourMode < (VC_MENU_RGB_BITS > VC_STATUS_RGB_BITS ? VC_MENU_RGB_BITS : VC_STATUS_RGB_BITS); colourMode++)
|
|
{
|
|
// Work out positional information.
|
|
bytesInChar = (width/8) + 1;
|
|
bitStartOffset = ((width%8) != 0 ? (8-(width%8)) : 0);
|
|
bitPos = col%8;
|
|
chrByteSize = (width < 8 ? 1 : width/8);
|
|
chrByteOffset = (col/8);
|
|
|
|
// When 'in the money', get an 8bit part row of the font based on the row/col.
|
|
vChrRow = (row < 0 || row >= height || col < 0 || col >= width) ? 0 : font->bitmap[((chr - font->start) * (height * chrByteSize)) + ((height-row-1) * chrByteSize) + chrByteOffset ];
|
|
|
|
// Calculate destination address.
|
|
// <- Start pos based on Y -> <- Offset to X ->
|
|
addr=((startY + row) * (osdWindow.params[osdWindow.mode].maxX/8)) + (startX + col)/8;
|
|
|
|
// Calculate the bit offset in the targetted byte according to the font width.
|
|
bitOffset = (startX+col)%8;
|
|
|
|
// Test to see if a pixel is set and update the framebuffer accordingly.
|
|
if(vChrRow & 1 << bitPos)
|
|
{
|
|
if(((attr & HILIGHT_FG_ACTIVE) && ((attr&~HILIGHT_FG_ACTIVE) & (1 << colourMode))) || (!(attr & HILIGHT_FG_ACTIVE) && (fg & (1 << colourMode))))
|
|
osdWindow.display[colourMode][addr] |= 0x80 >> bitOffset;
|
|
else
|
|
osdWindow.display[colourMode][addr] &= ~(0x80 >> bitOffset);
|
|
if(osdWindow.debug && colourMode == 0) { printf("*"); }
|
|
}
|
|
else if(((attr & HILIGHT_BG_ACTIVE) && ((attr&~HILIGHT_BG_ACTIVE) & (1 << colourMode))) || (!(attr & HILIGHT_BG_ACTIVE) && (bg & (1 << colourMode))))
|
|
{
|
|
osdWindow.display[colourMode][addr] |= 0x80 >> bitOffset;
|
|
if(osdWindow.debug && colourMode == 0) { printf(" "); }
|
|
} else
|
|
{
|
|
osdWindow.display[colourMode][addr] &= ~(0x80 >> bitOffset);
|
|
if(osdWindow.debug && colourMode == 0)
|
|
{
|
|
printf(" ");
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|
|
if(osdWindow.debug)
|
|
printf("\n");
|
|
}
|
|
break;
|
|
|
|
case NORMAL:
|
|
default:
|
|
// Trace out the bitmap into the framebuffer.
|
|
for(int16_t row=-ypad; row < height+ypad; row++)
|
|
{
|
|
for(int16_t col=-xpad; col < width+spacing+xpad; col++)
|
|
{
|
|
for(uint8_t colourMode = 0; colourMode < (VC_MENU_RGB_BITS > VC_STATUS_RGB_BITS ? VC_MENU_RGB_BITS : VC_STATUS_RGB_BITS); colourMode++)
|
|
{
|
|
// Calculate destination address.
|
|
// <- Start pos based on Y -> <- Offset to X ->
|
|
addr=((startY + row) * (osdWindow.params[osdWindow.mode].maxX/8)) + ((startX + col)/8);
|
|
|
|
// When 'in the money', get an 8bit part row of the font based on the row/col.
|
|
vChrRow = (row < 0 || row >= height || col < 0 || col >= width) ? 0 : font->bitmap[((chr - font->start) * (width * (height < 8 ? 1 : height/8))) + (height > 8 ? col*2 : col) + row/8];
|
|
|
|
// Calculate the bit offset in the targetted byte according to the font width.
|
|
bitOffset = (startX+col)%8;
|
|
|
|
if(vChrRow & (1 << (row % 8)))
|
|
{
|
|
if(((attr & HILIGHT_FG_ACTIVE) && ((attr&~HILIGHT_FG_ACTIVE) & (1 << colourMode))) || (!(attr & HILIGHT_FG_ACTIVE) && (fg & (1 << colourMode))))
|
|
osdWindow.display[colourMode][addr] |= 0x80 >> bitOffset;
|
|
else
|
|
osdWindow.display[colourMode][addr] &= ~(0x80 >> bitOffset);
|
|
if(osdWindow.debug && colourMode == 0) { printf("*"); }
|
|
}
|
|
else if(((attr & HILIGHT_BG_ACTIVE) && ((attr&~HILIGHT_BG_ACTIVE) & (1 << colourMode))) || (!(attr & HILIGHT_BG_ACTIVE) && (bg & (1 << colourMode))))
|
|
{
|
|
osdWindow.display[colourMode][addr] |= 0x80 >> bitOffset;
|
|
if(osdWindow.debug && colourMode == 0) { printf(" "); }
|
|
} else
|
|
{
|
|
osdWindow.display[colourMode][addr] &= ~(0x80 >> bitOffset);
|
|
if(osdWindow.debug && colourMode == 0)
|
|
{
|
|
printf(" ");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if(osdWindow.debug)
|
|
printf("\n");
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Method to draw a stored bitmap onto the OSD display.
|
|
void OSDWriteBitmap(uint16_t x, uint16_t y, enum BITMAPS bitmap, enum COLOUR fg, enum COLOUR bg)
|
|
{
|
|
// Locals.
|
|
bitmapStruct *bmptr = OSDGetBitmap(bitmap);
|
|
uint16_t addr;
|
|
uint16_t height = bmptr->height;
|
|
uint16_t width = bmptr->width;
|
|
uint8_t bytesPerRow = width % 8 == 0 ? width/8 : (width/8)+1;
|
|
uint8_t vChrRow;
|
|
uint8_t bitOffset;
|
|
|
|
// Check parameters.
|
|
if(x >= osdWindow.params[osdWindow.mode].maxX || y >= osdWindow.params[osdWindow.mode].maxY)
|
|
{
|
|
debugf("Bitmap coordinates out of range:(%d,%d)\n", x, y);
|
|
return;
|
|
}
|
|
|
|
// Trace out the bitmap into the framebuffer.
|
|
for(int16_t row=y; row < (y+height >= osdWindow.params[osdWindow.mode].maxY ? osdWindow.params[osdWindow.mode].maxY : y+height); row++)
|
|
{
|
|
for(int16_t col=x; col < (x+width >= osdWindow.params[osdWindow.mode].maxX ? osdWindow.params[osdWindow.mode].maxX : x+width); col++)
|
|
{
|
|
// Calculate the bitmap array location.
|
|
uint16_t bmAddr = ((col-x)/8) + ((row-y) * bytesPerRow);
|
|
|
|
// When 'in the money', get an 8bit part row of the bitmap based on the row/col.
|
|
//vChrRow = (row < 0 || row >= y+height || col < 0 || col >= x+width) ? 0 : bmptr->bitmap[(uint16_t)((((int)(col-x)/t)/8) + ((int)((row - y)/t) * bytesPerRow))];
|
|
vChrRow = bmptr->bitmap[bmAddr];
|
|
|
|
// Calculate the bit offset in the targetted byte according to the bmptr width.
|
|
bitOffset = (uint8_t)(col-x)%8;
|
|
|
|
// Calculate destination address.
|
|
// <- Start pos based on Y -> <- Offset to X ->
|
|
addr=(((uint16_t)row * osdWindow.params[osdWindow.mode].maxX)+ ((uint16_t)col))/8;
|
|
|
|
// Set/clear the pixel in the relevant colour plane.
|
|
for(uint8_t colourMode = 0; colourMode < (VC_MENU_RGB_BITS > VC_STATUS_RGB_BITS ? VC_MENU_RGB_BITS : VC_STATUS_RGB_BITS); colourMode++)
|
|
{
|
|
if(vChrRow & (0x80 >> bitOffset))
|
|
{
|
|
if(fg & (1 << colourMode))
|
|
osdWindow.display[colourMode][addr] |= 0x80 >> bitOffset;
|
|
else
|
|
osdWindow.display[colourMode][addr] &= ~(0x80 >> bitOffset);
|
|
if(osdWindow.debug && colourMode == 0) { printf("*"); }
|
|
}
|
|
else if(bg & (1 << colourMode))
|
|
{
|
|
osdWindow.display[colourMode][addr] |= 0x80 >> bitOffset;
|
|
if(osdWindow.debug && colourMode == 0) { printf(" "); }
|
|
} else
|
|
{
|
|
osdWindow.display[colourMode][addr] &= ~(0x80 >> bitOffset);
|
|
if(osdWindow.debug && colourMode == 0)
|
|
{
|
|
printf(" ");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if(osdWindow.debug)
|
|
printf("\n");
|
|
}
|
|
}
|
|
|
|
// External method to write a single character onto the required framebuffer. Font, orientation and colour can be specified. The X/Y co-ordinates
|
|
// are at the character level and suitably adjusted based on the font selected.
|
|
//
|
|
void OSDWriteChar(uint8_t x, uint8_t y, uint8_t xoff, uint8_t yoff, uint8_t xpad, uint8_t ypad, enum FONTS font, enum ORIENTATION orientation, char chr, enum COLOUR fg, enum COLOUR bg)
|
|
{
|
|
// Locals.
|
|
//
|
|
|
|
_OSDwrite(x, y, xoff, yoff, xpad, ypad, orientation, chr, NOATTR, fg, bg, OSDGetFont(font));
|
|
return;
|
|
}
|
|
|
|
// Method to write a string to the required framebuffer. The X/Y co-ordinates are relative to the orientation, ie. start - NORMAL=0/0, DEG90 = maxX-font width/0,
|
|
// DEG180 = maxX-font width/maxY-font height, DEG270 = 0/maxY-font height.
|
|
//
|
|
void OSDWriteString(uint8_t x, uint8_t y, int8_t xoff, int8_t yoff, uint8_t xpad, uint8_t ypad, enum FONTS font, enum ORIENTATION orientation, char *str, uint16_t *attr, enum COLOUR fg, enum COLOUR bg)
|
|
{
|
|
// Locals.
|
|
//
|
|
fontStruct *fontptr;
|
|
uint8_t startX;
|
|
uint8_t startY;
|
|
uint8_t maxX;
|
|
uint8_t maxY;
|
|
uint8_t xpos = x;
|
|
uint8_t ypos = y;
|
|
uint8_t *ptr;
|
|
uint16_t *aptr;
|
|
|
|
// Obtain the font structure based on the provided type.
|
|
fontptr = OSDGetFont(font);
|
|
|
|
// Use the orientation to set the physical start and maxim coordinates for the given font.
|
|
switch(orientation)
|
|
{
|
|
case DEG90:
|
|
startX=osdWindow.params[osdWindow.mode].maxX/(fontptr->height + fontptr->spacing);
|
|
startY=0;
|
|
maxX=osdWindow.params[osdWindow.mode].maxX/(fontptr->height + fontptr->spacing);
|
|
maxY=osdWindow.params[osdWindow.mode].maxY/(fontptr->width + fontptr->spacing);
|
|
break;
|
|
|
|
case DEG180:
|
|
startX=osdWindow.params[osdWindow.mode].maxX/(fontptr->width + fontptr->spacing);
|
|
startY=osdWindow.params[osdWindow.mode].maxY/(fontptr->height + fontptr->spacing);
|
|
maxX=osdWindow.params[osdWindow.mode].maxX/(fontptr->width + fontptr->spacing);
|
|
maxY=osdWindow.params[osdWindow.mode].maxY/(fontptr->height + fontptr->spacing);
|
|
break;
|
|
|
|
case DEG270:
|
|
startX=0;
|
|
startY=osdWindow.params[osdWindow.mode].maxY/(fontptr->width + fontptr->spacing);
|
|
maxX=osdWindow.params[osdWindow.mode].maxX/(fontptr->height + fontptr->spacing);
|
|
maxY=osdWindow.params[osdWindow.mode].maxY/(fontptr->width + fontptr->spacing);
|
|
break;
|
|
|
|
case NORMAL:
|
|
default:
|
|
startX=0;
|
|
startY=0;
|
|
maxX=osdWindow.params[osdWindow.mode].maxX/(fontptr->width + fontptr->spacing);
|
|
maxY=osdWindow.params[osdWindow.mode].maxY/(fontptr->height + fontptr->spacing);
|
|
break;
|
|
}
|
|
|
|
// Output the string.
|
|
for(ptr=str,aptr=attr; *ptr != 0x00; ptr++, aptr++)
|
|
{
|
|
_OSDwrite(xpos++, ypos, xoff, yoff, xpad, ypad, orientation, (*ptr), (attr != NULL ? (*aptr) : NOATTR), fg, bg, fontptr);
|
|
|
|
if(xpos > maxX)
|
|
{
|
|
if(!osdWindow.params[osdWindow.mode].lineWrap)
|
|
xpos--;
|
|
else if(ypos < maxY)
|
|
{
|
|
ypos++;
|
|
xpos=0;
|
|
}
|
|
}
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
// Method to refresh the OSD size parameters from the hardware. This is required should the screen resolution change which may change
|
|
// OSD size.
|
|
void OSDUpdateScreenSize(void)
|
|
{
|
|
// Locals.
|
|
uint8_t result;
|
|
uint8_t osdInData[8];
|
|
|
|
// Read the OSD Size parameters. These represent the X and Y size once multiplied by 8 (8 being the minimum size, ie. block size).
|
|
//
|
|
result=readZ80Array(VCADDR_8BIT_OSDMNU_SZX, osdInData, 6, FPGA);
|
|
if(!result)
|
|
{
|
|
osdWindow.params[STATUS].maxX = (uint16_t)(osdInData[2] * 8);
|
|
osdWindow.params[STATUS].maxY = (uint16_t)(osdInData[3] * 8) + (uint16_t)(osdInData[5] * 8);
|
|
osdWindow.params[MENU].maxX = (uint16_t)(osdInData[0] * 8);
|
|
osdWindow.params[MENU].maxY = (uint16_t)(osdInData[1] * 8);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
// Method to refresh the active screen from the buffer contents.
|
|
void OSDRefreshScreen(void)
|
|
{
|
|
// Locals.
|
|
//
|
|
uint32_t addr = VIDEO_OSD_BLUE_ADDR;
|
|
|
|
// Loop through the colour buffers and write contents out to the FPGA memory.
|
|
for(uint8_t c=0; c < (VC_MENU_RGB_BITS > VC_STATUS_RGB_BITS ? VC_MENU_RGB_BITS : VC_STATUS_RGB_BITS); c++)
|
|
{
|
|
writeZ80Array(addr, osdWindow.display[c], (VC_MENU_BUFFER_SIZE > VC_STATUS_BUFFER_SIZE ? VC_MENU_BUFFER_SIZE : VC_STATUS_BUFFER_SIZE), FPGA);
|
|
addr += 0x10000;
|
|
}
|
|
return;
|
|
}
|
|
|
|
// Simple screen clear method using memset to erase all pixels to inactive state.
|
|
//
|
|
void OSDClearScreen(enum COLOUR colour)
|
|
{
|
|
// Clear the buffer memory to effect a blank screen.
|
|
for(uint8_t c=0; c < (VC_MENU_RGB_BITS > VC_STATUS_RGB_BITS ? VC_MENU_RGB_BITS : VC_STATUS_RGB_BITS); c++)
|
|
{
|
|
memset(osdWindow.display[c], (colour & 1 << c ? 0xFF : 0x00), VC_MENU_BUFFER_SIZE > VC_STATUS_BUFFER_SIZE ? VC_MENU_BUFFER_SIZE : VC_STATUS_BUFFER_SIZE);
|
|
}
|
|
return;
|
|
}
|
|
|
|
// Clear a portion of the screen and set to a fixed colour.
|
|
//
|
|
void OSDClearArea(int16_t startX, int16_t startY, int16_t endX, int16_t endY, enum COLOUR colour)
|
|
{
|
|
// Locals.
|
|
int16_t sx = (startX == -1 ? 0 : startX);
|
|
int16_t sy = (startY == -1 ? 0 : startY);
|
|
int16_t ex = (endX == -1 ? osdWindow.params[osdWindow.mode].maxX-1 : endX);
|
|
int16_t ey = (endY == -1 ? osdWindow.params[osdWindow.mode].maxY-1 : endY);
|
|
|
|
// Sanity check.
|
|
if(sx < 0 || ex >= osdWindow.params[osdWindow.mode].maxX || sx > ex || sy < 0 || ey >= osdWindow.params[osdWindow.mode].maxY || sy > ey)
|
|
return;
|
|
|
|
// Not the most efficient but speed not essential, go through the entire pixel area and clear or set the required colour bit for each pixel.
|
|
//
|
|
for(uint16_t row=0; row < osdWindow.params[osdWindow.mode].maxY; row++)
|
|
{
|
|
for(uint16_t col=0; col < osdWindow.params[osdWindow.mode].maxX; col++)
|
|
{
|
|
for(uint8_t c=0; c < (VC_MENU_RGB_BITS > VC_STATUS_RGB_BITS ? VC_MENU_RGB_BITS : VC_STATUS_RGB_BITS); c++)
|
|
{
|
|
if(row >= sy && row <= ey && col >= sx && col <= ex)
|
|
{
|
|
if(colour & (1 << c))
|
|
{
|
|
osdWindow.display[c][((row * osdWindow.params[osdWindow.mode].maxX) + col)/8] |= 0x80 >> col%8;
|
|
} else
|
|
{
|
|
osdWindow.display[c][((row * osdWindow.params[osdWindow.mode].maxX) + col)/8] &= ~(0x80 >> col%8);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return;
|
|
}
|
|
|
|
// Method to draw a line on the active window.
|
|
//
|
|
void OSDDrawLine(int16_t startX, int16_t startY, int16_t endX, int16_t endY, enum COLOUR colour)
|
|
{
|
|
// Locals.
|
|
int16_t sx = (startX == -1 ? osdWindow.params[osdWindow.mode].maxX-1 : startX);
|
|
int16_t sy = (startY == -1 ? osdWindow.params[osdWindow.mode].maxY-1 : startY);
|
|
int16_t ex = (endX == -1 ? osdWindow.params[osdWindow.mode].maxX-1 : endX);
|
|
int16_t ey = (endY == -1 ? osdWindow.params[osdWindow.mode].maxY-1 : endY);
|
|
int dx = abs (ex - sx), stx = sx < ex ? 1 : -1;
|
|
int dy = -abs (ey - sy), sty = sy < ey ? 1 : -1;
|
|
int err = dx + dy, e2; /* error value e_xy */
|
|
int16_t x = sx;
|
|
int16_t y = sy;
|
|
|
|
// Sanity check.
|
|
if(sx < 0 || ex >= osdWindow.params[osdWindow.mode].maxX || sx > ex || ey < 0 || ey >= osdWindow.params[osdWindow.mode].maxY || sy > ey)
|
|
return;
|
|
|
|
for (;;)
|
|
{
|
|
setPixel(sx, sy, colour);
|
|
|
|
if (sx == ex && sy == ey) break;
|
|
e2 = 2 * err;
|
|
if (e2 >= dy) { err += dy; sx += stx; } /* e_xy+e_x > 0 */
|
|
if (e2 <= dx) { err += dx; sy += sty; } /* e_xy+e_y < 0 */
|
|
}
|
|
return;
|
|
}
|
|
|
|
// Method to draw a circle in the active window.
|
|
//
|
|
void OSDDrawCircle(int16_t startX, int16_t startY, int16_t radius, enum COLOUR colour)
|
|
{
|
|
int16_t sx = (startX == -1 ? osdWindow.params[osdWindow.mode].maxX-1 : startX);
|
|
int16_t sy = (startY == -1 ? osdWindow.params[osdWindow.mode].maxY-1 : startY);
|
|
int16_t x = -radius;
|
|
int16_t y = 0;
|
|
int16_t err = 2-2*radius;
|
|
|
|
do {
|
|
setPixel(((sx-x)/VC_OSD_X_CORRECTION), ((sy+y)/VC_OSD_Y_CORRECTION), colour); /* I. Quadrant */
|
|
setPixel(((sx-y)/VC_OSD_X_CORRECTION), ((sy-x)/VC_OSD_Y_CORRECTION), colour); /* II. Quadrant */
|
|
setPixel(((sx+x)/VC_OSD_X_CORRECTION), ((sy-y)/VC_OSD_Y_CORRECTION), colour); /* III. Quadrant */
|
|
setPixel(((sx+y)/VC_OSD_X_CORRECTION), ((sy+x)/VC_OSD_Y_CORRECTION), colour); /* IV. Quadrant */
|
|
|
|
radius = err;
|
|
if (radius > x) err += ++x*2+1; /* e_xy+e_x > 0 */
|
|
if (radius <= y) err += ++y*2+1; /* e_xy+e_y < 0 */
|
|
} while (x < 0);
|
|
return;
|
|
}
|
|
|
|
// A brute force method to create a filled circle in the active window.
|
|
//
|
|
void OSDDrawFilledCircle(int16_t startX, int16_t startY, int16_t radius, enum COLOUR colour)
|
|
{
|
|
int16_t sx = (startX == -1 ? osdWindow.params[osdWindow.mode].maxX-1 : startX);
|
|
int16_t sy = (startY == -1 ? osdWindow.params[osdWindow.mode].maxY-1 : startY);
|
|
int16_t r2 = radius * radius;
|
|
|
|
for(int y=-radius; y<=radius; y++)
|
|
{
|
|
for(int x=-radius; x<=radius; x++)
|
|
{
|
|
if(x*x+y*y <= r2)
|
|
{
|
|
setPixel(((sx+x)/VC_OSD_X_CORRECTION), ((sy+y)/VC_OSD_Y_CORRECTION), colour);
|
|
}
|
|
}
|
|
}
|
|
return;
|
|
}
|
|
|
|
|
|
// Method to draw an ellipse in the active window.
|
|
//
|
|
void OSDDrawEllipse(int16_t startX, int16_t startY, int16_t endX, int16_t endY, enum COLOUR colour)
|
|
{
|
|
int16_t sx = (startX == -1 ? osdWindow.params[osdWindow.mode].maxX-1 : startX);
|
|
int16_t sy = (startY == -1 ? osdWindow.params[osdWindow.mode].maxY-1 : startY);
|
|
int16_t ex = (endX == -1 ? osdWindow.params[osdWindow.mode].maxX-1 : endX);
|
|
int16_t ey = (endY == -1 ? osdWindow.params[osdWindow.mode].maxY-1 : endY);
|
|
uint16_t a = abs(ex - sx);
|
|
uint16_t b = abs(ey - sy);
|
|
uint16_t b1 = b & 1; /* values of diameter */
|
|
long dx = 4 * (1 - a) * b * b;
|
|
long dy = 4 * (b1 + 1) * a * a; /* error increment */
|
|
long err = dx + dy + b1 * a * a;
|
|
long e2; /* error of 1.step */
|
|
|
|
if (sx > ex) { sx = ex; ex += a; } /* if called with swapped points */
|
|
if (sy > ey) sy = ey; /* .. exchange them */
|
|
sy += (b + 1) / 2;
|
|
ey = sy-b1; /* starting pixel */
|
|
a *= 8 * a; b1 = 8 * b * b;
|
|
do
|
|
{
|
|
setPixel((ex/VC_OSD_X_CORRECTION), (sy/VC_OSD_Y_CORRECTION), colour); /* I. Quadrant */
|
|
setPixel((sx/VC_OSD_X_CORRECTION), (sy/VC_OSD_Y_CORRECTION), colour); /* II. Quadrant */
|
|
setPixel((sx/VC_OSD_X_CORRECTION), (ey/VC_OSD_Y_CORRECTION), colour); /* III. Quadrant */
|
|
setPixel((ex/VC_OSD_X_CORRECTION), (ey/VC_OSD_Y_CORRECTION), colour); /* IV. Quadrant */
|
|
e2 = 2 * err;
|
|
|
|
/* x step */
|
|
if (e2 >= dx)
|
|
{
|
|
sx++;
|
|
ex--;
|
|
err += dx += b1;
|
|
}
|
|
|
|
/* y step */
|
|
if (e2 <= dy)
|
|
{
|
|
sy++;
|
|
ey--;
|
|
err += dy += a;
|
|
}
|
|
} while (sx <= ex);
|
|
|
|
/* too early stop of flat ellipses a=1 */
|
|
while (sy-ey < b)
|
|
{
|
|
setPixel(((sx-1)/VC_OSD_X_CORRECTION), (sy/VC_OSD_Y_CORRECTION), colour); /* I. Quadrant */
|
|
setPixel(((ex+1)/VC_OSD_X_CORRECTION), (sy/VC_OSD_Y_CORRECTION), colour); /* I. Quadrant */
|
|
sy++;
|
|
setPixel(((sx-1)/VC_OSD_X_CORRECTION), (ey/VC_OSD_Y_CORRECTION), colour); /* I. Quadrant */
|
|
setPixel(((ex+1)/VC_OSD_X_CORRECTION), (ey/VC_OSD_Y_CORRECTION), colour); /* I. Quadrant */
|
|
ey--;
|
|
}
|
|
}
|
|
|
|
// Method to change the active window used by the OSD routines. Only one window can be active at a time as they share underlying resources
|
|
// (ie. FPGA BRAM, OSD display buffer etc).
|
|
void OSDSetActiveWindow(enum WINDOWS window)
|
|
{
|
|
// Set the starting default window.
|
|
osdWindow.mode = window;
|
|
return;
|
|
}
|
|
|
|
|
|
// Method to setup the data structures and enable flashing of a cursor at a given point, in a given font wth necessary attributes.
|
|
// This mechanism is used for data entry where the next character typically appears at the flashing curspr.
|
|
void OSDSetCursorFlash(uint8_t col, uint8_t row, uint8_t offsetCol, uint8_t offsetRow, enum FONTS font, uint8_t dispChar, enum COLOUR fg, enum COLOUR bg, uint16_t attr, unsigned long speed)
|
|
{
|
|
// Disable any active cursor.
|
|
if(osdWindow.params[osdWindow.mode].cursor.enabled)
|
|
OSDClearCursorFlash();
|
|
|
|
// Store the given cursor data.
|
|
osdWindow.params[osdWindow.mode].cursor.row = row;
|
|
osdWindow.params[osdWindow.mode].cursor.col = col;
|
|
osdWindow.params[osdWindow.mode].cursor.ofrow = offsetRow;
|
|
osdWindow.params[osdWindow.mode].cursor.ofcol = offsetCol;
|
|
osdWindow.params[osdWindow.mode].cursor.font = font;
|
|
osdWindow.params[osdWindow.mode].cursor.dispChar = dispChar;
|
|
osdWindow.params[osdWindow.mode].cursor.attr = attr;
|
|
osdWindow.params[osdWindow.mode].cursor.fg = fg;
|
|
osdWindow.params[osdWindow.mode].cursor.bg = bg;
|
|
osdWindow.params[osdWindow.mode].cursor.speed = speed;
|
|
|
|
// Enable the cursor.
|
|
osdWindow.params[osdWindow.mode].cursor.enabled = 1;
|
|
osdWindow.params[osdWindow.mode].cursor.flashing = 0;
|
|
|
|
return;
|
|
}
|
|
|
|
// Method to clear any running cursor and restore the character under the cursor.
|
|
//
|
|
void OSDClearCursorFlash(void)
|
|
{
|
|
// Locals.
|
|
char lineBuf[2];
|
|
|
|
// Verify there is an active cursor.
|
|
if(osdWindow.params[osdWindow.mode].cursor.enabled)
|
|
{
|
|
// Restore the character under the cursor to original value.
|
|
lineBuf[0] = osdWindow.params[osdWindow.mode].cursor.dispChar;
|
|
lineBuf[1] = 0x00;
|
|
OSDWriteString(osdWindow.params[osdWindow.mode].cursor.col, osdWindow.params[osdWindow.mode].cursor.row, osdWindow.params[osdWindow.mode].cursor.ofcol, osdWindow.params[osdWindow.mode].cursor.ofrow, 0, 0, osdWindow.params[osdWindow.mode].cursor.font, NORMAL, lineBuf, NULL, osdWindow.params[osdWindow.mode].cursor.fg, osdWindow.params[osdWindow.mode].cursor.bg);
|
|
OSDRefreshScreen();
|
|
|
|
// Disable the cursor flashing.
|
|
osdWindow.params[osdWindow.mode].cursor.enabled = 0;
|
|
osdWindow.params[osdWindow.mode].cursor.flashing = 0;
|
|
}
|
|
return;
|
|
}
|
|
|
|
// Method to flash a cursor. This is based on a timer and using the hilight properties of text write, rewrites the character at the position where the cursor should
|
|
// appear in the correct format.
|
|
void OSDCursorFlash(void)
|
|
{
|
|
// Locals.
|
|
static unsigned long time = 0;
|
|
char lineBuf[2];
|
|
uint16_t attrBuf[2];
|
|
uint32_t timeElapsed;
|
|
|
|
// Get elapsed time since last service poll.
|
|
timeElapsed = *msecs - time;
|
|
|
|
// If the elapsed time is greater than the flash speed, toggle the flash state.
|
|
if(osdWindow.params[osdWindow.mode].cursor.enabled == 1 && timeElapsed > osdWindow.params[osdWindow.mode].cursor.speed)
|
|
{
|
|
|
|
lineBuf[0] = osdWindow.params[osdWindow.mode].cursor.dispChar;
|
|
lineBuf[1] = 0x00;
|
|
attrBuf[0] = osdWindow.params[osdWindow.mode].cursor.attr;
|
|
attrBuf[1] = 0x0000;
|
|
OSDWriteString(osdWindow.params[osdWindow.mode].cursor.col, osdWindow.params[osdWindow.mode].cursor.row, osdWindow.params[osdWindow.mode].cursor.ofcol, osdWindow.params[osdWindow.mode].cursor.ofrow, 0, 0, osdWindow.params[osdWindow.mode].cursor.font, NORMAL, lineBuf, osdWindow.params[osdWindow.mode].cursor.flashing == 0 ? NULL : attrBuf, osdWindow.params[osdWindow.mode].cursor.fg, osdWindow.params[osdWindow.mode].cursor.bg);
|
|
OSDRefreshScreen();
|
|
|
|
// Set to next flash state.
|
|
osdWindow.params[osdWindow.mode].cursor.flashing = osdWindow.params[osdWindow.mode].cursor.flashing == 0 ? 1 : 0;
|
|
|
|
// Reset the timer.
|
|
time = *msecs;
|
|
}
|
|
return;
|
|
}
|
|
|
|
// Method to allow for OSD periodic updates as needed.
|
|
//
|
|
void OSDService(void)
|
|
{
|
|
// Call the cursor flash routine to allow an interactive flashing cursor if enabled.
|
|
//
|
|
OSDCursorFlash();
|
|
|
|
return;
|
|
}
|
|
|
|
// Initialise the OSD subsystem. This method only needs to be called once, calling subsequent times will free and reallocate memory.
|
|
//
|
|
uint8_t OSDInit(enum WINDOWS window)
|
|
{
|
|
// Locals.
|
|
//
|
|
uint8_t result = 0;
|
|
|
|
// Allocate heap for the OSD display buffers. The size is set to the maximum required buffer.
|
|
if(osdWindow.display != NULL)
|
|
{
|
|
debugf("Freeing OSD display framebuffer:%08lx\n", osdWindow.display);
|
|
free(osdWindow.display);
|
|
}
|
|
|
|
// Allocate largest required block on the heap which will act as the OSD window framebuffer - this is necessary as updating the physical display is time consuming due to control overhead.
|
|
//
|
|
osdWindow.display = malloc(VC_MENU_RGB_BITS * VC_MENU_BUFFER_SIZE > VC_STATUS_RGB_BITS * VC_STATUS_BUFFER_SIZE ? VC_MENU_RGB_BITS * VC_MENU_BUFFER_SIZE : VC_STATUS_RGB_BITS * VC_STATUS_BUFFER_SIZE);
|
|
if(osdWindow.display == NULL)
|
|
{
|
|
printf("Failed to allocate heap for the OSD display framebuffer, %d bytes\n", VC_MENU_RGB_BITS * VC_MENU_BUFFER_SIZE > VC_STATUS_RGB_BITS * VC_STATUS_BUFFER_SIZE ? VC_MENU_RGB_BITS * VC_MENU_BUFFER_SIZE : VC_STATUS_RGB_BITS * VC_STATUS_BUFFER_SIZE);
|
|
result = 1;
|
|
} else
|
|
{
|
|
debugf("OSD window framebuffer allocated: %dBytes@%08lx\n", VC_MENU_RGB_BITS * VC_MENU_BUFFER_SIZE > VC_STATUS_RGB_BITS * VC_STATUS_BUFFER_SIZE ? VC_MENU_RGB_BITS * VC_MENU_BUFFER_SIZE : VC_STATUS_RGB_BITS * VC_STATUS_BUFFER_SIZE, osdWindow.display);
|
|
}
|
|
|
|
// Clear screen ready for use.
|
|
OSDClearScreen(BLACK);
|
|
|
|
// Set the starting default window.
|
|
OSDSetActiveWindow(window);
|
|
|
|
return(result);
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
#ifdef __cplusplus
|
|
}
|
|
#endif
|