mirror of
https://github.com/smartykit/apple1.git
synced 2025-03-14 02:29:59 +00:00
Arduino libraries for Keyboard and Video drivers Co-Authored-By: Computer construction kit SmartyKit <smartykit@users.noreply.github.com>
1100 lines
28 KiB
C++
Executable File
1100 lines
28 KiB
C++
Executable File
/*
|
|
Copyright (c) 2010 Myles Metzer
|
|
Permission is hereby granted, free of charge, to any person
|
|
obtaining a copy of this software and associated documentation
|
|
files (the "Software"), to deal in the Software without
|
|
restriction, including without limitation the rights to use,
|
|
copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
copies of the Software, and to permit persons to whom the
|
|
Software is furnished to do so, subject to the following
|
|
conditions:
|
|
The above copyright notice and this permission notice shall be
|
|
included in all copies or substantial portions of the Software.
|
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
|
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
|
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
|
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
|
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
|
OTHER DEALINGS IN THE SOFTWARE.
|
|
*/
|
|
/*
|
|
This library provides a simple method for outputting data to a tv
|
|
from a frame buffer stored in sram. This implementation is done
|
|
completly by interupt and will return give as much cpu time to the
|
|
application as possible.
|
|
*/
|
|
|
|
#include "TVout.h"
|
|
|
|
/* call this to start video output with the default resolution.
|
|
Arguments:
|
|
mode: The video standard to follow:
|
|
PAL = 1 = _PAL
|
|
NTSC = 0 = _NTSC
|
|
Returns:
|
|
0 if no error.
|
|
4 if there is not enough memory.
|
|
*/
|
|
|
|
uint8_t TVout::begin(uint8_t mode) {
|
|
return begin(mode, 128, 96);
|
|
}
|
|
|
|
|
|
/* call this to start video output with a specified resolution.
|
|
Arguments:
|
|
mode: The video standard to follow:
|
|
PAL = 1 = _PAL
|
|
NTSC = 0 = _NTSC
|
|
x: Horizonal resolution must be divisable by 8.
|
|
y: Vertical resolution.
|
|
Returns:
|
|
0 if no error.
|
|
1 if x is not divisable by 8.
|
|
2 if y is to large (NTSC only cannot fill PAL vertical resolution by 8bit limit)
|
|
4 if there is not enough memory for the frame buffer.
|
|
*/
|
|
|
|
uint8_t TVout::begin(uint8_t mode, uint8_t x, uint8_t y) {
|
|
// check if x is divisable by 8
|
|
if (x % 8) {
|
|
return 1;
|
|
}
|
|
x = x / 8;
|
|
screen = (unsigned char*)malloc(x * y * sizeof(unsigned char));
|
|
if (screen == NULL) {
|
|
return 4;
|
|
}
|
|
_cursorX = 0;
|
|
_cursorY = 0;
|
|
render_setup(mode, x, y, screen);
|
|
clearScreen();
|
|
return 0;
|
|
}
|
|
|
|
// stop video render and free the used memory
|
|
void TVout::end() {
|
|
TIMSK1 = 0;
|
|
free(screen);
|
|
}
|
|
|
|
|
|
/* Gets the Horizontal resolution of the screen
|
|
Returns: The horizonal resolution.
|
|
*/
|
|
unsigned int TVout::hres() {
|
|
return display.hres * 8;
|
|
}
|
|
|
|
/* Gets the Vertical resolution of the screen
|
|
Returns: The vertical resolution
|
|
*/
|
|
unsigned int TVout::vres() {
|
|
return display.vres;
|
|
}
|
|
|
|
/* Return the number of characters that will fit on a line
|
|
Returns: The number of characters that will fit on a text line starting from x = 0.
|
|
Will return -1 for dynamic width fonts as this cannot be determined.
|
|
*/
|
|
char TVout::charLine() {
|
|
return ((display.hres * 8) / pgm_read_byte(font));
|
|
}
|
|
|
|
/* Delay for x ms
|
|
The resolution is 16ms for NTSC and 20ms for PAL
|
|
Arguments:
|
|
x: The number of ms this function should consume.
|
|
*/
|
|
void TVout::delay(unsigned int x) {
|
|
unsigned long time = millis() + x;
|
|
while (millis() < time);
|
|
}
|
|
|
|
/* Delay for x frames, exits at the end of the last display line.
|
|
delayFrame(1) is useful prior to drawing so there is little/no flicker.
|
|
Arguments:
|
|
x: The number of frames to delay for.
|
|
*/
|
|
void TVout::delayFrame(unsigned int x) {
|
|
int stop_line = (int)(display.start_render + (display.vres*(display.vscale_const + 1 ))) + 1;
|
|
while (x) {
|
|
while (display.scanLine != stop_line);
|
|
while (display.scanLine == stop_line);
|
|
x--;
|
|
}
|
|
}
|
|
|
|
|
|
/* Get the time in ms since begin was called.
|
|
The resolution is 16ms for NTSC and 20ms for PAL
|
|
Returns: The time in ms since video generation has started.
|
|
*/
|
|
unsigned long TVout::millis() {
|
|
if (display.lines_frame == _NTSC_LINE_FRAME) {
|
|
return display.frames * _NTSC_TIME_SCANLINE * _NTSC_LINE_FRAME / 1000;
|
|
}
|
|
else {
|
|
return display.frames * _PAL_TIME_SCANLINE * _PAL_LINE_FRAME / 1000;
|
|
}
|
|
}
|
|
|
|
void TVout::clearScreen() {
|
|
fill(0);
|
|
}
|
|
|
|
void TVout::invert() {
|
|
fill(2);
|
|
}
|
|
|
|
/* Fill the screen with some color.
|
|
Arguments:
|
|
color: The color to fill the screen with
|
|
*/
|
|
void TVout::fill(uint8_t color) {
|
|
switch(color) {
|
|
case BLACK:
|
|
_cursorX = 0;
|
|
_cursorY = 0;
|
|
for (int i = 0; i < (display.hres)*display.vres; i++)
|
|
display.screen[i] = 0;
|
|
break;
|
|
case WHITE:
|
|
_cursorX = 0;
|
|
_cursorY = 0;
|
|
for (int i = 0; i < (display.hres)*display.vres; i++)
|
|
display.screen[i] = 0xFF;
|
|
break;
|
|
case INVERT:
|
|
for (int i = 0; i < display.hres*display.vres; i++)
|
|
display.screen[i] = ~display.screen[i];
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* Set the color of a pixel
|
|
Arguments:
|
|
x: The x coordinate of the pixel.
|
|
y: The y coordinate of the pixel.
|
|
c: The color of the pixel
|
|
*/
|
|
void TVout::setPixel(uint8_t x, uint8_t y, char c) {
|
|
if (x >= display.hres * 8 || y >= display.vres)
|
|
return;
|
|
sp(x, y, c);
|
|
}
|
|
|
|
|
|
/* get the color of the pixel at x, y
|
|
Arguments:
|
|
x: The x coordinate of the pixel.
|
|
y: The y coordinate of the pixel.
|
|
Returns:
|
|
The color of the pixel
|
|
*/
|
|
bool TVout::getPixel(uint8_t x, uint8_t y) {
|
|
if (x >= display.hres * 8 || y >= display.vres)
|
|
return false;
|
|
if (display.screen[x / 8 + y * display.hres] & (0x80 >> (x & 7)))
|
|
return true;
|
|
return false;
|
|
}
|
|
|
|
/* Draw a line from one point to another
|
|
Arguments:
|
|
x0: The x coordinate of point 0.
|
|
y0: The y coordinate of point 0.
|
|
x1: The x coordinate of point 1.
|
|
y1: The y coordinate of point 1.
|
|
c: The color of the line.
|
|
*/
|
|
void TVout::drawLine(uint8_t x0, uint8_t y0, uint8_t x1, uint8_t y1, char c) {
|
|
if (x0 > display.hres*8 || y0 > display.vres || x1 > display.hres*8 || y1 > display.vres)
|
|
return;
|
|
if (x0 == x1)
|
|
drawColumn(x0, y0, y1, c);
|
|
else if (y0 == y1)
|
|
drawRow(y0, x0, x1, c);
|
|
else {
|
|
int e;
|
|
int dx, dy, j, temp;
|
|
char s1, s2, xchange;
|
|
int x, y;
|
|
|
|
x = x0;
|
|
y = y0;
|
|
|
|
// take absolute value
|
|
if (x1 < x0) {
|
|
dx = x0 - x1;
|
|
s1 = -1;
|
|
} else if (x1 == x0) {
|
|
dx = 0;
|
|
s1 = 0;
|
|
} else {
|
|
dx = x1 - x0;
|
|
s1 = 1;
|
|
}
|
|
|
|
if (y1 < y0) {
|
|
dy = y0 - y1;
|
|
s2 = -1;
|
|
} else if (y1 == y0) {
|
|
dy = 0;
|
|
s2 = 0;
|
|
} else {
|
|
dy = y1 - y0;
|
|
s2 = 1;
|
|
}
|
|
|
|
xchange = 0;
|
|
|
|
if (dy > dx) {
|
|
temp = dx;
|
|
dx = dy;
|
|
dy = temp;
|
|
xchange = 1;
|
|
}
|
|
|
|
e = ((int)dy << 1) - dx;
|
|
|
|
for (j = 0; j <= dx; j++) {
|
|
sp(x, y, c);
|
|
|
|
if (e >= 0) {
|
|
if (xchange == 1) {
|
|
x = x + s1;
|
|
} else {
|
|
y = y + s2;
|
|
}
|
|
e = e - ((int)dx << 1);
|
|
}
|
|
if (xchange == 1) {
|
|
y = y + s2;
|
|
} else {
|
|
x = x + s1;
|
|
}
|
|
e = e + ((int)dy << 1);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/* Fill a row from one point to another
|
|
Argument:
|
|
line: The row that fill will be performed on.
|
|
x0: edge 0 of the fill.
|
|
x1: edge 1 of the fill.
|
|
c: the color of the fill.
|
|
*/
|
|
void TVout::drawRow(uint8_t line, uint16_t x0, uint16_t x1, uint8_t c) {
|
|
uint8_t lbit, rbit;
|
|
|
|
if (x0 == x1) {
|
|
setPixel(x0, line, c);
|
|
} else {
|
|
if (x0 > x1) {
|
|
lbit = x0;
|
|
x0 = x1;
|
|
x1 = lbit;
|
|
}
|
|
lbit = 0xff >> (x0 & 7);
|
|
x0 = x0 / 8 + display.hres * line;
|
|
rbit = ~(0xff >> (x1 & 7));
|
|
x1 = x1 / 8 + display.hres * line;
|
|
if (x0 == x1) {
|
|
lbit = lbit & rbit;
|
|
rbit = 0;
|
|
}
|
|
if (c == WHITE) {
|
|
screen[x0++] |= lbit;
|
|
while (x0 < x1) {
|
|
screen[x0++] = 0xff;
|
|
}
|
|
screen[x0] |= rbit;
|
|
} else if (c == BLACK) {
|
|
screen[x0++] &= ~lbit;
|
|
while (x0 < x1) {
|
|
screen[x0++] = 0;
|
|
}
|
|
screen[x0] &= ~rbit;
|
|
} else if (c == INVERT) {
|
|
screen[x0++] ^= lbit;
|
|
while (x0 < x1) {
|
|
screen[x0++] ^= 0xff;
|
|
}
|
|
screen[x0] ^= rbit;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/* Fill a column from one point to another
|
|
Argument:
|
|
row: The row that fill will be performed on.
|
|
y0: edge 0 of the fill.
|
|
y1: edge 1 of the fill.
|
|
c: the color of the fill.
|
|
*/
|
|
void TVout::drawColumn(uint8_t row, uint16_t y0, uint16_t y1, uint8_t c) {
|
|
unsigned char bit;
|
|
int byte;
|
|
|
|
if (y0 == y1) {
|
|
setPixel(row, y0, c);
|
|
} else {
|
|
if (y1 < y0) {
|
|
bit = y0;
|
|
y0 = y1;
|
|
y1 = bit;
|
|
}
|
|
bit = 0x80 >> (row&7);
|
|
byte = row/8 + y0*display.hres;
|
|
if (c == WHITE) {
|
|
while (y0 <= y1) {
|
|
screen[byte] |= bit;
|
|
byte += display.hres;
|
|
y0++;
|
|
}
|
|
} else if (c == BLACK) {
|
|
while (y0 <= y1) {
|
|
screen[byte] &= ~bit;
|
|
byte += display.hres;
|
|
y0++;
|
|
}
|
|
} else if (c == INVERT) {
|
|
while ( y0 <= y1) {
|
|
screen[byte] ^= bit;
|
|
byte += display.hres;
|
|
y0++;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/* draw a rectangle at x,y with a specified width and height
|
|
Arguments:
|
|
x0: The x coordinate of upper left corner of the rectangle.
|
|
y0: The y coordinate of upper left corner of the rectangle.
|
|
w: The widht of the rectangle.
|
|
h: The height of the rectangle.
|
|
c: The color of the rectangle.
|
|
fc: The fill color of the rectangle.
|
|
*/
|
|
void TVout::drawRect(uint8_t x0, uint8_t y0, uint8_t w, uint8_t h, char c, char fc) {
|
|
if (fc != -1) {
|
|
for (unsigned char i = y0; i < y0 + h; i++) {
|
|
drawRow(i, x0, x0 + w, fc);
|
|
}
|
|
}
|
|
drawLine(x0, y0, x0 + w, y0, c);
|
|
drawLine(x0, y0, x0, y0 + h, c);
|
|
drawLine(x0 + w, y0, x0 + w, y0 + h, c);
|
|
drawLine(x0, y0 + h, x0 + w, y0 + h, c);
|
|
}
|
|
|
|
|
|
/* draw a circle given a coordinate x,y and radius both filled and non filled.
|
|
Arguments:
|
|
x0: The x coordinate of the center of the circle.
|
|
y0: The y coordinate of the center of the circle.
|
|
radius: The radius of the circle.
|
|
c: The color of the circle.
|
|
fc: The color to fill the circle.
|
|
*/
|
|
void TVout::drawCircle(uint8_t x0, uint8_t y0, uint8_t radius, char c, char fc) {
|
|
int f = 1 - radius;
|
|
int ddF_x = 1;
|
|
int ddF_y = -2 * radius;
|
|
int x = 0;
|
|
int y = radius;
|
|
uint8_t pyy = y, pyx = x;
|
|
|
|
// there is a fill color
|
|
if (fc != -1) {
|
|
drawRow(y0, x0 - radius, x0 + radius, fc);
|
|
}
|
|
|
|
sp(x0, y0 + radius, c);
|
|
sp(x0, y0 - radius, c);
|
|
sp(x0 + radius, y0, c);
|
|
sp(x0 - radius, y0, c);
|
|
|
|
while(x < y) {
|
|
if (f >= 0) {
|
|
y--;
|
|
ddF_y += 2;
|
|
f += ddF_y;
|
|
}
|
|
x++;
|
|
ddF_x += 2;
|
|
f += ddF_x;
|
|
|
|
// there is a fill color
|
|
if (fc != -1) {
|
|
// prevent double draws on the same rows
|
|
if (pyy != y) {
|
|
drawRow(y0+y, x0 - x, x0 + x, fc);
|
|
drawRow(y0-y, x0 - x, x0 + x, fc);
|
|
}
|
|
if (pyx != x && x != y) {
|
|
drawRow(y0 + x, x0 - y, x0 + y, fc);
|
|
drawRow(y0 - x, x0 - y, x0 + y, fc);
|
|
}
|
|
pyy = y;
|
|
pyx = x;
|
|
}
|
|
sp(x0 + x, y0 + y,c);
|
|
sp(x0 - x, y0 + y,c);
|
|
sp(x0 + x, y0 - y,c);
|
|
sp(x0 - x, y0 - y,c);
|
|
sp(x0 + y, y0 + x,c);
|
|
sp(x0 - y, y0 + x,c);
|
|
sp(x0 + y, y0 - x,c);
|
|
sp(x0 - y, y0 - x,c);
|
|
}
|
|
}
|
|
|
|
|
|
/* place a bitmap at x,y where the bitmap is defined as {width,height,imagedata....}
|
|
Arguments:
|
|
x: The x coordinate of the upper left corner.
|
|
y: The y coordinate of the upper left corner.
|
|
bmp: The bitmap data to print.
|
|
i: The offset into the image data to start at. This is mainly used for fonts
|
|
default = 0
|
|
width: Override the bitmap width. This is mainly used for fonts.
|
|
default = 0 (do not override)
|
|
height: Override the bitmap height. This is mainly used for fonts.
|
|
default = 0 (do not override)
|
|
*/
|
|
void TVout::bitmap(uint8_t x, uint8_t y, const unsigned char * bmp,
|
|
uint16_t i, uint8_t width, uint8_t lines) {
|
|
uint8_t temp, lshift, rshift, save, xtra;
|
|
uint16_t si = 0;
|
|
|
|
rshift = x & 7;
|
|
lshift = 8 - rshift;
|
|
if (width == 0) {
|
|
width = pgm_read_byte((uint32_t)(bmp) + i);
|
|
i++;
|
|
}
|
|
if (lines == 0) {
|
|
lines = pgm_read_byte((uint32_t)(bmp) + i);
|
|
i++;
|
|
}
|
|
|
|
if (width & 7) {
|
|
xtra = width & 7;
|
|
width = width / 8;
|
|
width++;
|
|
} else {
|
|
xtra = 8;
|
|
width = width / 8;
|
|
}
|
|
|
|
for (uint8_t l = 0; l < lines; l++) {
|
|
si = (y + l) * display.hres + x / 8;
|
|
if (width == 1) {
|
|
temp = 0xff >> rshift + xtra;
|
|
} else {
|
|
temp = 0;
|
|
}
|
|
save = screen[si];
|
|
screen[si] &= ((0xff << lshift) | temp);
|
|
temp = pgm_read_byte((uint32_t)(bmp) + i++);
|
|
screen[si++] |= temp >> rshift;
|
|
for (uint16_t b = i + width - 1; i < b; i++) {
|
|
save = screen[si];
|
|
screen[si] = temp << lshift;
|
|
temp = pgm_read_byte((uint32_t)(bmp) + i);
|
|
screen[si++] |= temp >> rshift;
|
|
}
|
|
if (rshift + xtra < 8) {
|
|
screen[si - 1] |= (save & (0xff >> rshift + xtra));
|
|
}
|
|
if (rshift + xtra - 8 > 0) {
|
|
screen[si] &= (0xff >> rshift + xtra - 8);
|
|
}
|
|
screen[si] |= temp << lshift;
|
|
}
|
|
}
|
|
|
|
/* shift the pixel buffer in any direction
|
|
This function will shift the screen in a direction by any distance.
|
|
Arguments:
|
|
distance: The distance to shift the screen
|
|
direction: The direction to shift the screen the direction and the integer values:
|
|
UP = 0
|
|
DOWN = 1
|
|
LEFT = 2
|
|
RIGHT = 3
|
|
*/
|
|
|
|
void TVout::shift(uint8_t distance, uint8_t direction) {
|
|
uint8_t * src;
|
|
uint8_t * dst;
|
|
uint8_t * end;
|
|
uint8_t shift;
|
|
uint8_t tmp;
|
|
switch (direction) {
|
|
case UP:
|
|
dst = display.screen;
|
|
src = display.screen + distance*display.hres;
|
|
end = display.screen + display.vres*display.hres;
|
|
while (src <= end) {
|
|
*dst = *src;
|
|
*src = 0;
|
|
dst++;
|
|
src++;
|
|
}
|
|
break;
|
|
case DOWN:
|
|
dst = display.screen + display.vres*display.hres;
|
|
src = dst - distance*display.hres;
|
|
end = display.screen;
|
|
while (src >= end) {
|
|
*dst = *src;
|
|
*src = 0;
|
|
dst--;
|
|
src--;
|
|
}
|
|
break;
|
|
case LEFT:
|
|
shift = distance & 7;
|
|
for (uint8_t line = 0; line < display.vres; line++) {
|
|
dst = display.screen + display.hres*line;
|
|
src = dst + distance/8;
|
|
end = dst + display.hres-2;
|
|
while (src <= end) {
|
|
tmp = 0;
|
|
tmp = *src << shift;
|
|
*src = 0;
|
|
src++;
|
|
tmp |= *src >> (8 - shift);
|
|
*dst = tmp;
|
|
dst++;
|
|
}
|
|
tmp = 0;
|
|
tmp = *src << shift;
|
|
*src = 0;
|
|
*dst = tmp;
|
|
}
|
|
break;
|
|
case RIGHT:
|
|
shift = distance & 7;
|
|
for (uint8_t line = 0; line < display.vres; line++) {
|
|
dst = display.screen + display.hres-1 + display.hres*line;
|
|
src = dst - distance/8;
|
|
end = dst - display.hres+2;
|
|
while (src >= end) {
|
|
tmp = 0;
|
|
tmp = *src >> shift;
|
|
*src = 0;
|
|
src--;
|
|
tmp |= *src << (8 - shift);
|
|
*dst = tmp;
|
|
dst--;
|
|
}
|
|
tmp = 0;
|
|
tmp = *src >> shift;
|
|
*src = 0;
|
|
*dst = tmp;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
void TVout::printChar(uint8_t x, uint8_t y, unsigned char c) {
|
|
c -= pgm_read_byte(font + 2);
|
|
bitmap(x, y, font, (c * pgm_read_byte(font + 1)) + 3, pgm_read_byte(font), pgm_read_byte(font + 1));
|
|
}
|
|
|
|
void TVout::setCursor(uint8_t x, uint8_t y) {
|
|
_cursorX = x;
|
|
_cursorY = y;
|
|
}
|
|
|
|
void TVout::selectFont(const unsigned char* f) {
|
|
font = f;
|
|
}
|
|
|
|
void TVout::write(const char *str) {
|
|
while (*str) {
|
|
write(*str++);
|
|
}
|
|
}
|
|
|
|
void TVout::write(const uint8_t *buffer, uint8_t size) {
|
|
while (size--) {
|
|
write(*buffer++);
|
|
}
|
|
}
|
|
|
|
void TVout::write(uint8_t c) {
|
|
switch(c) {
|
|
// null
|
|
case '\0':
|
|
break;
|
|
// line feed
|
|
case '\n':
|
|
_cursorX = 0;
|
|
incTxtLine();
|
|
break;
|
|
// backspace
|
|
case 8:
|
|
_cursorX -= pgm_read_byte(font);
|
|
printChar(_cursorX, _cursorY,' ');
|
|
break;
|
|
// carriage return
|
|
case 13:
|
|
_cursorX = 0;
|
|
break;
|
|
// form feed new page(clear screen)
|
|
case 14:
|
|
//clearScreen();
|
|
break;
|
|
default:
|
|
if (_cursorX >= (display.hres * 8 - pgm_read_byte(font))) {
|
|
_cursorX = 0;
|
|
incTxtLine();
|
|
printChar(_cursorX, _cursorY, c);
|
|
} else {
|
|
printChar(_cursorX, _cursorY, c);
|
|
}
|
|
_cursorX += pgm_read_byte(font);
|
|
}
|
|
}
|
|
|
|
void TVout::print(const char str[]) {
|
|
write(str);
|
|
}
|
|
|
|
void TVout::print(char c, int base) {
|
|
print((long) c, base);
|
|
}
|
|
|
|
void TVout::print(unsigned char b, int base) {
|
|
print((unsigned long) b, base);
|
|
}
|
|
|
|
void TVout::print(int n, int base) {
|
|
print((long) n, base);
|
|
}
|
|
|
|
void TVout::print(unsigned int n, int base) {
|
|
print((unsigned long) n, base);
|
|
}
|
|
|
|
void TVout::print(long n, int base) {
|
|
if (base == 0) {
|
|
write(n);
|
|
} else if (base == 10) {
|
|
if (n < 0) {
|
|
print('-');
|
|
n = -n;
|
|
}
|
|
printNumber(n, 10);
|
|
} else {
|
|
printNumber(n, base);
|
|
}
|
|
}
|
|
|
|
void TVout::print(unsigned long n, int base) {
|
|
if (base == 0) {
|
|
write(n);
|
|
} else {
|
|
printNumber(n, base);
|
|
}
|
|
}
|
|
|
|
void TVout::print(float n, int digits) {
|
|
printFloat(n, digits);
|
|
}
|
|
|
|
void TVout::println(void) {
|
|
print('\r');
|
|
print('\n');
|
|
}
|
|
|
|
void TVout::println(const char c[]) {
|
|
print(c);
|
|
println();
|
|
}
|
|
|
|
void TVout::println(char c, int base) {
|
|
print(c, base);
|
|
println();
|
|
}
|
|
|
|
void TVout::println(unsigned char b, int base) {
|
|
print(b, base);
|
|
println();
|
|
}
|
|
|
|
void TVout::println(int n, int base) {
|
|
print(n, base);
|
|
println();
|
|
}
|
|
|
|
void TVout::println(unsigned int n, int base) {
|
|
print(n, base);
|
|
println();
|
|
}
|
|
|
|
void TVout::println(long n, int base) {
|
|
print(n, base);
|
|
println();
|
|
}
|
|
|
|
void TVout::println(unsigned long n, int base) {
|
|
print(n, base);
|
|
println();
|
|
}
|
|
|
|
void TVout::println(float n, int digits) {
|
|
print(n, digits);
|
|
println();
|
|
}
|
|
|
|
void TVout::printPGM(const char str[]) {
|
|
char c;
|
|
while ((c = pgm_read_byte(str))) {
|
|
str++;
|
|
write(c);
|
|
}
|
|
}
|
|
|
|
void TVout::printPGM(uint8_t x, uint8_t y, const char str[]) {
|
|
char c;
|
|
_cursorX = x;
|
|
_cursorY = y;
|
|
while ((c = pgm_read_byte(str))) {
|
|
str++;
|
|
write(c);
|
|
}
|
|
}
|
|
|
|
void TVout::print(uint8_t x, uint8_t y, const char str[]) {
|
|
_cursorX = x;
|
|
_cursorY = y;
|
|
write(str);
|
|
|
|
}
|
|
|
|
void TVout::print(uint8_t x, uint8_t y, char c, int base) {
|
|
_cursorX = x;
|
|
_cursorY = y;
|
|
print((long) c, base);
|
|
}
|
|
|
|
void TVout::print(uint8_t x, uint8_t y, unsigned char b, int base) {
|
|
_cursorX = x;
|
|
_cursorY = y;
|
|
print((unsigned long) b, base);
|
|
}
|
|
|
|
void TVout::print(uint8_t x, uint8_t y, int n, int base) {
|
|
_cursorX = x;
|
|
_cursorY = y;
|
|
print((long) n, base);
|
|
}
|
|
|
|
void TVout::print(uint8_t x, uint8_t y, unsigned int n, int base) {
|
|
_cursorX = x;
|
|
_cursorY = y;
|
|
print((unsigned long) n, base);
|
|
}
|
|
|
|
void TVout::print(uint8_t x, uint8_t y, long n, int base) {
|
|
_cursorX = x;
|
|
_cursorY = y;
|
|
print(n,base);
|
|
}
|
|
|
|
void TVout::print(uint8_t x, uint8_t y, unsigned long n, int base) {
|
|
_cursorX = x;
|
|
_cursorY = y;
|
|
print(n,base);
|
|
}
|
|
|
|
void TVout::print(uint8_t x, uint8_t y, float n, int digits) {
|
|
_cursorX = x;
|
|
_cursorY = y;
|
|
print(n,digits);
|
|
}
|
|
|
|
void TVout::println(uint8_t x, uint8_t y, const char c[]) {
|
|
_cursorX = x;
|
|
_cursorY = y;
|
|
print(c);
|
|
println();
|
|
}
|
|
|
|
void TVout::println(uint8_t x, uint8_t y, char c, int base) {
|
|
_cursorX = x;
|
|
_cursorY = y;
|
|
print(c, base);
|
|
println();
|
|
}
|
|
|
|
void TVout::println(uint8_t x, uint8_t y, unsigned char b, int base) {
|
|
_cursorX = x;
|
|
_cursorY = y;
|
|
print(b, base);
|
|
println();
|
|
}
|
|
|
|
void TVout::println(uint8_t x, uint8_t y, int n, int base) {
|
|
_cursorX = x;
|
|
_cursorY = y;
|
|
print(n, base);
|
|
println();
|
|
}
|
|
|
|
void TVout::println(uint8_t x, uint8_t y, unsigned int n, int base) {
|
|
_cursorX = x;
|
|
_cursorY = y;
|
|
print(n, base);
|
|
println();
|
|
}
|
|
|
|
void TVout::println(uint8_t x, uint8_t y, long n, int base) {
|
|
_cursorX = x;
|
|
_cursorY = y;
|
|
print(n, base);
|
|
println();
|
|
}
|
|
|
|
void TVout::println(uint8_t x, uint8_t y, unsigned long n, int base) {
|
|
_cursorX = x;
|
|
_cursorY = y;
|
|
print(n, base);
|
|
println();
|
|
}
|
|
|
|
void TVout::println(uint8_t x, uint8_t y, float n, int digits) {
|
|
_cursorX = x;
|
|
_cursorY = y;
|
|
print(n, digits);
|
|
println();
|
|
}
|
|
|
|
void TVout::printNumber(unsigned long n, uint8_t base) {
|
|
unsigned char buf[8 * sizeof(long)];
|
|
unsigned long i = 0;
|
|
if (n == 0) {
|
|
print('0');
|
|
return;
|
|
}
|
|
|
|
while (n > 0) {
|
|
buf[i++] = n % base;
|
|
n /= base;
|
|
}
|
|
|
|
for (; i > 0; i--) {
|
|
print((char) (buf[i - 1] < 10 ?
|
|
'0' + buf[i - 1] :
|
|
'A' + buf[i - 1] - 10));
|
|
}
|
|
}
|
|
|
|
void TVout::printFloat(float number, uint8_t digits) {
|
|
// Handle negative numbers
|
|
if (number < 0.0) {
|
|
print('-');
|
|
number = -number;
|
|
}
|
|
|
|
// Round correctly so that print(1.999, 2) prints as "2.00"
|
|
float rounding = 0.5;
|
|
for (uint8_t i = 0; i < digits; ++i) {
|
|
rounding /= 10.0;
|
|
}
|
|
number += rounding;
|
|
|
|
// Extract the integer part of the number and print it
|
|
unsigned long int_part = (unsigned long)number;
|
|
float remainder = number - (float)int_part;
|
|
print(int_part);
|
|
|
|
// Print the decimal point, but only if there are digits beyond
|
|
if (digits > 0) {
|
|
print(".");
|
|
}
|
|
|
|
// Extract digits from the remainder one at a time
|
|
while (digits-- > 0) {
|
|
remainder *= 10.0;
|
|
int toPrint = int(remainder);
|
|
print(toPrint);
|
|
remainder -= toPrint;
|
|
}
|
|
}
|
|
|
|
/* Simple tone generation
|
|
Arguments:
|
|
frequency: the frequency of the tone courtesy of adamwwolf
|
|
*/
|
|
void TVout::tone(unsigned int frequency) {
|
|
tone(frequency, 0);
|
|
}
|
|
|
|
/* Simple tone generation
|
|
Arguments:
|
|
frequency: the frequency of the tone
|
|
durationMS: the duration to play the tone in ms courtesy of adamwwolf
|
|
*/
|
|
void TVout::tone(unsigned int frequency, unsigned long durationMS) {
|
|
if (frequency == 0) {
|
|
return;
|
|
}
|
|
|
|
#define TIMER 2
|
|
// this is init code
|
|
TCCR2A = 0;
|
|
TCCR2B = 0;
|
|
TCCR2A |= _BV(WGM21);
|
|
TCCR2B |= _BV(CS20);
|
|
// end init code
|
|
|
|
// most of this is taken from Tone.cpp from Arduino
|
|
uint8_t prescalarbits = 0b001;
|
|
uint32_t ocr = 0;
|
|
|
|
// set pb3 (digital pin 11) to output
|
|
DDR_SND |= _BV(SND_PIN);
|
|
|
|
// we are using an 8 bit timer, scan through prescalars to find the best fit
|
|
ocr = F_CPU / frequency / 2 - 1;
|
|
// ck/1: same for both timers
|
|
prescalarbits = 0b001;
|
|
if (ocr > 255) {
|
|
ocr = F_CPU / frequency / 2 / 8 - 1;
|
|
// ck/8: same for both timers
|
|
prescalarbits = 0b010;
|
|
|
|
if (ocr > 255) {
|
|
ocr = F_CPU / frequency / 2 / 32 - 1;
|
|
prescalarbits = 0b011;
|
|
}
|
|
|
|
if (ocr > 255) {
|
|
ocr = F_CPU / frequency / 2 / 64 - 1;
|
|
prescalarbits = TIMER == 0 ? 0b011 : 0b100;
|
|
if (ocr > 255) {
|
|
ocr = F_CPU / frequency / 2 / 128 - 1;
|
|
prescalarbits = 0b101;
|
|
}
|
|
|
|
if (ocr > 255) {
|
|
ocr = F_CPU / frequency / 2 / 256 - 1;
|
|
prescalarbits = TIMER == 0 ? 0b100 : 0b110;
|
|
if (ocr > 255) {
|
|
// can't do any better than / 1024
|
|
ocr = F_CPU / frequency / 2 / 1024 - 1;
|
|
prescalarbits = TIMER == 0 ? 0b101 : 0b111;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
TCCR2B = prescalarbits;
|
|
|
|
if (durationMS > 0) {
|
|
// 60 here represents the framerate
|
|
remainingToneVsyncs = durationMS * 60 / 1000;
|
|
} else {
|
|
remainingToneVsyncs = -1;
|
|
}
|
|
|
|
// set the OCR for the given timer,
|
|
OCR2A = ocr;
|
|
// set it to toggle the pin by itself
|
|
// set COM2A1 to 0
|
|
TCCR2A &= ~(_BV(COM2A1));
|
|
TCCR2A |= _BV(COM2A0);
|
|
}
|
|
|
|
/* stops tone generation
|
|
*/
|
|
void TVout::noTone() {
|
|
TCCR2B = 0;
|
|
// set pin 11 to 0
|
|
PORT_SND &= ~(_BV(SND_PIN));
|
|
}
|
|
|
|
/* set the vertical blank function call
|
|
The function passed to this function will be called one per frame. The function should be quickish.
|
|
Arguments:
|
|
func: The function to call
|
|
*/
|
|
void TVout::setVBIHook(void (*func)()) {
|
|
vbi_hook = func;
|
|
}
|
|
|
|
|
|
/* set the horizonal blank function call
|
|
This function passed to this function will be called one per scan line.
|
|
The function MUST be VERY FAST(~2us max).
|
|
Arguments:
|
|
funct: The function to call
|
|
*/
|
|
void TVout::setHBIHook(void (*func)()) {
|
|
hbi_hook = func;
|
|
}
|
|
|
|
|
|
/* force the number of times to display each line
|
|
Arguments:
|
|
sfactor: The scale number of times to repeate each line.
|
|
*/
|
|
void TVout::forceVscale(char sfactor) {
|
|
delayFrame(1);
|
|
display.vscale_const = sfactor - 1;
|
|
display.vscale = sfactor - 1;
|
|
}
|
|
|
|
|
|
/* force the output start time of a scanline in micro seconds
|
|
Arguments:
|
|
time: The new output start time in micro seconds.
|
|
*/
|
|
void TVout::forceOutStart(uint8_t time) {
|
|
delayFrame(1);
|
|
display.output_delay = ((time * _CYCLES_PER_US) - 1);
|
|
}
|
|
|
|
|
|
/* force the start line for active video
|
|
Arguments:
|
|
line: The new active video output start line
|
|
*/
|
|
void TVout::forceLineStart(uint8_t line) {
|
|
delayFrame(1);
|
|
display.start_render = line;
|
|
}
|
|
|
|
void TVout::incTxtLine() {
|
|
if (_cursorY >= (display.vres - pgm_read_byte(font + 1)))
|
|
shift(pgm_read_byte(font + 1), UP);
|
|
else
|
|
_cursorY += pgm_read_byte(font + 1);
|
|
}
|
|
|
|
/* Inline version of setPixel that does not perform a bounds check
|
|
*/
|
|
static void inline sp(uint8_t x, uint8_t y, char c) {
|
|
if (c == 1) {
|
|
display.screen[(x / 8) + (y * display.hres)] |= 0x80 >> (x & 7);
|
|
} else if (c == 0) {
|
|
display.screen[(x / 8) + (y * display.hres)] &= ~0x80 >> (x & 7);
|
|
} else {
|
|
display.screen[(x / 8) + (y * display.hres)] ^= 0x80 >> (x & 7);
|
|
}
|
|
}
|