Some quality review. Made keyboard time milliseconds.

This commit is contained in:
lephe 2017-03-26 18:38:32 +02:00
parent 31e2b453dd
commit a6e312a39c
79 changed files with 1482 additions and 1175 deletions

View file

@ -13,7 +13,7 @@ include Makefile.cfg
# Modules # Modules
modules-gint = bopti clock core display events gray keyboard mmu rtc \ modules-gint = bopti clock core display events gray keyboard mmu rtc \
screen tales timer screen tales timer
modules-libc = setjmp stdio stdlib string time modules-libc = ctype setjmp stdio stdlib string time
# Targets # Targets
target-lib = libgint.a target-lib = libgint.a

4
TODO
View file

@ -23,8 +23,8 @@ Larger improvements:
* core: Allow return to menu * core: Allow return to menu
- serial: Implement a driver - serial: Implement a driver
- usb: Implement a driver - usb: Implement a driver
- esper: Cleaner playback, synthetizing - esper: Cleaner playback, synthesizing
- clock: Handle overclocking (relaunch clocks when overclocking) - clock: Handle overclock (relaunch clocks when overclocking)
- project: Unify this hellish mess of register access! - project: Unify this hellish mess of register access!
Things to investigate: Things to investigate:

View file

@ -288,7 +288,7 @@ void main_menu(int *category, int *app)
// Quite a few things to declare... // Quite a few things to declare...
//--- //---
extern Image res_opt_menu; extern image_t res_opt_menu;
const char *mpu, *mpu_names[] = { const char *mpu, *mpu_names[] = {
"Unknown", "Unknown",
@ -332,7 +332,7 @@ void main_menu(int *category, int *app)
int i; int i;
mpu = mpu_names[MPU_CURRENT < 5 ? MPU_CURRENT : 5]; mpu = mpu_names[MPU_CURRENT < 5 ? MPU_CURRENT : 5];
text_configure(NULL, Color_Black); text_configure(NULL, color_black);
while(1) while(1)
{ {
@ -386,8 +386,8 @@ void main_menu(int *category, int *app)
if(scroll > 0) locate(20, 2, "\x0d"); if(scroll > 0) locate(20, 2, "\x0d");
if(scroll + 6 < list_len) locate(20, 7, "\x0e"); if(scroll + 6 < list_len) locate(20, 7, "\x0e");
dreverse_area(0, 8 * (index - scroll) + 8, 127, drect(0, 8 * (index - scroll) + 8, 127,
8 * (index - scroll) + 15); 8 * (index - scroll) + 15, color_invert);
} }
dupdate(); dupdate();
@ -495,8 +495,6 @@ int main(void)
{ {
int category, app; int category, app;
sleep_ms(2000);
while(1) while(1)
{ {
main_menu(&category, &app); main_menu(&category, &app);

View file

@ -23,7 +23,7 @@
--------------------------------------------------------- ---------------------------------------------------------
*/ */
static void getwh(Image *img, int *width, int *height) static void getwh(image_t *img, int *width, int *height)
{ {
const uint8_t *data; const uint8_t *data;
@ -42,7 +42,7 @@ static void getwh(Image *img, int *width, int *height)
*height = (data[2] << 8) | data[3]; *height = (data[2] << 8) | data[3];
} }
static void getxy(Image *img, int *x, int *y) static void getxy(image_t *img, int *x, int *y)
{ {
int width, height; int width, height;
@ -51,10 +51,10 @@ static void getxy(Image *img, int *x, int *y)
*y = 28 - (height >> 1); *y = 28 - (height >> 1);
} }
static Image *select(Image *current) static image_t *select(image_t *current)
{ {
extern Image res_bopti_thumbs; extern image_t res_bopti_thumbs;
extern Image extern image_t
res_items, res_items,
res_sprites, res_sprites,
res_swords, res_swords,
@ -62,7 +62,7 @@ static Image *select(Image *current)
res_isometric; res_isometric;
struct { struct {
Image *img; image_t *img;
const char *name; const char *name;
const char *info; const char *info;
} images[] = { } images[] = {
@ -74,7 +74,7 @@ static Image *select(Image *current)
{ NULL, NULL, NULL } { NULL, NULL, NULL }
}; };
Image *thumbs = &res_bopti_thumbs; image_t *thumbs = &res_bopti_thumbs;
int items = 0; int items = 0;
static int row = 0; static int row = 0;
int leave = 1, i; int leave = 1, i;
@ -106,7 +106,7 @@ static Image *select(Image *current)
} }
} }
greverse_area(0, 8 * row + 8, 128, 8 * row + 23); grect(0, 8 * row + 8, 128, 8 * row + 23, color_invert);
gupdate(); gupdate();
do do
@ -138,8 +138,8 @@ static Image *select(Image *current)
void test_bopti(void) void test_bopti(void)
{ {
extern Image res_opt_bitmap; extern image_t res_opt_bitmap;
Image *img = NULL; image_t *img = NULL;
int leave = 1; int leave = 1;
int black_bg = 0; int black_bg = 0;
@ -152,10 +152,10 @@ void test_bopti(void)
gray_start(); gray_start();
gclear(); gclear();
if(black_bg) greverse_area(0, 0, 127, 63); if(black_bg) grect(0, 0, 127, 63, color_invert);
if(img) gimage(x, y, img); if(img) gimage(x, y, img);
gclear_area(0, 55, 127, 63); grect(0, 55, 127, 63, color_white);
gimage(0, 56, &res_opt_bitmap); gimage(0, 56, &res_opt_bitmap);
gupdate(); gupdate();
} }
@ -164,10 +164,10 @@ void test_bopti(void)
gray_stop(); gray_stop();
dclear(); dclear();
if(black_bg) dreverse_area(0, 0, 127, 63); if(black_bg) drect(0, 0, 127, 63, color_invert);
if(img) dimage(x, y, img); if(img) dimage(x, y, img);
dclear_area(0, 55, 127, 63); drect(0, 55, 127, 63, color_white);
dimage(0, 56, &res_opt_bitmap); dimage(0, 56, &res_opt_bitmap);
dupdate(); dupdate();
} }

View file

@ -10,9 +10,9 @@
static void draw(int delay1, int delay2, int selected) static void draw(int delay1, int delay2, int selected)
{ {
extern Image res_opt_gray; extern image_t res_opt_gray;
unsigned int *vl = gray_lightVRAM(); uint32_t *vl = gray_lightVRAM();
unsigned int *vd = gray_darkVRAM(); uint32_t *vd = gray_darkVRAM();
gclear(); gclear();
locate(1, 1, "Gray engine"); locate(1, 1, "Gray engine");
@ -58,7 +58,7 @@ void test_gray(void)
} }
changed = 0; changed = 0;
key = getkey_opt(Getkey_RepeatArrowKeys, 1); key = getkey_opt(getkey_repeat_arrow_keys, 25);
if(key == KEY_EXIT) break; if(key == KEY_EXIT) break;
changed = 1; changed = 1;

View file

@ -31,24 +31,24 @@ static void draw_keyboard(volatile uint8_t *state)
{ {
for(k = -2; k <= 2; k++) for(l = -2; l <= 2; l++) for(k = -2; k <= 2; k++) for(l = -2; l <= 2; l++)
if(abs(k) + abs(l) <= 2) if(abs(k) + abs(l) <= 2)
dpixel(x + k, y + l, Color_Black); dpixel(x + k, y + l, color_black);
} }
// Drawing a square border otherwise. // Drawing a square border otherwise.
else else
{ {
for(k = -1; k <= 1; k++) for(l = -1; l <= 1; l++) for(k = -1; k <= 1; k++) for(l = -1; l <= 1; l++)
if(k || l) dpixel(x + k, y + l, Color_Black); if(k || l) dpixel(x + k, y + l, color_black);
} }
} }
// Binding the arrow keys together for a more visual thing. // Binding the arrow keys together for a more visual thing.
dpixel(28, 19, Color_Black); dpixel(29, 19, Color_Black); dpixel(28, 19, color_black); dpixel(29, 19, color_black);
dpixel(28, 24, Color_Black); dpixel(29, 24, Color_Black); dpixel(28, 24, color_black); dpixel(29, 24, color_black);
dpixel(26, 21, Color_Black); dpixel(26, 22, Color_Black); dpixel(26, 21, color_black); dpixel(26, 22, color_black);
dpixel(31, 21, Color_Black); dpixel(31, 22, Color_Black); dpixel(31, 21, color_black); dpixel(31, 22, color_black);
// An horizontal line to separate parts of the keyboard. // An horizontal line to separate parts of the keyboard.
dline(5, 28, 32, 28, Color_Black); dline(5, 28, 32, 28, color_black);
} }
typedef struct { typedef struct {
@ -64,7 +64,7 @@ static void push_history(enhanced_event_t *history, int size, event_t event)
// Determining where the history ends. // Determining where the history ends.
int length = 0; int length = 0;
while(length < size && history[length].type != ET_None) length++; while(length < size && history[length].type != event_none) length++;
// Checking if the previous event is being repeated. // Checking if the previous event is being repeated.
if(length > 0 && event_eq(history[length - 1], event)) if(length > 0 && event_eq(history[length - 1], event))
@ -105,10 +105,10 @@ static void draw_events(enhanced_event_t *history, int size)
"None ", "User ", "Press", "Rept.", "Rel. ", "Timer" "None ", "User ", "Press", "Rept.", "Rel. ", "Timer"
}; };
for(int i = 0; i < size && history[i].type != ET_None; i++) for(int i = 0; i < size && history[i].type != event_none; i++)
{ {
print(8, 3 + i, "%s %s", event_names[history[i].type], print(8, 3 + i, "%s %s", event_names[history[i].type],
key_names[keyid(history[i].key)]); key_names[key_id(history[i].key)]);
if(history[i].repeats > 1) if(history[i].repeats > 1)
print(19, 3 + i, "%d", history[i].repeats); print(19, 3 + i, "%d", history[i].repeats);
} }
@ -124,18 +124,19 @@ void test_keyboard_events(void)
int history_size = 5; int history_size = 5;
event_t event; event_t event;
for(int i = 0; i < history_size; i++) history[i].type = ET_None; for(int i = 0; i < history_size; i++) history[i].type = event_none;
while(1) while(1)
{ {
dclear(); dclear();
locate(1, 1, "Keyboard and events"); locate(1, 1, "Keyboard and events");
draw_keyboard(keystate()); draw_keyboard(keyboard_stateBuffer());
draw_events(history, history_size); draw_events(history, history_size);
dupdate(); dupdate();
event = waitevent(); event = waitevent();
if(event.type == ET_KeyPress && event.key == KEY_EXIT) break; if(event.type == event_key_press && event.key == KEY_EXIT)
break;
push_history(history, history_size, event); push_history(history, history_size, event);
} }
} }

View file

@ -18,7 +18,7 @@
static void draw(rtc_time_t time) static void draw(rtc_time_t time)
{ {
extern Image res_rtc_segments; extern image_t res_rtc_segments;
const char *days[7] = { const char *days[7] = {
"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
@ -42,7 +42,7 @@ static void draw(rtc_time_t time)
12 * digits[i], 0, 11, 19); 12 * digits[i], 0, 11, 19);
// Drawing ':' between pairs of digits. // Drawing ':' between pairs of digits.
for(i = 0; i < 16; i++) dpixel(47 + 32 * (i >= 8) + (i & 1), for(i = 0; i < 16; i++) dpixel(47 + 32 * (i >= 8) + (i & 1),
14 + 5 * !!(i & 4) + !!(i & 2), Color_Black); 14 + 5 * !!(i & 4) + !!(i & 2), color_black);
// This should print time.year + 1900 but for the sake of this demo we // This should print time.year + 1900 but for the sake of this demo we
// have tweaked the field so that it already contains time.year + 1900. // have tweaked the field so that it already contains time.year + 1900.
@ -52,7 +52,7 @@ static void draw(rtc_time_t time)
static void callback(void) static void callback(void)
{ {
extern Image res_opt_rtc; extern image_t res_opt_rtc;
rtc_time_t time = rtc_getTime(); rtc_time_t time = rtc_getTime();
dclear(); dclear();
@ -115,8 +115,8 @@ static void set_region(rtc_time_t *time, int region, int value)
static void set(void) static void set(void)
{ {
extern Image res_opt_rtc; extern image_t res_opt_rtc;
Image *opt = &res_opt_rtc; image_t *opt = &res_opt_rtc;
struct { struct {
int x, y; int x, y;
@ -136,8 +136,8 @@ static void set(void)
{ {
dclear(); dclear();
draw(time); draw(time);
dreverse_area(regions[n].x, regions[n].y, regions[n].x drect(regions[n].x, regions[n].y, regions[n].x + regions[n].w
+ regions[n].w - 1, regions[n].y + regions[n].h - 1); - 1, regions[n].y + regions[n].h - 1, color_invert);
if(n == 6) dimage_part(0, 56, opt, 0, 9 * (1 + slide), 128, 8); if(n == 6) dimage_part(0, 56, opt, 0, 9 * (1 + slide), 128, 8);
if(n == 7) dimage_part(0, 56, opt, 0, 9 * (3 + slide), 128, 8); if(n == 7) dimage_part(0, 56, opt, 0, 9 * (3 + slide), 128, 8);
@ -191,9 +191,9 @@ static void set(void)
else leave = 0; else leave = 0;
} }
else if(isdigit(keychar(key))) // Numbers else if(isdigit(key_char(key))) // Numbers
{ {
int val = keychar(key) - '0'; int val = key_char(key) - '0';
int ok = 1; int ok = 1;
if(n == 0) ok = (val <= 2); if(n == 0) ok = (val <= 2);

View file

@ -27,7 +27,7 @@ static Font *select(Font *current)
while(1) while(1)
{ {
text_configure(NULL, Color_Black); text_configure(NULL, color_black);
dclear(); dclear();
locate(1, 1, "Select a font:"); locate(1, 1, "Select a font:");
@ -39,18 +39,18 @@ static Font *select(Font *current)
int height = fonts[i].font->line_height; int height = fonts[i].font->line_height;
int y = (i + 2) * 8 - 8 + ((7 - height) >> 1); int y = (i + 2) * 8 - 8 + ((7 - height) >> 1);
text_configure(fonts[i].font, Color_Black); text_configure(fonts[i].font, color_black);
dtext(7, y, fonts[i].name); dtext(7, y, fonts[i].name);
} }
else else
{ {
text_configure(NULL, Color_Black); text_configure(NULL, color_black);
locate(2, i + 2, fonts[i].name); locate(2, i + 2, fonts[i].name);
} }
} }
dreverse_area(0, 8 * row + 8, 128, 8 * row + 15); drect(0, 8 * row + 8, 128, 8 * row + 15, color_invert);
dupdate(); dupdate();
do do
@ -80,9 +80,9 @@ static Font *select(Font *current)
void test_tales(void) void test_tales(void)
{ {
enum Color colors[] = { Color_Black, Color_Dark, Color_Light, color_t colors[] = { color_black, color_dark, color_light, color_white,
Color_White, Color_Invert }; color_invert };
extern Image res_opt_tales; extern image_t res_opt_tales;
Font *font = NULL; Font *font = NULL;
int black_bg = 0; int black_bg = 0;
@ -94,7 +94,7 @@ void test_tales(void)
while(1) while(1)
{ {
gclear(); gclear();
if(black_bg) greverse_area(0, 0, 127, 54); if(black_bg) grect(0, 0, 127, 54, color_invert);
if(font) if(font)
{ {
@ -119,10 +119,10 @@ void test_tales(void)
gimage(0, 56, &res_opt_tales); gimage(0, 56, &res_opt_tales);
x = 45 + 8 * color; x = 45 + 8 * color;
gline(x, 57, x + 5, 57, Color_Black); gline(x, 57, x + 5, 57, color_black);
gline(x, 57, x, 62, Color_Black); gline(x, 57, x, 62, color_black);
gline(x + 5, 57, x + 5, 62, Color_Black); gline(x + 5, 57, x + 5, 62, color_black);
gline(x, 62, x + 5, 62, Color_Black); gline(x, 62, x + 5, 62, color_black);
gupdate(); gupdate();
@ -146,7 +146,7 @@ void test_tales(void)
case KEY_EXIT: case KEY_EXIT:
gray_stop(); gray_stop();
text_configure(NULL, Color_Black); text_configure(NULL, color_black);
return; return;
default: default:
leave = 0; leave = 0;

View file

@ -36,7 +36,7 @@ static void timing_timer(void)
static void timing_start(void) static void timing_start(void)
{ {
uint32_t delay = clock_setting(64, Clock_Hz); uint32_t delay = clock_setting(64, clock_Hz);
htimer = htimer_setup(timer_user, delay, timer_Po_4, 0); htimer = htimer_setup(timer_user, delay, timer_Po_4, 0);
timer_attach(htimer, timing_timer, NULL); timer_attach(htimer, timing_timer, NULL);
timer_start(htimer); timer_start(htimer);
@ -59,8 +59,8 @@ static void timing_start(void)
*/ */
static void small_text(int x, int y, const char *text, int alignment) static void small_text(int x, int y, const char *text, int alignment)
{ {
extern Image res_clock_chars; extern image_t res_clock_chars;
Image *chars = &res_clock_chars; image_t *chars = &res_clock_chars;
const char *table = "0123456789kMHz*/"; const char *table = "0123456789kMHz*/";
if(alignment) x -= 2 * strlen(text) - 1, y -= 2; if(alignment) x -= 2 * strlen(text) - 1, y -= 2;
@ -133,9 +133,9 @@ static void display_freq(int x, int y, int freq)
*/ */
static void draw(int tab) static void draw(int tab)
{ {
extern Image res_opt_timer; extern image_t res_opt_timer;
extern Image res_clock_7705; extern image_t res_clock_7705;
extern Image res_clock_7305; extern image_t res_clock_7305;
char buffer[16]; char buffer[16];
@ -163,14 +163,14 @@ static void draw(int tab)
small_text(84, 34, buffer, 1); small_text(84, 34, buffer, 1);
if(conf.Iphi_div1 == 1) if(conf.Iphi_div1 == 1)
dline(85, 43, 99, 43, Color_Black); dline(85, 43, 99, 43, color_black);
else else
{ {
sprintf(buffer, "/%d", conf.Iphi_div1); sprintf(buffer, "/%d", conf.Iphi_div1);
small_text(89, 41, buffer, 0); small_text(89, 41, buffer, 0);
} }
if(conf.Pphi_div1 == 1) if(conf.Pphi_div1 == 1)
dline(85, 50, 99, 50, Color_Black); dline(85, 50, 99, 50, color_black);
else else
{ {
sprintf(buffer, "/%d", conf.Pphi_div1); sprintf(buffer, "/%d", conf.Pphi_div1);
@ -249,13 +249,13 @@ void test_timer(void)
elapsed_rtc = -1; elapsed_rtc = -1;
cb_id = rtc_cb_add(RTCFreq_64Hz, timing_start, 0); cb_id = rtc_cb_add(RTCFreq_64Hz, timing_start, 0);
text_configure(NULL, Color_Black); text_configure(NULL, color_black);
while(1) while(1)
{ {
draw(tab); draw(tab);
switch(getkey_opt(Getkey_NoOption, 1)) switch(getkey_opt(getkey_none, 25))
{ {
case KEY_EXIT: case KEY_EXIT:
rtc_cb_end(cb_id); rtc_cb_end(cb_id);

View file

@ -3,12 +3,14 @@
// standard library module: alloca // standard library module: alloca
// //
// Allows dynamic memory allocation on the stack. Memory is automatically // Allows dynamic memory allocation on the stack. Memory is automatically
// freed when the calling function exits. // freed when the calling function exits, but this function suffers from
// risks of stack overflow; make sure you don't inline functions that use
// alloca or allocate more than a few hundred bytes with it.
// //
//--- //---
#ifndef _ALLOCA_H #ifndef _ALLOCA_H
#define _ALLOCA_H 1 #define _ALLOCA_H
#include <stddef.h> #include <stddef.h>

67
include/bopti.h Normal file
View file

@ -0,0 +1,67 @@
//---
//
// gint drawing module: bopti
//
// This module is a powerful bitmap renderer. It *heavily* relies on the
// line-based structure of the video RAM as well as the high density of
// information. A single CPU access (longword operation) can affect 32
// pixels at once, which is crucial for performance. The same goes for all
// other drawing modules, but this one typically has 350 lines of code
// just to wrap these longword accesses -- and it's blazingly fast.
//
//---
#ifndef _BOPTI_H
#define _BOPTI_H
/*
image_t
This structure holds meta-data of a bitmap encoded with fxconv. Data is
accessed using longword operations for performance considerations,
which requires that the all fields of the structure be properly aligned
and of a correct size.
*/
typedef struct
{
uint8_t magic;
uint8_t format;
uint8_t width;
uint8_t height;
const uint32_t data[];
} __attribute__((packed, aligned(4))) image_t;
/*
dimage()
Displays a monochrome image in the vram. This function does a real lot
of optimization.
*/
void dimage(int x, int y, image_t *image);
/*
dimage_part()
Draws a portion of an image, defined by its bounding rectangle.
Point (left, top) is included, but (left + width, top + height) is
excluded.
*/
void dimage_part(int x, int y, image_t *img, int left, int top, int width,
int height);
/*
gimage()
Displays a gray image in the dual-vram.
*/
void gimage(int x, int y, image_t *image);
/*
gimage_part()
Draws a portion of a gray image, defined by its bounding rectangle.
Point (left, top) is included, but (left + width, top + height) is
excluded.
*/
void gimage_part(int x, int y, image_t *image, int left, int top, int width,
int height);
#endif // _BOPTI_H

View file

@ -2,8 +2,14 @@
// //
// gint core module: clock // gint core module: clock
// //
// Measures the frequency of the MPU clocks. This module assumes that the // This module interfaces with the MPU clocks and is used to measure the
// clock mode is 3 on SH7305 (as does FTune). // clock frequencies at the beginning of execution. At this stage, it
// assumes that clock mode 3 is used on SH7305 (as does FTune), because
// there doesn't seem to be a way of getting this information.
//
// It also provides some sleep and time conversion functions, and access
// to how the clocks are configured. In the future, it would be the module
// that supports overclock.
// //
//--- //---
@ -18,7 +24,12 @@
/* /*
sleep() sleep()
Puts the processor to sleep until an interrupt request is issued. Puts the processor to sleep until an interrupt request is accepted.
This function should be called every time the program because idle
because it doesn't have anything to do -- between two game frames or
while waiting for a keyboard event.
This function is called by getkey_opt(), getkey(), waitevent(), this
module's sleep functions among others.
*/ */
void sleep(void); void sleep(void);
@ -41,17 +52,29 @@ void sleep_us(int us_delay);
// Clock management. // Clock management.
//--- //---
enum ClockUnit /*
clock_unit_t
Enumerated type used by the time conversion functions. It indicates the
type (delay or frequency) of a parameter.
*/
typedef enum
{ {
Clock_us = 0, clock_us = 0,
Clock_ms = 1, clock_ms = 1,
Clock_s = 2, clock_s = 2,
Clock_Hz = 10, clock_Hz = 10,
Clock_kHz = 11, clock_kHz = 11,
Clock_MHz = 12, clock_MHz = 12,
};
} clock_unit_t;
/*
clock_config_t
A copy of the Clock Pulse Generator (CPG) configuration. Be sure to
check which MPU the program is running on (using <mpu.h>) to access the
right fields.
*/
typedef struct typedef struct
{ {
union union
@ -75,9 +98,9 @@ typedef struct
int RTCCLK_f; // SH7305 int RTCCLK_f; // SH7305
}; };
int Bphi_f; int Bphi_f; // Bus clock frequency
int Iphi_f; int Iphi_f; // Processor clock frequency
int Pphi_f; int Pphi_f; // Peripheral clock frequency
} clock_config_t; } clock_config_t;
@ -86,10 +109,8 @@ typedef struct
Returns the P_phi / 4 timer setting that will last for the given time. Returns the P_phi / 4 timer setting that will last for the given time.
Several units can be used. Be aware that the result is approximate, and Several units can be used. Be aware that the result is approximate, and
very high frequencies or very short delays will yield important errors. very high frequencies or very short delays will yield important errors.
Normally you need not use this function when setting up timers because
timer_start() handles this conversion for you.
*/ */
uint32_t clock_setting(int duration, enum ClockUnit unit); uint32_t clock_setting(int duration, clock_unit_t unit);
/* /*
clock_config() clock_config()

View file

@ -7,78 +7,29 @@
//--- //---
#ifndef _CTYPE_H #ifndef _CTYPE_H
#define _CTYPE_H 1 #define _CTYPE_H
#include <stdint.h> #include <stdint.h>
//---
// Character classes.
//---
extern uint8_t ctype_classes[0x80]; extern uint8_t ctype_classes[0x80];
__attribute__((always_inline)) static inline int isalnum(int c) { // Character classes.
return ctype_classes[c] & 0xf0; #define isalnum(c) (ctype_classes[(int)(c)] & 0xf0)
} #define isalpha(c) (ctype_classes[(int)(c)] & 0x30)
#define iscntrl(c) (ctype_classes[(int)(c)] & 0x01)
#define isdigit(c) (ctype_classes[(int)(c)] & 0x40)
#define isgraph(c) (ctype_classes[(int)(c)] & 0xf4)
#define islower(c) (ctype_classes[(int)(c)] & 0x10)
#define isprint(c) (ctype_classes[(int)(c)] & 0x08)
#define ispunct(c) (ctype_classes[(int)(c)] & 0x04)
#define isspace(c) (ctype_classes[(int)(c)] & 0x02)
#define isupper(c) (ctype_classes[(int)(c)] & 0x20)
#define isxdigit(c) (ctype_classes[(int)(c)] & 0x80)
#define isascii(c) ((unsigned)c <= 0x7f)
#define isblank(c) (c == '\t' || c == ' ')
__attribute__((always_inline)) static inline int isalpha(int c) {
return ctype_classes[c] & 0x30;
}
__attribute__((always_inline)) static inline int iscntrl(int c) {
return ctype_classes[c] & 0x01;
}
__attribute__((always_inline)) static inline int isdigit(int c) {
return ctype_classes[c] & 0x40;
}
__attribute__((always_inline)) static inline int isgraph(int c) {
return ctype_classes[c] & 0xf4;
}
__attribute__((always_inline)) static inline int islower(int c) {
return ctype_classes[c] & 0x10;
}
__attribute__((always_inline)) static inline int isprint(int c) {
return ctype_classes[c] & 0x08;
}
__attribute__((always_inline)) static inline int ispunct(int c) {
return ctype_classes[c] & 0x04;
}
__attribute__((always_inline)) static inline int isspace(int c) {
return ctype_classes[c] & 0x02;
}
__attribute__((always_inline)) static inline int isupper(int c) {
return ctype_classes[c] & 0x20;
}
__attribute__((always_inline)) static inline int isxdigit(int c) {
return ctype_classes[c] & 0x80;
}
__attribute__((always_inline)) static inline int isascii(int c) {
return ((unsigned)c <= 0x7f);
}
__attribute__((always_inline)) static inline int isblank(int c) {
return (c == '\t' || c == ' ');
}
//---
// Character manipulation. // Character manipulation.
//--- #define tolower(c) ((c) | isupper(c))
#define toupper(c) ((c) & ~(islower(c) << 1))
__attribute__((always_inline)) static inline int tolower(int c) {
return c | isupper(c);
}
__attribute__((always_inline)) static inline int toupper(int c) {
return c & ~(islower(c) << 1);
}
#endif // _CTYPE_H #endif // _CTYPE_H

View file

@ -2,94 +2,107 @@
// //
// gint drawing module: display // gint drawing module: display
// //
// Handles vram manipulation and drawing for plain monochrome display. // This module does most of the monochrome drawing. It manages the video
// memory although image rendering and text rendering, as complex tasks,
// are left to other modules (bopti and tales, respectively).
// //
//--- //---
#ifndef _DISPLAY_H #ifndef _DISPLAY_H
#define _DISPLAY_H 1 #define _DISPLAY_H
#include <stdint.h> #include <stdint.h>
#include <stddef.h> #include <stddef.h>
//--- //---
// Heading declarations. // Drawing-related types and constants.
//--- //---
enum Color #define DWIDTH 128 /* Width of the screen */
{ #define DHEIGHT 64 /* Height of the screen */
Color_White = 0,
Color_Light = 1,
Color_Dark = 2,
Color_Black = 3,
Color_None = 4,
Color_Invert = 5,
};
// This header needs enum Color to be defined.
#include <tales.h>
/* /*
struct Image color_t
This structure holds information about a bitmap encoded with fxconv. Defines all colors that the library knows about:
Data is accessed using longword operations, which *requires* many - white is exactly what you think it is;
sizes to be multiples of 4 (structure alignment, data alignment, layer - light is a light gray used by the gray module;
size, ...). - dark is a dark gray, also used by the gray engine;
- black is nothing more than black; (sorry)
- none means transparent, but is shorter to write.
There are also some transformation-associated colors:
- invert reverses the intensity of the color (white -> black, dark ->
light, etc);
- lighten is some kind of partially-transparent white. It lightens the
color which it is drawn onto (black -> dark, light -> light);
- lighten2 is the same as lighten, except it lightens more (black ->
light, light -> white);
- darken is the exact opposite of lighten (light -> dark, black ->
black).
- darken2 is the same to darken as lighten2 to lighten (white -> dark,
dark -> black);
All transformations except invert only operate when the gray engine is
running.
*/ */
struct Image typedef enum
{ {
uint8_t magic; color_white = 0,
uint8_t format; color_light = 1,
color_dark = 2,
color_black = 3,
color_none = 4,
uint8_t width; color_invert = 5,
uint8_t height; color_lighten = 6,
color_lighten2 = 7,
color_darken = 8,
color_darken2 = 9,
const uint32_t data[]; } color_t;
} __attribute__((packed, aligned(4))); // The bopti module provides bitmap rendering functions.
// Useful shorthand for user code. #include <bopti.h>
typedef struct Image Image;
// The tales module provides text rendering functions but requires the color_t
// type definition.
// A few other constants. #include <tales.h>
#define DISPLAY_WIDTH 128
#define DISPLAY_HEIGHT 64
//--- //---
// Generic functions. // Video RAM management.
//--- //---
/* /*
display_getLocalVRAM() display_getLocalVRAM()
Returns the local video ram address. This function always return the Returns gint's local video RAM address. Gint does not use the system's
same address. buffer because it is misaligned. This function always returns the same
The buffer returned by this function should not be used directly when address. Both the display and the gray module heavily use this buffer;
running the gray engine. make sure you don't interfere with them if you access it.
This function does not necessarily returns the video ram that is
currently in use; call display_getCurrentVRAM() for this.
*/ */
void *display_getLocalVRAM(void); uint32_t *display_getLocalVRAM(void);
/* /*
display_getCurrentVRAM() display_getCurrentVRAM()
Returns the current video ram. This function usually returns the Returns the current monochrome video ram buffer. This function usually
parameter of the last call to display_useVRAM(), unless the gray engine returns the parameter of the last call to display_useVRAM(), or the
is running (in which case the result is undefined). Returns the local local vram address (which is default when the library starts).
vram address by default. The return value of this function is undefined if the gray engine is
running.
*/ */
void *display_getCurrentVRAM(void); uint32_t *display_getCurrentVRAM(void);
/* /*
display_useVRAM() display_useVRAM()
Changes the current video ram address. The argument MUST be a 4- Changes the current monochrome video ram address. The argument must be
aligned 1024-byte buffer; otherwise any drawing operation will crash a 4-aligned 1024-byte buffer because the library's design requires it.
the program. This function refuses misaligned buffers but trusts that enough space
is available; failing to provide enough memory may crash the program.
This function will most likely have no effect when running the gray This function will most likely have no effect when running the gray
engine. engine.
*/ */
void display_useVRAM(void *vram); void display_useVRAM(uint32_t *vram);
@ -99,30 +112,24 @@ void display_useVRAM(void *vram);
/* /*
dupdate() dupdate()
Displays the vram on the physical screen. Does nothing when the gray Pushes the video RAM to the physical screen. This function also works
engine is running. when the gray engine is running, but that's probably not what you want.
*/ */
void dupdate(void); void dupdate(void);
/* /*
dclear() dclear()
Clears the whole video ram. Clears the whole video ram, making all pixels white.
*/ */
void dclear(void); void dclear(void);
/* /*
dclear_area() drect()
Clears an area of the video ram. Both (x1, y1) and (x2, y2) are Draws a rectangle on the screen. This function can use any color which
cleared. is not associated with the gray engine, including the reverse operator.
Both end points (x1, y1) and (x2, y2) are affected as well.
*/ */
void dclear_area(int x1, int y1, int x2, int y2); void drect(int x1, int y1, int x2, int y2, color_t operator);
/*
dreverse_area()
Reverses an area of the vram. (x1, y1) and (x2, y2) are reversed as
well.
*/
void dreverse_area(int x1, int y1, int x2, int y2);
@ -132,9 +139,10 @@ void dreverse_area(int x1, int y1, int x2, int y2);
/* /*
dpixel() dpixel()
Puts a pixel in the vram. Changes a pixel's color in the video ram. The result may depend on the
current color of the pixel.
*/ */
void dpixel(int x, int y, enum Color color); void dpixel(size_t x, size_t y, color_t operator);
/* /*
dline() dline()
@ -143,22 +151,6 @@ void dpixel(int x, int y, enum Color color);
Uses an algorithm written by PierrotLL for MonochromeLib. Uses an algorithm written by PierrotLL for MonochromeLib.
*/ */
void dline(int x1, int y1, int x2, int y2, enum Color color); void dline(int x1, int y1, int x2, int y2, color_t operator);
/*
dimage()
Displays a monochrome image in the vram. Does a real lot of
optimization.
*/
void dimage(int x, int y, struct Image *image);
/*
dimage_part()
Draws a portion of an image, defined by its bounding rectangle.
Point (left, top) is included, but (left + width, top + height) is
excluded.
*/
void dimage_part(int x, int y, struct Image *img, int left, int top,
int width, int height);
#endif // _DISPLAY_H #endif // _DISPLAY_H

View file

@ -2,7 +2,17 @@
// //
// gint core module: events // gint core module: events
// //
// Finally some user-friendly API. // Finally some user-friendly API. This module is in charge of managing
// the event queue. The waitevent() function should be particularly useful
// in program main loops to record key presses and releases in real-time
// games.
//
// Other functions such as the getkey() of the keyboard module provide
// more advanced features such as SHIFT and ALPHA modifiers, backlight
// control for instance; these functions rely on this module and they
// ignore all events that they do not handle. If you want to catch several
// types of events (eg. keyboard and serial communication), then you need
// to use directly this module.
// //
//--- //---
@ -17,30 +27,24 @@
*/ */
typedef enum typedef enum
{ {
EventType_None = 0, // Specific events.
ET_None = EventType_None, event_none = 0,
event_user = 1,
EventType_User = 1, // Keyboard events.
ET_User = EventType_User, event_key_press = 2,
event_key_repeat = 3,
event_key_release = 4,
EventType_KeyPressed = 2, // Other events.
ET_KeyPress = EventType_KeyPressed, event_timer_underflow = 5,
EventType_KeyRepeated = 3,
ET_KeyRepeat = EventType_KeyRepeated,
EventType_KeyReleased = 4,
ET_KeyRel = EventType_KeyReleased,
EventType_TimerUnderflow = 5,
ET_Timer = EventType_TimerUnderflow,
} event_type_t; } event_type_t;
/* /*
event_t event_t
Wake up, something's going on. The union member that holds information Wake up, something's going on. The union member that holds information
about the event is implicitly defined by the type attribute. about the event is specified by the type attribute.
*/ */
typedef struct typedef struct
{ {
@ -48,11 +52,11 @@ typedef struct
union union
{ {
// For ET_User. // For event_user.
void *data; void *data;
// For ET_KeyPress, ET_KeyRepeat and ET_KeyRel. // For event_key_press, event_key_repeat and event_key_release.
int key; int key;
// For ET_Timer. // For event_timer_underflow.
timer_t *timer; timer_t *timer;
}; };

View file

@ -2,8 +2,10 @@
// //
// gint core module: interrupt handler // gint core module: interrupt handler
// //
// Central point of the library. Controls the interrupt handler and // This module is the core of the gint library. It controls the interrupt
// defines a few functions to configure callbacks for some interrupts. // handler, allows the user to customize interrupt management, provides
// peripheral register access and some information about the runtime
// environment.
// //
//--- //---

View file

@ -7,10 +7,14 @@
//--- //---
#ifndef _GRAY_H #ifndef _GRAY_H
#define _GRAY_H 1 #define _GRAY_H
#include <stdint.h>
#include <display.h> #include <display.h>
// This module provides bitmap rendering.
#include <bopti.h>
//--- //---
// Engine control. // Engine control.
//--- //---
@ -39,13 +43,13 @@ void gray_stop(void);
gray_lightVRAM() gray_lightVRAM()
Returns the module's light gray vram address. Returns the module's light gray vram address.
*/ */
void *gray_lightVRAM(void); uint32_t *gray_lightVRAM(void);
/* /*
gray_darkVRAM() gray_darkVRAM()
Returns the module's dark gray vram address. Returns the module's dark gray vram address.
*/ */
void *gray_darkVRAM(void); uint32_t *gray_darkVRAM(void);
/* /*
gray_getDelays() gray_getDelays()
@ -81,29 +85,26 @@ void gray_setDelays(int light, int dark);
/* /*
gupdate() gupdate()
Swaps the vram buffer sets. Swaps the vram buffer sets. You need to call this function each time
you finish drawing something in the video ram. Unlike the monochrome
function dupdate(), gupdate() only does a quick operation indicating
that drawing and exposed buffers have been swapped, but nothing on the
screen will change until the gray timer fires.
*/ */
void gupdate(void); void gupdate(void);
/* /*
gclear() gclear()
Clears the video ram. Clears the gray video ram, making all pixels white.
*/ */
void gclear(void); void gclear(void);
/* /*
gclear_area() grect()
Clears an area of the video ram. End points (x1, y1) and (x2, y2) are Draws a rectangle in the gray video ram; this function accepts all
included. values of the color_t type, including gray operators.
*/ */
void gclear_area(int x1, int y1, int x2, int y2); void grect(int x1, int y1, int x2, int y2, color_t operator);
/*
greverse_area()
Reverses an area of the vram. End points (x1, y1) and (x2, y2) are
included.
*/
void greverse_area(int x1, int y1, int x2, int y2);
@ -113,29 +114,18 @@ void greverse_area(int x1, int y1, int x2, int y2);
/* /*
gpixel() gpixel()
Puts a pixel in the vram. Puts a pixel in the vram. This function accepts all values of the
color_t type, including gray operators.
*/ */
void gpixel(int x, int y, enum Color color); void gpixel(size_t x, size_t y, color_t operator);
/* /*
gline() gline()
Draws a line in the vram. Automatically optimizes special cases. Draws a line in the vram while automatically optimizing special cases.
This function supports all plain colors from the color_t type, but not
the gray operators. If you need them for horizontal or vertical lines,
you may want to use grect() as a replacement.
*/ */
void gline(int x1, int y1, int x2, int y2, enum Color color); void gline(int x1, int y1, int x2, int y2, color_t operator);
/*
gimage()
Displays a gray image in the vram.
*/
void gimage(int x, int y, struct Image *image);
/*
gimage_part()
Draws a portion of a gray image, defined by its bounding rectangle.
Point (left, top) is included, but (left + width, top + height) is
excluded.
*/
void gimage_part(int x, int y, struct Image *image, int left, int top,
int width, int height);
#endif // _GRAY_H #endif // _GRAY_H

View file

@ -15,7 +15,7 @@
//--- //---
#ifndef _INTERNALS_BOPTI_H #ifndef _INTERNALS_BOPTI_H
#define _INTERNALS_BOPTI_H 1 #define _INTERNALS_BOPTI_H
#include <stdint.h> #include <stdint.h>
#include <display.h> #include <display.h>
@ -86,7 +86,7 @@ struct Command
// The video ram addresses are set by the public functions and used internally // The video ram addresses are set by the public functions and used internally
// by the module. // by the module.
// Monochrome video ram, light and dark buffers (in this order). // Monochrome video ram, light and dark buffers (in this order).
extern int *bopti_vram, *bopti_v1, *bopti_v2; extern uint32_t *bopti_vram, *bopti_v1, *bopti_v2;
@ -155,6 +155,6 @@ void bopti(const unsigned char *layer, struct Structure *s, struct Command *c);
getStructure() getStructure()
Determines the image size and data pointer. Determines the image size and data pointer.
*/ */
void getStructure(struct Image *img, struct Structure *structure); void getStructure(image_t *img, struct Structure *structure);
#endif // _INTERNALS_BOPTI_H #endif // _INTERNALS_BOPTI_H

View file

@ -1,47 +1,40 @@
//---
//
// gint drawing module: display
//
// Handles vram manipulation and drawing.
//
//---
#ifndef _INTERNALS_DISPLAY_H #ifndef _INTERNALS_DISPLAY_H
#define _INTERNALS_DISPLAY_H 1 #define _INTERNALS_DISPLAY_H
#include <stdint.h> #include <stdint.h>
#include <stddef.h>
extern int *vram; extern uint32_t *vram;
//--- //---
// Rectangle masks. // Rectangle masks.
// //
// The concept of 'rectangle masks' is used several times in this module. // The concept of 'rectangle masks' is used several times in this module.
// It is based on the fact that an operation that affects a rectangle acts // It relies on the fact that operations affecting a rectangle act the
// the same on all its lines. Therefore the behavior of the operation is // same for all lines, and line operation is very optimized. A rectangle
// determined by its behavior on a single line, which is represented using // mask is a set of integers, where each bit indicate whether a specific
// 'masks' whose bits indicate whether a pixel is affected (1) or not (0). // pixel is affected (1) by the operation, or not (0).
// //
// For example when clearing the screen rectangle (16, 16, 112, 48), the // For example to clear a rectangle such as (14, 16, 112, 48), the masks
// masks will represent information '16 to 112 on x-axis', and will hold // will need to hold 0003ffff ffffffff ffffffff ffff0000. Bitwise-
// the following values : 0000ffff, ffffffff, ffffffff and ffff0000. These // combining them with video ram long entries yields very good performance
// masks can then be used by setting vram[offset] &= ~masks[i]. This // as compared to operation on single pixels. Each bitwise operation will
// appears to be very flexible : for instance, vram[offset] ^= masks[i] // produce different results, which is very flexible.
// will reverse the pixels in the same rectangle.
//
// This technique can also be used in more subtle cases with more complex
// patterns, but within this module it is unlikely to happen.
// //
// This technique can also be used in subtle cases with patterns more
// complicated than rectangles, but within this module this is unlikely to
// happen.
//--- //---
/* /*
adjustRectangle() adjustRectangle()
Adjusts the given rectangle coordinates to ensure that : Adjusts the given rectangle coordinates to ensure that :
- the rectangle is entirely contained in the screen - the rectangle is entirely contained in the screen;
- x1 < x2 - x1 < x2;
- y1 < y2 - y1 < y2,
which is needed when working with screen rectangles. which is needed when working with screen rectangles. Returns non-zero
Returns non-zero if the rectangle is outside the screen. if the rectangle is outside the screen, which usually means there is
nothing to do.
*/ */
int adjustRectangle(int *x1, int *y1, int *x2, int *y2); int adjustRectangle(int *x1, int *y1, int *x2, int *y2);
@ -51,6 +44,6 @@ int adjustRectangle(int *x1, int *y1, int *x2, int *y2);
and x2 (both included). The four masks are stored in the third argument and x2 (both included). The four masks are stored in the third argument
(seen as an array). (seen as an array).
*/ */
void getMasks(int x1, int x2, uint32_t *masks); void getMasks(size_t x1, size_t x2, uint32_t *masks);
#endif // _INTERNALS_DISPLAY_H #endif // _INTERNALS_DISPLAY_H

View file

@ -1,11 +1,11 @@
#ifndef _INTERNALS_GINT_H #ifndef _INTERNALS_GINT_H
#define _INTERNALS_GINT_H 1 #define _INTERNALS_GINT_H
#include <stdint.h> #include <stdint.h>
#include <gint.h> #include <gint.h>
//--- //---
// Interrupt handlers // Interrupt handlers.
//--- //---
// General exception handler. // General exception handler.
@ -18,7 +18,7 @@ void gint_int(void);
//--- //---
// Assembler-level VBR management // Assembler-level VBR management.
//--- //---
/* /*
@ -37,7 +37,7 @@ void gint_setvbr(uint32_t vbr, void (*setup)(void));
//--- //---
// Initialization and termination routines // Initialization and termination routines.
//--- //---
/* /*

View file

@ -5,7 +5,7 @@
#include <internals/gint.h> #include <internals/gint.h>
//--- //---
// Interrupt handlers // Interrupt handlers.
//--- //---
/* /*
@ -60,7 +60,7 @@ extern gint_interrupt_handler_t gint_handlers[];
//--- //---
// Interrupt maps // Interrupt maps.
//--- //---
/* /*

View file

@ -2,6 +2,7 @@
#define _INTERNALS_KEYBOARD_H #define _INTERNALS_KEYBOARD_H
#include <keyboard.h> #include <keyboard.h>
#include <timer.h>
#include <clock.h> #include <clock.h>
// Keyboard variables. // Keyboard variables.
@ -10,10 +11,10 @@ extern volatile int interrupt_flag;
// Key statistics. // Key statistics.
extern int repeat_first, repeat_next; extern int repeat_first, repeat_next;
extern int last_key, last_repeats, last_events; extern int last_key, last_repeats, last_time;
// RTC callback id. // Virtual timer object.
extern unsigned cb_id; extern timer_t *vtimer;
/* /*
getPressedKey() getPressedKey()

View file

@ -8,8 +8,8 @@
// //
//--- //---
#ifndef _MMU_H #ifndef _INTERNALS_MMU_H
#define _MMU_H 1 #define _INTERNALS_MMU_H
/* /*
mmu_pseudoTLBInit() mmu_pseudoTLBInit()

View file

@ -8,7 +8,7 @@
//--- //---
#ifndef _INTERNALS_STDIO_H #ifndef _INTERNALS_STDIO_H
#define _INTERNALS_STDIO_H 1 #define _INTERNALS_STDIO_H
#include <stddef.h> #include <stddef.h>
#include <stdarg.h> #include <stdarg.h>

View file

@ -1,5 +1,5 @@
#ifndef _INTERNALS_TALES_H #ifndef _INTERNALS_TALES_H
#define _INTERNALS_TALES_H 1 #define _INTERNALS_TALES_H
#include <tales.h> #include <tales.h>
#include <stdint.h> #include <stdint.h>
@ -7,13 +7,14 @@
#define OPERATE_ARGS uint32_t *operators, int height, int x, int y #define OPERATE_ARGS uint32_t *operators, int height, int x, int y
extern struct Font *font; extern struct Font *font;
extern enum Color color; extern color_t operator;
/* /*
tales_init() tales_init()
Configures tales with the default font (which is part of gint). Configures tales with the default font (which is part of gint).
*/ */
void tales_init(void) __attribute__((constructor)); __attribute__((constructor))
void tales_init(void);
/* /*
getCharacterIndex() getCharacterIndex()

View file

@ -1,5 +1,5 @@
#ifndef _INTERNALS_TIME_H #ifndef _INTERNALS_TIME_H
#define _INTERNALS_TIME_H 1 #define _INTERNALS_TIME_H
/* /*
isLeap() isLeap()

View file

@ -11,7 +11,7 @@
//--- //---
#ifndef _KEYBOARD_H #ifndef _KEYBOARD_H
#define _KEYBOARD_H 1 #define _KEYBOARD_H
#include <stdint.h> #include <stdint.h>
#include <rtc.h> #include <rtc.h>
@ -20,92 +20,87 @@
// Keycodes and related. // Keycodes and related.
//--- //---
// The following codes are gint matrix codes. They are not compatible with the /*
// system's. key_t
The following codes are gint matrix codes. They are not compatible with
#define KEY_F1 0x69 the system's. Some keycodes are special event codes; all others are
#define KEY_F2 0x59 made of a key identifier and possibly one or more modifiers.
#define KEY_F3 0x49 Binary-and a keycode with MOD_CLEAR to remove the modifiers; this will
#define KEY_F4 0x39 not work with special event codes.
#define KEY_F4 0x39 */
#define KEY_F5 0x29 typedef enum
#define KEY_F6 0x19 {
// Special events codes.
#define KEY_SHIFT 0x68 KEY_NONE = 0x00,
#define KEY_OPTN 0x58 KEY_NOEVENT = 0xff,
#define KEY_VARS 0x48
#define KEY_MENU 0x38
#define KEY_LEFT 0x28
#define KEY_UP 0x18
#define KEY_ALPHA 0x67
#define KEY_SQUARE 0x57
#define KEY_POWER 0x47
#define KEY_EXIT 0x37
#define KEY_DOWN 0x27
#define KEY_RIGHT 0x17
#define KEY_XOT 0x66
#define KEY_LOG 0x56
#define KEY_LN 0x46
#define KEY_SIN 0x36
#define KEY_COS 0x26
#define KEY_TAN 0x16
#define KEY_FRAC 0x65
#define KEY_FD 0x55
#define KEY_LEFTP 0x45
#define KEY_RIGHTP 0x35
#define KEY_COMMA 0x25
#define KEY_ARROW 0x15
#define KEY_7 0x64
#define KEY_8 0x54
#define KEY_9 0x44
#define KEY_DEL 0x34
#define KEY_AC_ON 0x24
#define KEY_4 0x63
#define KEY_5 0x53
#define KEY_6 0x43
#define KEY_MUL 0x33
#define KEY_DIV 0x23
#define KEY_1 0x62
#define KEY_2 0x52
#define KEY_3 0x42
#define KEY_PLUS 0x32
#define KEY_MINUS 0x22
#define KEY_0 0x61
#define KEY_DOT 0x51
#define KEY_EXP 0x41
#define KEY_NEG 0x31
#define KEY_EXE 0x21
// Key modifiers. // Key modifiers.
#define MOD_SHIFT 0x80 MOD_SHIFT = 0x80,
#define MOD_ALPHA 0x100 MOD_ALPHA = 0x100,
#define MOD_CLEAR ~(MOD_SHIFT | MOD_ALPHA) MOD_CLEAR = ~(MOD_SHIFT | MOD_ALPHA),
// Key events. // Key identifiers.
#define KEY_NONE 0x00
#define KEY_NOEVENT 0xff
/* KEY_F1 = 0x69,
enum KeyboardFrequency KEY_F2 = 0x59,
Possible values for the keyboard frequency. KEY_F3 = 0x49,
*/ KEY_F4 = 0x39,
enum KeyboardFrequency KEY_F5 = 0x29,
{ KEY_F6 = 0x19,
KeyboardFreq_500mHz = RTCFreq_500mHz,
KeyboardFreq_1Hz = RTCFreq_1Hz, KEY_SHIFT = 0x68,
KeyboardFreq_2Hz = RTCFreq_2Hz, KEY_OPTN = 0x58,
KeyboardFreq_4Hz = RTCFreq_4Hz, KEY_VARS = 0x48,
KeyboardFreq_16Hz = RTCFreq_16Hz, KEY_MENU = 0x38,
KeyboardFreq_64Hz = RTCFreq_64Hz, KEY_LEFT = 0x28,
KeyboardFreq_256Hz = RTCFreq_256Hz, KEY_UP = 0x18,
};
KEY_ALPHA = 0x67,
KEY_SQUARE = 0x57,
KEY_POWER = 0x47,
KEY_EXIT = 0x37,
KEY_DOWN = 0x27,
KEY_RIGHT = 0x17,
KEY_XOT = 0x66,
KEY_LOG = 0x56,
KEY_LN = 0x46,
KEY_SIN = 0x36,
KEY_COS = 0x26,
KEY_TAN = 0x16,
KEY_FRAC = 0x65,
KEY_FD = 0x55,
KEY_LEFTP = 0x45,
KEY_RIGHTP = 0x35,
KEY_COMMA = 0x25,
KEY_ARROW = 0x15,
KEY_7 = 0x64,
KEY_8 = 0x54,
KEY_9 = 0x44,
KEY_DEL = 0x34,
KEY_AC_ON = 0x24,
KEY_4 = 0x63,
KEY_5 = 0x53,
KEY_6 = 0x43,
KEY_MUL = 0x33,
KEY_DIV = 0x23,
KEY_1 = 0x62,
KEY_2 = 0x52,
KEY_3 = 0x42,
KEY_PLUS = 0x32,
KEY_MINUS = 0x22,
KEY_0 = 0x61,
KEY_DOT = 0x51,
KEY_EXP = 0x41,
KEY_NEG = 0x31,
KEY_EXE = 0x21,
} key_t;
@ -114,23 +109,31 @@ enum KeyboardFrequency
//--- //---
/* /*
keyboard_setFrequency() keyboard_setAnalysisDelay()
Sets the keyboard frequency. The default frequency is 16 Hz. Very few Sets the keyboard analysis delay, that is, the delay (in ms) between
applications will need to change this setting. two keyboard analyzes. If a key is pressed then released in the lapse
At low frequencies, you will miss key hits. At high frequencies, you between two analyzes, the program won't notice anything. On the other
may lose execution power. hand, if the program spends too much time reading the keyboard, it will
lose a bit of execution power.
The default frequency is about 40 Hz; very few programs will need to
change this setting. Please note that the repeat delays should be
multiples of the analysis delay for better accuracy.
*/ */
void keyboard_setFrequency(enum KeyboardFrequency frequency); void keyboard_setAnalysisDelay(int analysis_delay_ms);
/* /*
keyboard_setRepeatRate() keyboard_setRepeatRate()
Sets the default repeat rate for key events. The delay before the first Sets the default repeat rate for key events. The delay before the first
repeat may have a different value (usually longer). The unit for the repeat may have a different value (usually longer). The unit for the
argument is the keyboard period. For example at 32 Hz, values of argument is ms, but the repeat events themselves may only be fired when
(20, 4) will imitate the system default. a keyboard analysis is performed; which means that for better accuracy,
Set to 0 to disable repetition. If first = 0, no repetition will be these delays should be a multiple of the keyboard period. The keyboard
allowed. If first != 0 and next = 0, only one repetition will be period may be changed by calling keyboard_setAnalysisDelay().
allowed. For instance, delays of (625 ms, 125 ms) will imitate the system's
default setting.
You can disable repetitions by passing 0 as arguments:
- if first = 0, no repetition will ever occur;
- if first != 0 and next = 0, only one repetition will occur.
*/ */
void keyboard_setRepeatRate(int first, int next); void keyboard_setRepeatRate(int first, int next);
@ -141,57 +144,74 @@ void keyboard_setRepeatRate(int first, int next);
//--- //---
/* /*
enum GetKeyOpt getkey_opt_t
Options available for use with getkey_opt(). Options available to customize the behavior of the getkey_opt()
function.
*/ */
enum GetkeyOpt typedef enum
{ {
Getkey_NoOption = 0x00, getkey_none = 0x00,
// Consider [SHIFT] and [ALPHA] as modifiers instead of returning // Consider [SHIFT] and [ALPHA] as modifiers. Returns key identifiers
// KEY_SHIFT and KEY_ALPHA. // with MOD_SHIFT and MOD_ALPHA flags instead of returning KEY_SHIFT
Getkey_ShiftModifier = 0x01, // and KEY_ALPHA.
Getkey_AlphaModifier = 0x02, getkey_shift_modifier = 0x01,
getkey_alpha_modifier = 0x02,
// Allow changing the backlight status on [SHIFT] + [OPTN]. // Allow changing the backlight status on [SHIFT] + [OPTN] on
Getkey_ManageBacklight = 0x04, // compatible models.
getkey_manage_backlight = 0x04,
// Key repetition. Notice that modifiers will never be repeated. // Allow key repetition. This option does not control the generation of
Getkey_RepeatArrowKeys = 0x10, // repeat events (use keyboard_setRepeatRate() for this) but filters
Getkey_RepeatCharKeys = 0x20, // them. Please note that modifiers will never be repeated, even when
Getkey_RepeatCtrlKeys = 0x40, // pressed continuously.
Getkey_RepeatFuncKeys = 0x80, getkey_repeat_arrow_keys = 0x10,
getkey_repeat_char_keys = 0x20,
getkey_repeat_ctrl_keys = 0x40,
getkey_repeat_func_keys = 0x80,
// Shorthand for the four previous properties. // Shorthand for the four previous properties.
Getkey_RepeatAllKeys = 0xf0, getkey_repeat_all_keys = 0xf0,
};
} getkey_option_t;
/* /*
getkey() getkey()
Blocking function with auto-repeat and SHIFT modifying functionalities. Blocking function with auto-repeat that heeds for the SHIFT and ALPHA
Reproduces the behavior of the system's GetKey(). Returns the matrix modifiers. In short, this function reproduces the behavior of the
code with a possible MOD_SHIFT bit. system's GetKey() function. It returns a matrix code, possibly with
modifier bits.
This function does not return until a key is pressed.
*/ */
int getkey(void); int getkey(void);
/* /*
getkey_opt() getkey_opt()
Enhances getkey() with most general functionalities. An OR-combination Enhances getkey() with more general functionalities. An OR-combination
of options may be given as first argument. of options of type getkey_option_t may be given as first argument.
If max_cycles is non-zero and positive, getkey_opt() will return If delay is non-zero and positive, getkey_opt() will return KEY_NOEVENT
KEY_NOEVENT if no event occurs during max_cycle analyzes. if no event occurs during the given delay. Please note that this
function can only ever return after a keyboard analysis is performed;
the actual delay may exceed the requested time if it's not a multiple
of the keyboard period (which can be changed by calling
keyboard_setAnalysisDelay()).
Like getkey(), returns the pressed key matrix code, possibly with Like getkey(), returns the pressed key matrix code, possibly with
modifiers depending on the options. modifiers depending on the options.
*/ */
int getkey_opt(enum GetkeyOpt options, int max_cycles); int getkey_opt(getkey_option_t options, int delay_ms);
/* /*
multigetkey() multigetkey()
Listens the keyboard for simultaneous key hits. This functions fills Listens the keyboard for simultaneous key hits. This functions fills
array `keys` with `count` keycodes, adding KEY_NONE at the end if the 'keys' array with 'count' keycodes, padding with KEY_NONE values at
less than `count` keys are pressed. the end if less that 'count' keys are detected.
If `max_cycles` is non-zero and nothing happens after `max_cycles` If 'delay_ms' is positive and nothing happens during this delay, this
cycles, this function returns an array of KEY_NONE. function returns an array of KEY_NONE. Please note that the delay
detection suffers the same limitation as getkey_opt().
This function suffers from severe limitations and may not be very
convenient to use. For more accuracy, consider using the event system.
WARNING: WARNING:
Because of hardware limitations, this function generally yields poor Because of hardware limitations, this function generally yields poor
@ -202,7 +222,7 @@ int getkey_opt(enum GetkeyOpt options, int max_cycles);
The results are guaranteed to be exact if two keys or less are pressed. The results are guaranteed to be exact if two keys or less are pressed.
With three keys or more, column effects (on SH4) and rectangle effects With three keys or more, column effects (on SH4) and rectangle effects
(on both platforms) mess up the results by making this function think (on both platforms) mess up the results by making this function think
that some keys, which are actually unpressed, are pressed. that some keys, which are actually released, are pressed.
This function is designed to make combinations of one or two arrow keys This function is designed to make combinations of one or two arrow keys
with another key as viable as possible. On SH4, this works pretty well with another key as viable as possible. On SH4, this works pretty well
@ -215,25 +235,26 @@ int getkey_opt(enum GetkeyOpt options, int max_cycles);
incorrect results. Please do not expect multigetkey() to work as an incorrect results. Please do not expect multigetkey() to work as an
ideal multi-key analyzer. ideal multi-key analyzer.
*/ */
void multigetkey(int *keys, int count, int max_cycles); void multigetkey(int *keys, int count, int delay_ms);
/* /*
keylast() keyboard_stateBuffer()
Returns the matrix code of the last pressed key. If repeat_count is
non-NULL, it is set to the number of repetitions.
*/
int keylast(int *repeat_count);
/*
keystate()
Returns the address of the keyboard state array. The keyboard state Returns the address of the keyboard state array. The keyboard state
consists in 10 bytes, in which every key is represented as a bit. consists in 10 bytes, in which every key is represented as a bit.
The returned address is the original buffer address. You should avoid The returned address is the original buffer address. You should avoid
editing the array. It wouldn't influence the behavior of the keyboard editing the array. It wouldn't influence the behavior of the keyboard
functions, but the buffer data is very volatile. Therefore, data functions, but the buffer data is very volatile and any data written to
written to the buffer could be replaced anytime. it could be replaced anytime without prior notice.
If the user wishes to do really advanced keyboard management that they
can't achieve it using the library, they can access this buffer.
Updates of this buffer's contents can be detected by watching the
'interrupt_flag' variable defined in internals/keyboard.h. However, the
library will continue firing events so the user needs to catch them and
ignore them.
*/ */
volatile uint8_t *keystate(void); volatile uint8_t *keyboard_stateBuffer(void);
@ -241,32 +262,61 @@ volatile uint8_t *keystate(void);
// Key analysis. // Key analysis.
//--- //---
enum KeyType
{
KeyType_Arrow = 1,
KeyType_Character = 2,
KeyType_Control = 4,
KeyType_Function = 8,
};
/* /*
keyid() keyid()
Returns a non-matrix key code that can be used for array subscript. Transforms a key identifier and returns a key code that is more
Ignores modifiers. convenient for array subscript that the original matrix codes. The new
codes are laid out the following way:
+0 +1 +2 +3 +4 +5
------------------------------------
+0 | F1 F2 F3 F4 F5 F6
+6 | SHIFT OPTN VARS MENU Left Top
+12 | ALPHA x^2 ^ EXIT Down Right
+18 | X,O,T log ln sin cos tan
+24 | Frac F<>D ( ) , ->
+30 | 7 8 9 DEL AC/ON
+36 | 4 5 6 * /
+42 | 1 2 3 + -
+48 | 0 . x10^ (-) EXE
The returned key code is the sum of the line and column headings. For
instance key_id(KEY_SIN) would be 18 + 3 = 21. Please note that there
are a few holes in the numbering.
This function ignores modifiers and returns -1 on error.
*/ */
int keyid(int key); int key_id(int matrix_key);
/* /*
keychar() key_char()
Returns the ASCII character associated with a character key; 0 for Returns the ASCII character associated with a character key, and 0 for
other keys. other keys. This function expects a matrix code and not a key_id()
code, and heeds for the ALPHA modifier.
*/ */
int keychar(int key); int key_char(int matrix_key);
/* /*
keytype() key_type_t
Returns a key's type. Ignores modifiers. Categorizes the keyboard's keys into several types:
- Arrow keys only include the REPLAY pad;
- Function keys only include the F1 .. F6 keys;
- Character keys are those which input characters;
- Control characters are all others.
*/ */
enum KeyType keytype(int key); typedef enum
{
key_type_arrow = 1,
key_type_character = 2,
key_type_control = 4,
key_type_function = 8,
} key_type_t;
/*
key_type()
Returns a key's type. This functions ignores modifiers and expects
matrix codes as argument, not key_id() codes.
*/
key_type_t key_type(int matrix_key);
#endif // _KEYBOARD_H #endif // _KEYBOARD_H

View file

@ -27,7 +27,7 @@
//--- //---
#ifndef _MPU_H #ifndef _MPU_H
#define _MPU_H 1 #define _MPU_H
/* /*
mpu_t mpu_t

View file

@ -7,7 +7,7 @@
//--- //---
#ifndef _RTC_H #ifndef _RTC_H
#define _RTC_H 1 #define _RTC_H
#include <stdint.h> #include <stdint.h>

View file

@ -12,7 +12,7 @@
//--- //---
#ifndef _SCREEN_H #ifndef _SCREEN_H
#define _SCREEN_H 1 #define _SCREEN_H
/* /*
screen_display() screen_display()

View file

@ -8,7 +8,7 @@
//--- //---
#ifndef _SETJMP_H #ifndef _SETJMP_H
#define _SETJMP_H 1 #define _SETJMP_H
// There are 16 CPU registers that *must* be saved to ensure a basically // There are 16 CPU registers that *must* be saved to ensure a basically
// safe jump. // safe jump.

View file

@ -8,7 +8,7 @@
//--- //---
#ifndef _STDIO_H #ifndef _STDIO_H
#define _STDIO_H 1 #define _STDIO_H
#include <stddef.h> #include <stddef.h>
#include <stdarg.h> #include <stdarg.h>

View file

@ -8,7 +8,7 @@
//--- //---
#ifndef _STDLIB_H #ifndef _STDLIB_H
#define _STDLIB_H 1 #define _STDLIB_H
//--- //---
// Common definitions. // Common definitions.

View file

@ -8,7 +8,7 @@
//--- //---
#ifndef _STRING_H #ifndef _STRING_H
#define _STRING_H 1 #define _STRING_H
#include <stddef.h> #include <stddef.h>

View file

@ -8,7 +8,7 @@
//--- //---
#ifndef _TALES_H #ifndef _TALES_H
#define _TALES_H 1 #define _TALES_H
#include <display.h> #include <display.h>
#include <stdint.h> #include <stdint.h>
@ -95,7 +95,7 @@ typedef struct Font Font;
Sets the font and color to use for subsequent text operations. Pass Sets the font and color to use for subsequent text operations. Pass
font = NULL to use the default font. font = NULL to use the default font.
*/ */
void text_configure(struct Font *font, enum Color color); void text_configure(struct Font *font, color_t operator);
/* /*
dtext() dtext()

View file

@ -7,7 +7,7 @@
//--- //---
#ifndef _TIME_H #ifndef _TIME_H
#define _TIME_H 1 #define _TIME_H
#include <stddef.h> #include <stddef.h>

View file

@ -8,7 +8,7 @@
//--- //---
#ifndef _TIMER_H #ifndef _TIMER_H
#define _TIMER_H 1 #define _TIMER_H
#include <clock.h> #include <clock.h>
#include <stdint.h> #include <stdint.h>
@ -49,6 +49,16 @@ typedef struct timer_t timer_t;
*/ */
timer_t *timer_create(int delay_ms, int repeats); timer_t *timer_create(int delay_ms, int repeats);
/*
timer_reload()
Changes a virtual timer's delay. The timer is not stopped nor started:
it keeps running or waiting. Events that were waiting to be handled are
dropped and the number of repeats left is not changed. The timer
restarts counting from 0 regardless of how much time had elapsed since
it last fired.
*/
void timer_reload(timer_t *timer, int new_ms_delay);
/* /*
timer_destroy() timer_destroy()
Destroys a virtual timer. This virtual timer pointer becomes invalid Destroys a virtual timer. This virtual timer pointer becomes invalid

View file

@ -1,7 +1,7 @@
#include <internals/bopti.h> #include <internals/bopti.h>
// Monochrome video ram, light and dark buffers (in this order). // Monochrome video ram, light and dark buffers (in this order).
int *bopti_vram, *bopti_v1, *bopti_v2; uint32_t *bopti_vram, *bopti_v1, *bopti_v2;
/* /*
bopti_op() bopti_op()
@ -285,7 +285,7 @@ void bopti(const unsigned char *layer, struct Structure *s, struct Command *c)
getStructure() getStructure()
Determines the image size and data pointer. Determines the image size and data pointer.
*/ */
void getStructure(struct Image *img, struct Structure *s) void getStructure(image_t *img, struct Structure *s)
{ {
int column_count, end, end_bytes, layer; int column_count, end, end_bytes, layer;

View file

@ -7,8 +7,8 @@
Point (left, top) is included, but (left + width, top + height) is Point (left, top) is included, but (left + width, top + height) is
excluded. excluded.
*/ */
void dimage_part(int x, int y, struct Image *img, int left, int top, void dimage_part(int x, int y, image_t *img, int left, int top, int width,
int width, int height) int height)
{ {
if(!img || img->magic != 0x01) return; if(!img || img->magic != 0x01) return;
@ -70,7 +70,7 @@ void dimage_part(int x, int y, struct Image *img, int left, int top,
dimage() dimage()
Displays a monochrome image in the video ram. Displays a monochrome image in the video ram.
*/ */
void dimage(int x, int y, struct Image *img) void dimage(int x, int y, image_t *img)
{ {
dimage_part(x, y, img, 0, 0, -1, -1); dimage_part(x, y, img, 0, 0, -1, -1);
} }

View file

@ -8,8 +8,8 @@
Point (left, top) is included, but (left + width, top + height) is Point (left, top) is included, but (left + width, top + height) is
excluded. excluded.
*/ */
void gimage_part(int x, int y, struct Image *img, int left, int top, void gimage_part(int x, int y, image_t *img, int left, int top, int width,
int width, int height) int height)
{ {
if(!img || img->magic != 0x01) return; if(!img || img->magic != 0x01) return;
@ -71,7 +71,7 @@ void gimage_part(int x, int y, struct Image *img, int left, int top,
gimage() gimage()
Displays a gray image in the video ram. Displays a gray image in the video ram.
*/ */
void gimage(int x, int y, struct Image *img) void gimage(int x, int y, image_t *img)
{ {
gimage_part(x, y, img, 0, 0, -1, -1); gimage_part(x, y, img, 0, 0, -1, -1);
} }

View file

@ -19,7 +19,7 @@ static clock_config_t conf = {
Several units can be used. Be aware that the result is approximate, and Several units can be used. Be aware that the result is approximate, and
very high frequencies or very short delays will yield important errors. very high frequencies or very short delays will yield important errors.
*/ */
uint32_t clock_setting(int duration, enum ClockUnit unit) uint32_t clock_setting(int duration, clock_unit_t unit)
{ {
if(conf.Pphi_f <= 0) return 0xffffffff; if(conf.Pphi_f <= 0) return 0xffffffff;
uint64_t f = conf.Pphi_f >> 2; uint64_t f = conf.Pphi_f >> 2;
@ -27,23 +27,23 @@ uint32_t clock_setting(int duration, enum ClockUnit unit)
switch(unit) switch(unit)
{ {
case Clock_us: case clock_us:
result = (duration * f) / 1000000; result = (duration * f) / 1000000;
break; break;
case Clock_ms: case clock_ms:
result = (duration * f) / 1000; result = (duration * f) / 1000;
break; break;
case Clock_s: case clock_s:
result = (duration * f); result = (duration * f);
break; break;
case Clock_Hz: case clock_Hz:
result = f / duration; result = f / duration;
break; break;
case Clock_kHz: case clock_kHz:
result = f / (duration * 1000); result = f / (duration * 1000);
break; break;
case Clock_MHz: case clock_MHz:
result = f / (duration * 1000000); result = f / (duration * 1000000);
break; break;
@ -103,7 +103,7 @@ void sleep_ms(int ms_delay)
void sleep_us(int us_delay) void sleep_us(int us_delay)
{ {
volatile int sleep_done = 0; volatile int sleep_done = 0;
const uint32_t constant = clock_setting(us_delay, Clock_us); const uint32_t constant = clock_setting(us_delay, clock_us);
timer_t *timer = htimer_setup(timer_user, constant, timer_Po_4, 1); timer_t *timer = htimer_setup(timer_user, constant, timer_Po_4, 1);
timer_attach(timer, sleep_callback, (void *)&sleep_done); timer_attach(timer, sleep_callback, (void *)&sleep_done);

View file

@ -37,7 +37,7 @@ static void show_error(const char *name, uint32_t *access_mode, uint32_t *tea,
__asm__("stc ssr, %0" : "=rm"(ssr)); __asm__("stc ssr, %0" : "=rm"(ssr));
dclear(); dclear();
text_configure(NULL, Color_Black); text_configure(NULL, color_black);
print(3, 1, "EXCEPTION RAISED!"); print(3, 1, "EXCEPTION RAISED!");
for(int i = 0; i < 36; i++) vram[i] = ~vram[i]; for(int i = 0; i < 36; i++) vram[i] = ~vram[i];

43
src/ctype/ctype_classes.c Normal file
View file

@ -0,0 +1,43 @@
#include <ctype.h>
// Let's save up some space and readability (That's Cake's idea, its a bit of a
// preprocessor trick but a rather nice trick).
#define r4(x) (x), (x), (x), (x)
#define r5(x) r4(x), (x)
#define r6(x) r5(x), (x)
#define r7(x) r6(x), (x)
#define r9(x) r7(x), (x), (x)
#define r10(x) r6(x), r4(x)
#define r15(x) r10(x), r4(x), (x)
#define r18(x) r9(x), r9(x)
#define r20(x) r10(x), r10(x)
enum {
cntrl = 0x01,
space = 0x02,
punct = 0x04,
print = 0x08,
upper = 0x20,
lower = 0x10,
digit = 0x40,
xdigt = 0x80,
};
uint8_t ctype_classes[0x80] = {
// Control characters.
r9(cntrl), r5(cntrl | space), r18(cntrl),
// Space and some punctuation.
space | print, r15(punct | print),
// Decimal digits.
r10(digit | xdigt | print),
// Some punctuation.
r7(punct | print),
// Uppercase alphabet.
r6(upper | xdigt | print), r20(upper | print),
// Other punctuation symbols.
r6(punct | print),
// Lowercase alphabet.
r6(lower | xdigt | print), r20(lower | print),
// Last punctuation characters and DEL.
r4(punct | print), cntrl,
};

View file

@ -0,0 +1,71 @@
//---
// Character type functions.
// Normally this functions need not be linked because there are macros to
// optimize performance, but we still need them to get some pointers.
//---
// We don't want to include <ctype.h> because it defines all the macros...
#include <stdint.h>
extern uint8_t ctype_classes[0x80];
#define _inline __attribute__((always_inline)) inline
_inline int isalnum(int c) {
return ctype_classes[c] & 0xf0;
}
_inline int isalpha(int c) {
return ctype_classes[c] & 0x30;
}
_inline int iscntrl(int c) {
return ctype_classes[c] & 0x01;
}
_inline int isdigit(int c) {
return ctype_classes[c] & 0x40;
}
_inline int isgraph(int c) {
return ctype_classes[c] & 0xf4;
}
_inline int islower(int c) {
return ctype_classes[c] & 0x10;
}
_inline int isprint(int c) {
return ctype_classes[c] & 0x08;
}
_inline int ispunct(int c) {
return ctype_classes[c] & 0x04;
}
_inline int isspace(int c) {
return ctype_classes[c] & 0x02;
}
_inline int isupper(int c) {
return ctype_classes[c] & 0x20;
}
_inline int isxdigit(int c) {
return ctype_classes[c] & 0x80;
}
_inline int isascii(int c) {
return ((unsigned)c <= 0x7f);
}
_inline int isblank(int c) {
return (c == '\t' || c == ' ');
}
_inline int tolower(int c) {
return c | isupper(c);
}
_inline int toupper(int c) {
return c & ~(islower(c) << 1);
}

View file

@ -3,10 +3,30 @@
/* /*
dclear() dclear()
Clears the whole vram. Clears the whole vram, making all pixels white.
*/ */
void dclear(void) void dclear(void)
{ {
int i; // I tend to use pre-decrement more than post-increment.
for(i = 0; i < 256; i++) vram[i] = 0; uint32_t *index = vram + 256;
while(index > vram)
{
*--index = 0;
*--index = 0;
*--index = 0;
*--index = 0;
*--index = 0;
*--index = 0;
*--index = 0;
*--index = 0;
*--index = 0;
*--index = 0;
*--index = 0;
*--index = 0;
*--index = 0;
*--index = 0;
*--index = 0;
*--index = 0;
}
} }

View file

@ -1,21 +0,0 @@
#include <internals/display.h>
#include <display.h>
/*
dclear_area()
Clears an area of the vram using rectangle masks. Both (x1, y1) and
(x2, y2) are cleared.
*/
void dclear_area(int x1, int y1, int x2, int y2)
{
uint32_t masks[4];
if(adjustRectangle(&x1, &y1, &x2, &y2)) return;
getMasks(x1, x2, masks);
int begin = y1 << 2;
int end = (y2 + 1) << 2;
int i;
for(i = 0; i < 4; i++) masks[i] = ~masks[i];
for(i = begin; i < end; i++) vram[i] &= masks[i & 3];
}

View file

@ -1,43 +1,48 @@
#include <display.h> #include <display.h>
// Program video ram. It resides in .bss section, therefore it is cleared at // Program video ram. It resides in BSS section, therefore it is cleared at
// program initialization and stripped from the executable file. // program initialization and stripped from the executable file.
static int local_vram[256]; static uint32_t local_vram[256];
int *vram = local_vram; uint32_t *vram = local_vram;
/* /*
display_getLocalVRAM() display_getLocalVRAM()
Returns the local video ram address. This function always return the Returns gint's local video RAM address. Gint does not use the system's
same address. buffer because it is misaligned. This function always returns the same
The buffer returned by this function should not be used directly when address. Both the display and the gray module heavily use this buffer;
running the gray engine. make sure you don't interfere with them if you access it.
This function does not necessarily returns the video ram that is
currently in use; call display_getCurrentVRAM() for this.
*/ */
inline void *display_getLocalVRAM(void) inline uint32_t *display_getLocalVRAM(void)
{ {
return (void *)local_vram; return local_vram;
} }
/* /*
display_getCurrentVRAM() display_getCurrentVRAM()
Returns the current video ram. This function usually returns the Returns the current monochrome video ram buffer. This function usually
parameter of the last call to display_useVRAM(), unless the gray engine returns the parameter of the last call to display_useVRAM(), or the
is running (in which case the result is undefined). Returns the local local vram address (which is default when the library starts).
vram address by default. The return value of this function is undefined if the gray engine is
running.
*/ */
inline void *display_getCurrentVRAM(void) inline uint32_t *display_getCurrentVRAM(void)
{ {
return (void *)vram; return vram;
} }
/* /*
display_useVRAM() display_useVRAM()
Changes the current video ram address. The argument MUST be a 4- Changes the current monochrome video ram address. The argument must be
aligned 1024-byte buffer ; otherwise any drawing operation will crash a 4-aligned 1024-byte buffer because the library's design requires it.
the program. This function refuses misaligned buffers but trusts that enough space
is available; failing to provide enough memory may crash the program.
This function will most likely have no effect when running the gray This function will most likely have no effect when running the gray
engine. engine.
*/ */
inline void display_useVRAM(void *ptr) inline void display_useVRAM(uint32_t *ptr)
{ {
vram = (int *)ptr; if((intptr_t)ptr & 3) return;
vram = ptr;
} }

View file

@ -1,38 +1,41 @@
#include <internals/display.h> #include <internals/display.h>
#include <display.h> #include <display.h>
#define sgn(x) ((x) < 0 ? -1 : 1)
#define abs(x) ((x) < 0 ? -(x) : (x))
#define rnd(x) ((int)((x) + 0.5))
/* /*
dline() dhline()
Draws a line on the screen. Automatically optimizes horizontal and Optimized procedure for drawing an horizontal line. Uses a rectangle
vertical lines. mask.
*/ */
static void dhline(size_t x1, size_t x2, int y, color_t operator)
static void dhline(int x1, int x2, int y, enum Color color)
{ {
uint32_t masks[4]; uint32_t masks[4];
int offset = y << 2; uint32_t *video = vram + (y << 2) + 4;
int i;
// Swapping x1 and x2 if needed. // Swapping x1 and x2 if needed.
if(x1 > x2) x1 ^= x2, x2 ^= x1, x1 ^= x2; if(x1 > x2) x1 ^= x2, x2 ^= x1, x1 ^= x2;
getMasks(x1, x2, masks); getMasks(x1, x2, masks);
switch(color) switch(operator)
{ {
case Color_White: case color_white:
for(i = 0; i < 4; i++) vram[offset + i] &= ~masks[i]; *--video &= ~masks[3];
*--video &= ~masks[2];
*--video &= ~masks[1];
*--video &= ~masks[0];
break; break;
case Color_Black: case color_black:
for(i = 0; i < 4; i++) vram[offset + i] |= masks[i]; *--video |= masks[3];
*--video |= masks[2];
*--video |= masks[1];
*--video |= masks[0];
break; break;
case Color_Invert: case color_invert:
for(i = 0; i < 4; i++) vram[offset + i] ^= masks[i]; *--video ^= masks[3];
*--video ^= masks[2];
*--video ^= masks[1];
*--video ^= masks[0];
break; break;
default: default:
@ -40,24 +43,30 @@ static void dhline(int x1, int x2, int y, enum Color color)
} }
} }
static void dvline(int y1, int y2, int x, enum Color color) /*
dvline()
Optimized procedure for drawing a vertical line. This one is far less
powerful than dhline() because the video ram is essentially line-
oriented. It also uses a mask.
*/
static void dvline(int y1, int y2, int x, color_t operator)
{ {
int offset = (y1 << 2) + (x >> 5); uint32_t *base = vram + (y1 << 2) + (x >> 5);
int end = (y2 << 2) + (x >> 5); uint32_t *video = vram + (y2 << 2) + (x >> 5) + 4;
int mask = 0x80000000 >> (x & 31); uint32_t mask = 0x80000000 >> (x & 31);
switch(color) switch(operator)
{ {
case Color_White: case color_white:
while(offset <= end) vram[offset] &= ~mask, offset += 4; while(video > base) video -= 4, *video &= ~mask;
break; break;
case Color_Black: case color_black:
while(offset <= end) vram[offset] |= mask, offset += 4; while(video > base) video -= 4, *video |= mask;
break; break;
case Color_Invert: case color_invert:
while(offset <= end) vram[offset] ^= mask, offset += 4; while(video > base) video -= 4, *video ^= mask;
break; break;
default: default:
@ -65,19 +74,28 @@ static void dvline(int y1, int y2, int x, enum Color color)
} }
} }
void dline(int x1, int y1, int x2, int y2, enum Color color) #define sgn(x) ((x) < 0 ? -1 : 1)
#define abs(x) ((x) < 0 ? -(x) : (x))
/*
dline()
Line drawing algorithm more or less directly taken for MonochromeLib.
Thanks PierrotLL for this. Relies on dhline() and dvline() for specific
cases.
*/
void dline(int x1, int y1, int x2, int y2, color_t operator)
{ {
if(adjustRectangle(&x1, &y1, &x2, &y2)) return; if(adjustRectangle(&x1, &y1, &x2, &y2)) return;
// Possible optimizations. // Possible optimizations.
if(y1 == y2) if(y1 == y2)
{ {
dhline(x1, x2, y1, color); dhline(x1, x2, y1, operator);
return; return;
} }
if(x1 == x2) if(x1 == x2)
{ {
dvline(y1, y2, x1, color); dvline(y1, y2, x1, operator);
return; return;
} }
@ -87,7 +105,7 @@ void dline(int x1, int y1, int x2, int y2, enum Color color)
dx = abs(dx), dy = abs(dy); dx = abs(dx), dy = abs(dy);
dpixel(x1, y1, color); dpixel(x1, y1, operator);
if(dx >= dy) if(dx >= dy)
{ {
@ -97,7 +115,7 @@ void dline(int x1, int y1, int x2, int y2, enum Color color)
x += sx; x += sx;
cumul += dy; cumul += dy;
if(cumul > dx) cumul -= dx, y += sy; if(cumul > dx) cumul -= dx, y += sy;
dpixel(x, y, color); dpixel(x, y, operator);
} }
} }
else else
@ -108,9 +126,9 @@ void dline(int x1, int y1, int x2, int y2, enum Color color)
y += sy; y += sy;
cumul += dx; cumul += dx;
if(cumul > dy) cumul -= dy, x += sx; if(cumul > dy) cumul -= dy, x += sx;
dpixel(x, y, color); dpixel(x, y, operator);
} }
} }
dpixel(x2, y2, color); dpixel(x2, y2, operator);
} }

View file

@ -3,30 +3,23 @@
/* /*
dpixel() dpixel()
Puts a pixel in the vram. Changes a pixel's color in the video ram. The result may depend on the
current color of the pixel.
*/ */
void dpixel(int x, int y, enum Color color) void dpixel(size_t x, size_t y, color_t operator)
{ {
if((unsigned int)x > 127 || (unsigned int)y > 63) return; // Let's be honest, all this module's code *heavily* relies on the
// screen dimension in the end, so it's not that big a deal.
if(x > 127 || y > 63) return;
int offset = (y << 2) + (x >> 5); uint32_t *video = vram + (y << 2) + (x >> 5);
int mask = 0x80000000 >> (x & 31); uint32_t mask = 0x80000000 >> (x & 31);
switch(color) switch(operator)
{ {
case Color_White: case color_white: *video &= ~mask; break;
vram[offset] &= ~mask; case color_black: *video |= mask; break;
break; case color_invert: *video ^= mask; break;
default: return;
case Color_Black:
vram[offset] |= mask;
break;
case Color_Invert:
vram[offset] ^= mask;
break;
default:
break;
} }
} }

59
src/display/drect.c Normal file
View file

@ -0,0 +1,59 @@
#include <internals/display.h>
#include <display.h>
/*
drect()
Draws a rectangle on the screen. This function can use any color which
is not associated with the gray engine, including the reverse operator.
*/
void drect(int x1, int y1, int x2, int y2, color_t operator)
{
// Avoid wasting time if the requested operation is invalid here.
if(operator != color_white && operator != color_black
&& operator != color_invert) return;
// Make sure the coordinates are in the right order, and that the
// requested rectangle crosses the screen.
if(adjustRectangle(&x1, &y1, &x2, &y2)) return;
uint32_t masks[4];
getMasks(x1, x2, masks);
uint32_t *base = vram + (y1 << 2);
uint32_t *video = vram + (y2 << 2) + 4;
switch(operator)
{
case color_white:
while(video > base)
{
*--video &= ~masks[3];
*--video &= ~masks[2];
*--video &= ~masks[1];
*--video &= ~masks[0];
}
break;
case color_black:
while(video > base)
{
*--video |= masks[3];
*--video |= masks[2];
*--video |= masks[1];
*--video |= masks[0];
}
break;
case color_invert:
while(video > base)
{
*--video ^= masks[3];
*--video ^= masks[2];
*--video ^= masks[1];
*--video ^= masks[0];
}
break;
// Avoid some warnings.
default: return;
}
}

View file

@ -1,21 +0,0 @@
#include <internals/display.h>
#include <display.h>
/*
dreverse_area()
Reverses an area of the vram. This function is a simple application of
the rectangle masks concept. (x1, y1) and (x2, y2) are reversed as
well.
*/
void dreverse_area(int x1, int y1, int x2, int y2)
{
uint32_t masks[4];
if(adjustRectangle(&x1, &y1, &x2, &y2)) return;
getMasks(x1, x2, masks);
int begin = y1 << 2;
int end = (y2 + 1) << 2;
int i;
for(i = begin; i < end; i++) vram[i] ^= masks[i & 3];
}

View file

@ -6,15 +6,15 @@
and x2 (both included). The four masks are stored in the third argument and x2 (both included). The four masks are stored in the third argument
(seen as an array). (seen as an array).
*/ */
void getMasks(int x1, int x2, uint32_t *masks) void getMasks(size_t x1, size_t x2, uint32_t *masks)
{ {
// Indexes of the first and last longs that are non-blank. // Indexes of the first and last longs that are not empty.
int l1 = x1 >> 5; size_t l1 = x1 >> 5;
int l2 = x2 >> 5; size_t l2 = x2 >> 5;
int i = 0; size_t i = 0;
// Setting the base masks. Those are the final values, except for the // Setting the base masks. Those are the final values, except for the
// longs with indexes l1 and l2, that still need to be adjusted. // longs between indexes l1 and l2, that still need to be adjusted.
while(i < l1) masks[i++] = 0x00000000; while(i < l1) masks[i++] = 0x00000000;
while(i <= l2) masks[i++] = 0xffffffff; while(i <= l2) masks[i++] = 0xffffffff;
while(i < 4) masks[i++] = 0x00000000; while(i < 4) masks[i++] = 0x00000000;

View file

@ -10,7 +10,7 @@
event_t pollevent(void) event_t pollevent(void)
{ {
event_t event = { event_t event = {
.type = ET_None .type = event_none,
}; };
if(queue_size <= 0) return event; if(queue_size <= 0) return event;
@ -33,6 +33,6 @@ event_t waitevent(void)
{ {
event_t event; event_t event;
while((event = pollevent()).type == ET_None) sleep(); while((event = pollevent()).type == event_none) sleep();
return event; return event;
} }

View file

@ -14,7 +14,7 @@ volatile int queue_size = 0;
int event_push(event_t event) int event_push(event_t event)
{ {
if(queue_size >= EVENTS_QUEUE_SIZE) return 1; if(queue_size >= EVENTS_QUEUE_SIZE) return 1;
if(event.type == ET_None) return 2; if(event.type == event_none) return 2;
int index = queue_start + queue_size; int index = queue_start + queue_size;
if(index >= EVENTS_QUEUE_SIZE) index -= EVENTS_QUEUE_SIZE; if(index >= EVENTS_QUEUE_SIZE) index -= EVENTS_QUEUE_SIZE;

View file

@ -6,9 +6,28 @@
*/ */
void gclear(void) void gclear(void)
{ {
int *v1 = gray_lightVRAM(); uint32_t *lbase = gray_lightVRAM();
int *v2 = gray_darkVRAM(); uint32_t *v1 = lbase + 256;
int i; uint32_t *v2 = gray_darkVRAM() + 256;
for(i = 0; i < 256; i++) v1[i] = v2[i] = 0; while(v1 > lbase)
{
*--v1 = 0;
*--v1 = 0;
*--v1 = 0;
*--v1 = 0;
*--v1 = 0;
*--v1 = 0;
*--v1 = 0;
*--v1 = 0;
*--v2 = 0;
*--v2 = 0;
*--v2 = 0;
*--v2 = 0;
*--v2 = 0;
*--v2 = 0;
*--v2 = 0;
*--v2 = 0;
}
} }

View file

@ -1,15 +0,0 @@
#include <gray.h>
#include <display.h>
/*
gclear_area()
Clears an area of the video ram. End points (x1, y1) and (x2, y2) are
included.
*/
void gclear_area(int x1, int y1, int x2, int y2)
{
display_useVRAM(gray_lightVRAM());
dclear_area(x1, y1, x2, y2);
display_useVRAM(gray_darkVRAM());
dclear_area(x1, y1, x2, y2);
}

View file

@ -3,22 +3,23 @@
/* /*
gline() gline()
Draws a line in the vram. Automatically optimizes special cases. Draws a line in the vram. Automatically optimizes special cases. This
function does not support the light operators.
*/ */
void gline(int x1, int y1, int x2, int y2, enum Color color) void gline(int x1, int y1, int x2, int y2, color_t operator)
{ {
enum Color c1, c2; color_t op1, op2;
if(color == Color_None) return; if(operator == color_invert) op1 = op2 = color_invert;
else if(color == Color_Invert) c1 = c2 = Color_Invert; else if(operator >= color_none) return;
else else
{ {
c1 = 3 * (color & 1); op1 = 3 * (operator & 1);
c2 = 3 * (color >> 1); op2 = 3 * (operator >> 1);
} }
display_useVRAM(gray_lightVRAM()); display_useVRAM(gray_lightVRAM());
dline(x1, y1, x2, y2, c1); dline(x1, y1, x2, y2, op1);
display_useVRAM(gray_darkVRAM()); display_useVRAM(gray_darkVRAM());
dline(x1, y1, x2, y2, c2); dline(x1, y1, x2, y2, op2);
} }

View file

@ -4,44 +4,56 @@
gpixel() gpixel()
Puts a pixel in the vram. Puts a pixel in the vram.
*/ */
void gpixel(int x, int y, enum Color color) void gpixel(size_t x, size_t y, color_t operator)
{ {
if((unsigned int)x > 127 || (unsigned int)y > 63) return; if(x > 127 || y > 63) return;
int offset = (y << 2) + (x >> 5); uint32_t *light = gray_lightVRAM() + (y << 2) + (x >> 5);
int mask = 0x80000000 >> (x & 31); uint32_t *dark = gray_darkVRAM() + (y << 2) + (x >> 5);
uint32_t mask = 0x80000000 >> (x & 31);
int *v1 = gray_lightVRAM(); switch(operator)
int *v2 = gray_lightVRAM();
switch(color)
{ {
case Color_White: case color_white:
v1[offset] &= ~mask; *light &= ~mask;
v2[offset] &= ~mask; *dark &= ~mask;
break; break;
case color_light:
case Color_Light: *light |= mask;
v1[offset] |= mask; *dark &= ~mask;
v2[offset] &= ~mask;
break; break;
case color_dark:
case Color_Dark: *light &= ~mask;
v1[offset] &= ~mask; *dark |= mask;
v2[offset] |= mask;
break; break;
case color_black:
case Color_Black: *light |= mask;
v1[offset] |= mask; *dark |= mask;
v2[offset] |= mask;
break; break;
case color_none:
return;
case Color_Invert: case color_invert:
v1[offset] ^= mask; *light ^= mask;
v2[offset] ^= mask; *dark ^= mask;
break; break;
case color_lighten:;
default: uint32_t old_light_1 = *light;
*light &= *dark | ~mask;
*dark = (old_light_1 | ~mask) & (mask ^ *dark);
break;
case color_lighten2:
*dark &= *light | ~mask;
*light &= ~mask;
break;
case color_darken:;
uint32_t old_light_2 = *light;
*light |= *dark & mask;
*dark = (old_light_2 & mask) | (mask ^ *dark);
break;
case color_darken2:
*dark |= *light | mask;
*light |= mask;
break; break;
} }
} }

View file

@ -11,8 +11,8 @@
#include <timer.h> #include <timer.h>
#include <mpu.h> #include <mpu.h>
static int internal_vrams[3][256]; static uint32_t internal_vrams[3][256];
static const void *vrams[4]; static uint32_t *vrams[4];
static int current = 0; static int current = 0;
static int delays[2]; static int delays[2];
@ -45,10 +45,10 @@ void gray_interrupt(void)
*/ */
__attribute__((constructor)) void gray_init(void) __attribute__((constructor)) void gray_init(void)
{ {
vrams[0] = (const void *)display_getLocalVRAM(); vrams[0] = display_getLocalVRAM();
vrams[1] = (const void *)internal_vrams[0]; vrams[1] = internal_vrams[0];
vrams[2] = (const void *)internal_vrams[1]; vrams[2] = internal_vrams[1];
vrams[3] = (const void *)internal_vrams[2]; vrams[3] = internal_vrams[2];
delays[0] = 912; delays[0] = 912;
delays[1] = 1343; delays[1] = 1343;
@ -119,18 +119,18 @@ inline int gray_runs(void)
gray_lightVRAM() gray_lightVRAM()
Returns the module's gray vram address. Returns the module's gray vram address.
*/ */
void *gray_lightVRAM(void) uint32_t *gray_lightVRAM(void)
{ {
return (void *)vrams[~current & 2]; return vrams[~current & 2];
} }
/* /*
gray_lightVRAM() gray_lightVRAM()
Returns the module's dark vram address. Returns the module's dark vram address.
*/ */
void *gray_darkVRAM(void) uint32_t *gray_darkVRAM(void)
{ {
return (void *)vrams[(~current & 2) | 1]; return vrams[(~current & 2) | 1];
} }
/* /*

70
src/gray/grect.c Normal file
View file

@ -0,0 +1,70 @@
#include <internals/display.h>
#include <gray.h>
/*
grect()
Draws a rectangle on the screen. This function can use all colors.
*/
void grect(int x1, int y1, int x2, int y2, color_t operator)
{
if(operator == color_none) return;
if(adjustRectangle(&x1, &y1, &x2, &y2)) return;
uint32_t masks[4];
getMasks(x1, x2, masks);
uint32_t *lbase = gray_lightVRAM() + (y1 << 2);
uint32_t *lvideo = gray_lightVRAM() + (y2 << 2) + 4;
uint32_t *dvideo = gray_darkVRAM() + (y2 << 2) + 4;
// Doing things in this order will be slower, but man, I can't stand
// writing that many lines of code for such a simple task. It will be
// terribly heavy in the binary file...
while(lvideo > lbase) for(int i = 0; i < 4; i++) switch(operator)
{
case color_white:
*--lvideo &= ~masks[i];
*--dvideo &= ~masks[i];
break;
case color_light:
*--lvideo |= masks[i];
*--dvideo &= ~masks[i];
break;
case color_dark:
*--lvideo &= ~masks[i];
*--dvideo |= masks[i];
break;
case color_black:
*--lvideo |= masks[i];
*--dvideo |= masks[i];
break;
case color_none: return;
case color_invert:
*--lvideo ^= masks[i];
*--dvideo ^= masks[i];
break;
case color_lighten:;
uint32_t light_1 = *lvideo;
dvideo--;
*--lvideo &= *dvideo | ~masks[i];
*dvideo = (light_1 | ~masks[i]) & (masks[i] ^ *dvideo);
break;
case color_lighten2:
lvideo--;
*--dvideo &= *lvideo | ~masks[i];
*lvideo &= ~masks[i];
break;
case color_darken:;
uint32_t light_2 = *lvideo;
dvideo--;
*--lvideo |= *dvideo & masks[i];
*dvideo = (light_2 & masks[i]) | (masks[i] ^ *dvideo);
break;
case color_darken2:
lvideo--;
*--dvideo |= *lvideo | masks[i];
*lvideo |= masks[i];
break;
}
}

View file

@ -1,15 +0,0 @@
#include <gray.h>
#include <display.h>
/*
greverse_area()
Reverses an area of the vram. End points (x1, y1) and (x2, y2) are
included.
*/
void greverse_area(int x1, int y1, int x2, int y2)
{
display_useVRAM(gray_lightVRAM());
dreverse_area(x1, y1, x2, y2);
display_useVRAM(gray_darkVRAM());
dreverse_area(x1, y1, x2, y2);
}

View file

@ -1,4 +1,5 @@
#include <internals/keyboard.h> #include <internals/keyboard.h>
#include <internals/timer.h>
#include <keyboard.h> #include <keyboard.h>
#include <events.h> #include <events.h>
#include <screen.h> #include <screen.h>
@ -11,10 +12,11 @@
int getkey(void) int getkey(void)
{ {
return getkey_opt( return getkey_opt(
Getkey_ShiftModifier | getkey_shift_modifier |
Getkey_AlphaModifier | getkey_alpha_modifier |
Getkey_ManageBacklight | getkey_manage_backlight |
Getkey_RepeatArrowKeys, getkey_repeat_arrow_keys,
0 0
); );
} }
@ -22,74 +24,50 @@ int getkey(void)
/* /*
getkey_opt() getkey_opt()
Enhances getkey() with more general functionalities. Enhances getkey() with more general functionalities.
If max_cycles is non-zero and positive, getkey_opt() will return If delay_ms is non-zero and positive, getkey_opt() will return
KEY_NOEVENT if no event occurs during max_cycle analysis. KEY_NOEVENT if no event occurs before delay_ms.
*/ */
static void getkey_opt_wait(int *cycles) static void getkey_opt_wait(int *delay_ms)
{ {
while(!interrupt_flag) sleep(); while(!interrupt_flag) sleep();
interrupt_flag = 0; interrupt_flag = 0;
if(*cycles > 0) (*cycles)--; if(*delay_ms > 0)
{
(*delay_ms) -= vtimer->ms_delay;
if(*delay_ms < 0) *delay_ms = 0;
} }
int getkey_opt(enum GetkeyOpt options, int cycles) }
int getkey_opt(getkey_option_t options, int delay_ms)
{ {
event_t event; event_t event;
int modifier = 0; int modifier = 0, key;
static int event_ref = 0; key_type_t type;
if(cycles <= 0) cycles = -1; if(delay_ms <= 0) delay_ms = -1;
while(cycles != 0)
{
event = pollevent();
switch(event.type)
{
case ET_None:
if(last_key == KEY_NONE)
{
getkey_opt_wait(&cycles);
continue;
}
// Handling repetitions. while(delay_ms != 0) switch((event = pollevent()).type)
enum KeyType type = keytype(last_key);
if(!(options & (type << 4))) break;
if(event_ref <= 0)
{ {
getkey_opt_wait(&cycles); case event_none:
event_ref++; getkey_opt_wait(&delay_ms);
continue;
}
last_events++;
event_ref--;
if(last_events >= (last_repeats ? repeat_next :
repeat_first))
{
last_repeats++;
last_events = 0;
return last_key;
}
break; break;
case ET_KeyPress: case event_key_press:
; key = event.key;
int key = event.key;
if(options & Getkey_ManageBacklight && key == KEY_OPTN if(options & getkey_manage_backlight && key == KEY_OPTN
&& modifier & MOD_SHIFT) && (modifier & MOD_SHIFT))
{ {
screen_toggleBacklight(); screen_toggleBacklight();
modifier &= ~MOD_SHIFT; modifier &= ~MOD_SHIFT;
continue; continue;
} }
if(options & Getkey_ShiftModifier && key == KEY_SHIFT) if(options & getkey_shift_modifier && key == KEY_SHIFT)
{ {
modifier ^= MOD_SHIFT; modifier ^= MOD_SHIFT;
continue; continue;
} }
if(options & Getkey_AlphaModifier && key == KEY_ALPHA) if(options & getkey_alpha_modifier && key == KEY_ALPHA)
{ {
modifier ^= MOD_ALPHA; modifier ^= MOD_ALPHA;
continue; continue;
@ -97,26 +75,41 @@ int getkey_opt(enum GetkeyOpt options, int cycles)
last_key = key; last_key = key;
last_repeats = 0; last_repeats = 0;
last_events = 0; last_time = 0;
event_ref = 0;
return key | modifier; return key | modifier;
case ET_KeyRel: case event_key_repeat:
key = event.key;
if(key != last_key) continue;
// Checking that this type of repetition is allowed.
type = key_type(key);
if(!(options & (type << 4))) break;
last_time += vtimer->ms_delay;
int cmp = last_repeats ? repeat_next : repeat_first;
if(last_time >= cmp)
{
last_repeats++;
last_time -= cmp;
return last_key;
}
break;
case event_key_release:
if(event.key != last_key) break; if(event.key != last_key) break;
last_key = KEY_NONE; last_key = KEY_NONE;
last_repeats = 0; last_repeats = 0;
last_events = 0; last_time = 0;
event_ref = 0;
break; break;
default: default:
break; break;
} }
}
last_key = KEY_NONE; last_key = KEY_NONE;
last_repeats = 0; last_repeats = 0;
last_events = 0; last_time = 0;
event_ref = 0;
return KEY_NONE; return KEY_NONE;
} }

View file

@ -1,11 +1,12 @@
#include <keyboard.h> #include <keyboard.h>
/* /*
keychar() key_char()
Returns the ASCII character associated with a key, or 0 for control Returns the ASCII character associated with a character key, and 0 for
keys. other keys. This function expects a matrix code and not a key_id()
code, and heeds for the ALPHA modifier.
*/ */
int keychar(int key) int key_char(int matrix_key)
{ {
char flat[] = { char flat[] = {
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
@ -30,7 +31,6 @@ int keychar(int key)
'Z', ' ', '"', 0x0, 0x0, 0x0 'Z', ' ', '"', 0x0, 0x0, 0x0
}; };
int id = keyid(key); int id = key_id(matrix_key);
return (matrix_key & MOD_ALPHA) ? alpha[id] : flat[id];
return (key & MOD_ALPHA) ? alpha[id] : flat[id];
} }

19
src/keyboard/key_id.c Normal file
View file

@ -0,0 +1,19 @@
#include <keyboard.h>
/*
keyid()
Transforms a key identifier and returns a key code that is more
convenient for array subscript that the original matrix codes. Please
note that there are a few holes in the numbering.
This function ignores modifiers and returns -1 on error.
*/
int key_id(int matrix_key)
{
if(matrix_key < 0) return -1;
matrix_key &= MOD_CLEAR;
int row = 9 - (matrix_key & 0x0f);
int column = 6 - ((matrix_key & 0xf0) >> 4);
return 6 * row + column;
}

24
src/keyboard/key_type.c Normal file
View file

@ -0,0 +1,24 @@
#include <keyboard.h>
/*
key_type()
Returns a key's type. This functions ignores modifiers and expects
matrix codes as argument, not key_id() codes.
*/
key_type_t key_type(int matrix_key)
{
matrix_key &= MOD_CLEAR;
// Arrow keys.
if(matrix_key == KEY_UP || matrix_key == KEY_RIGHT
|| matrix_key == KEY_DOWN || matrix_key == KEY_LEFT)
{
return key_type_arrow;
}
// Function keys (F1 .. F6) are ton only keys of the ninth row.
if((matrix_key & 0x0f) == 0x09) return key_type_function;
// Then character keys are those that have an associated character. =p
return key_char(matrix_key) ? key_type_character : key_type_control;
}

View file

@ -1,20 +0,0 @@
#include <internals/keyboard.h>
#include <keyboard.h>
#include <clock.h>
#include <rtc.h>
/*
keyboard_setRepeatRate()
Sets the default repeat rate for key events. The unit for the argument
is the keyboard period.
For example at 32 Hz, values of (20, 4) will imitate the system
default.
*/
void keyboard_setRepeatRate(int first, int next)
{
if(first < 0) first = 0;
if(next < 0) next = 0;
repeat_first = first;
repeat_next = next;
}

View file

@ -0,0 +1,181 @@
#include <internals/keyboard.h>
#include <keyboard.h>
#include <timer.h>
#include <mpu.h>
#include <clock.h>
#include <events.h>
//---
// Keyboard variables.
//---
// Current keyboard state: each element represents a row, and each bit a key,
// but not all bits are used.
volatile uint8_t keyboard_state[10] = { 0 };
// Interrupt flag: set when an interrupt occurs, and cleared by functions such
// as getkey() that watch it (because such functions will wake up every time an
// interrupt occurs, and they need to know whether it was the keyboard).
volatile int interrupt_flag = 0;
// Delays, in milliseconds, before repeating keys (the first repetition may
// have a different delay).
int repeat_first = 625, repeat_next = 125;
// Which key was pressed last, how many times it has been repeated, and how
// much time (in milliseconds) has elapsed since it was last repeated.
int last_key = KEY_NONE, last_repeats = 0, last_time = 0;
// Virtual timer object.
timer_t *vtimer = NULL;
//---
// Interrupt management.
//---
static inline void push_press(int keycode)
{
event_t event = {
.type = event_key_press,
.key = keycode,
};
event_push(event);
}
static inline void push_repeat(int keycode)
{
event_t event = {
.type = event_key_repeat,
.key = keycode,
};
event_push(event);
}
static inline void push_release(int keycode)
{
event_t event = {
.type = event_key_release,
.key = keycode,
};
event_push(event);
}
/*
keyboard_interrupt()
Callback function for keyboard update; called by the timer manager when
the keyboard's virtual timer fires. Ideally this function should be
interrupt-driven but the PINT interrupts seem to fire continuously,
which is annoying.
*/
void keyboard_interrupt(void)
{
uint8_t state[10] = { 0 };
isSH3() ? keyboard_updateState_7705(state)
: keyboard_updateState_7305(state);
// This procedure really needs to be speed-optimized... and it's hard
// because of this bit manipulation. This condition handles AC/ON.
if(keyboard_state[0] ^ state[0])
{
uint8_t pressed = ~keyboard_state[0] & state[0];
uint8_t released = keyboard_state[0] & ~state[0];
if(pressed & 1) push_press(KEY_AC_ON);
if(released & 1) push_release(KEY_AC_ON);
}
keyboard_state[0] = state[0];
for(int row = 1; row <= 9; row++)
{
uint8_t pressed = ~keyboard_state[row] & state[row];
uint8_t repeated = keyboard_state[row] & state[row];
uint8_t released = keyboard_state[row] & ~state[row];
keyboard_state[row] = state[row];
// Make this a bit faster.
if(!(pressed | repeated | released)) continue;
for(int column = 0; column < 8; column++)
{
if(pressed & 1) push_press ((column << 4) | row);
if(repeated & 1) push_repeat ((column << 4) | row);
if(released & 1) push_release((column << 4) | row);
pressed >>= 1;
repeated >>= 1;
released >>= 1;
}
}
// Signal the interrupt to the higher-level functions.
interrupt_flag = 1;
}
//---
// Keyboard configuration.
//---
/*
keyboard_init()
Starts the keyboard timer.
*/
__attribute__((constructor)) void keyboard_init(void)
{
keyboard_setRepeatRate(625, 125);
vtimer = timer_create(25, 0);
timer_attach(vtimer, keyboard_interrupt, NULL);
timer_start(vtimer);
}
/*
keyboard_setAnalysisDelay()
Sets the keyboard analysis delay, that is, the delay (in ms) between
two keyboard analyzes. Please note that the repeat delays should be
multiples of the analysis delay for better accuracy.
*/
void keyboard_setAnalysisDelay(int analysis_delay_ms)
{
if(analysis_delay_ms <= 0) return;
timer_reload(vtimer, analysis_delay_ms);
}
/*
keyboard_setRepeatRate()
Sets the default repeat rate for key events. The delay before the first
repeat may have a different value (usually longer). The unit for the
argument is ms, but the repeat events themselves may only be fired when
a keyboard analysis is performed; which means that for better accuracy,
these delays should be a multiple of the keyboard period.
For instance, delays of (625 ms, 125 ms) will imitate the system's
default setting.
*/
void keyboard_setRepeatRate(int first, int next)
{
repeat_first = (first > 0) ? first : 0;
repeat_next = (next > 0) ? next : 0;
}
/*
keyboard_quit()
Stops the keyboard timer.
*/
__attribute__((destructor)) void keyboard_quit(void)
{
timer_destroy(vtimer);
vtimer = NULL;
}
/*
keyboard_stateBuffer()
Returns the address of the keyboard state array. The returned address
is the handler's buffer, therefore it contains volatile data.
*/
volatile uint8_t *keyboard_stateBuffer(void)
{
return keyboard_state;
}

View file

@ -1,132 +0,0 @@
#include <internals/keyboard.h>
#include <keyboard.h>
#include <timer.h>
#include <mpu.h>
#include <clock.h>
#include <events.h>
//---
// Keyboard variables.
//---
// These ones get modified by interrupts.
volatile uint8_t keyboard_state[10] = { 0 };
volatile int interrupt_flag = 0;
// Key statistics.
int repeat_first = 10, repeat_next = 2;
int last_key = KEY_NONE, last_repeats = 0, last_events = 0;
// RTC callback id.
unsigned cb_id;
//---
// Interrupt management.
//---
static void push_press(int keycode)
{
event_t event = {
.type = ET_KeyPress,
.key = keycode,
};
event_push(event);
}
static void push_repeat(int keycode)
{
event_t event = {
.type = ET_KeyRepeat,
.key = keycode,
};
event_push(event);
}
static void push_release(int keycode)
{
event_t event = {
.type = ET_KeyRel,
.key = keycode,
};
event_push(event);
}
/*
keyboard_interrupt()
Callback for keyboard update. Allows keyboard analysis functions to
wake only when keyboard interrupts happen.
*/
void keyboard_interrupt(void)
{
uint8_t state[10] = { 0 };
isSH3() ? keyboard_updateState_7705(state)
: keyboard_updateState_7305(state)
;
// Try to minimize number of operations in common cases... this handles
// AC/ON.
if(keyboard_state[0] ^ state[0])
{
uint8_t pressed = ~keyboard_state[0] & state[0];
uint8_t released = keyboard_state[0] & ~state[0];
if(pressed & 1) push_press(KEY_AC_ON);
if(released & 1) push_release(KEY_AC_ON);
}
keyboard_state[0] = state[0];
for(int row = 1; row <= 9; row++)
{
uint8_t pressed = ~keyboard_state[row] & state[row];
uint8_t repeated = keyboard_state[row] & state[row];
uint8_t released = keyboard_state[row] & ~state[row];
keyboard_state[row] = state[row];
// Fasten this a bit.
if(!(pressed | repeated | released)) continue;
for(int column = 0; column < 8; column++)
{
if(pressed & 1) push_press ((column << 4) | row);
if(repeated & 1) push_repeat ((column << 4) | row);
if(released & 1) push_release((column << 4) | row);
pressed >>= 1;
repeated >>= 1;
released >>= 1;
}
}
interrupt_flag = 1;
}
/*
keyboard_init()
Starts the keyboard timer.
*/
__attribute__((constructor)) void keyboard_init(void)
{
cb_id = rtc_cb_add(RTCFreq_16Hz, keyboard_interrupt, 0);
}
/*
keyboard_setFrequency()
Sets the keyboard frequency.
*/
void keyboard_setFrequency(enum KeyboardFrequency frequency)
{
if(frequency < 1 || frequency > 7) return;
rtc_cb_edit(cb_id, frequency, keyboard_interrupt);
}
/*
keyboard_quit()
Stops the keyboard timer.
*/
__attribute__((destructor)) void keyboard_quit(void)
{
rtc_cb_end(cb_id);
}

View file

@ -1,25 +0,0 @@
#include <keyboard.h>
#include <internals/keyboard.h>
/*
keylast()
Returns the matrix code of the last pressed key. If repeat_count is
non-NULL, it is set to the number of repetitions.
*/
int keylast(int *repeat_count)
{
if(repeat_count) *repeat_count = last_repeats;
return last_key;
}
/*
keystate()
Returns the address of the keyboard state array. The returned address
is the handler's buffer, therefore it contains volatile data.
*/
volatile uint8_t *keystate(void)
{
return keyboard_state;
}

View file

@ -10,6 +10,7 @@
// //
//--- //---
#include <stdint.h>
#include <keyboard.h> #include <keyboard.h>
#include <7305.h> #include <7305.h>
@ -61,20 +62,20 @@ static void kdelay(void)
*/ */
static int krow(int row) static int krow(int row)
{ {
volatile unsigned short *injector1 = (unsigned short *)0xa4050116; volatile uint16_t *injector1 = (void *)0xa4050116;
volatile unsigned char *data1 = (unsigned char *)0xa4050136; volatile uint8_t *data1 = (void *)0xa4050136;
volatile unsigned short *injector2 = (unsigned short *)0xa4050118; volatile uint16_t *injector2 = (void *)0xa4050118;
volatile unsigned char *data2 = (unsigned char *)0xa4050138; volatile uint8_t *data2 = (void *)0xa4050138;
volatile unsigned short *detector = (unsigned short *)0xa405014c; volatile uint16_t *detector = (void *)0xa405014c;
volatile unsigned char *keys = (unsigned char *)0xa405016c; volatile uint8_t *keys = (void *)0xa405016c;
volatile unsigned char *key_register = (unsigned char *)0xa40501c6; volatile uint8_t *key_register = (void *)0xa40501c6;
// volatile unsigned short *hizcrb = (unsigned short *)0xa405015a; // volatile uint16_t *hizcrb = (void *)0xa405015a;
unsigned short smask; uint16_t smask;
unsigned char cmask; uint8_t cmask;
int result = 0; int result = 0;
if(row < 0 || row > 9) return 0; if(row < 0 || row > 9) return 0;

View file

@ -1,17 +0,0 @@
#include <keyboard.h>
/*
keyid()
Returns a non-matrix key code that can be used for array subscript.
Ignores modifiers.
*/
int keyid(int key)
{
if(key < 0) return -1;
key &= MOD_CLEAR;
int row = 9 - (key & 0x0f);
int column = 6 - ((key & 0xf0) >> 4);
return 6 * row + column;
}

View file

@ -1,17 +0,0 @@
#include <keyboard.h>
/*
keytype()
Returns a key's type. Ignores modifiers.
*/
enum KeyType keytype(int key)
{
key &= MOD_CLEAR;
if(key == KEY_UP || key == KEY_RIGHT || key == KEY_DOWN ||
key == KEY_LEFT) return KeyType_Arrow;
if((key & 0x0f) == 0x09) return KeyType_Function;
return keychar(key) ? KeyType_Character : KeyType_Control;
}

View file

@ -40,7 +40,7 @@ void multigetkey(int *keys, int count, int cycles)
{ {
last_key = keys[0]; last_key = keys[0];
last_repeats = 0; last_repeats = 0;
last_events = 0; last_time = 0;
} }
if(number) break; if(number) break;
@ -48,7 +48,7 @@ void multigetkey(int *keys, int count, int cycles)
} }
do event = pollevent(); do event = pollevent();
while(event.type != ET_None); while(event.type != event_none);
return; return;
} }

View file

@ -1,64 +0,0 @@
#include <ctype.h>
enum {
cntrl = 0x01,
space = 0x02,
punct = 0x04,
print = 0x08,
upper = 0x20,
lower = 0x10,
digit = 0x40,
xdigt = 0x80,
};
uint8_t ctype_classes[0x80] = {
// Control characters.
cntrl, cntrl, cntrl, cntrl, cntrl, cntrl, cntrl, cntrl, cntrl,
cntrl | space, // Tabulation
cntrl | space, cntrl | space, cntrl | space, cntrl | space,
cntrl, cntrl, cntrl, cntrl, cntrl, cntrl, cntrl, cntrl, cntrl, cntrl,
cntrl, cntrl, cntrl, cntrl, cntrl, cntrl, cntrl, cntrl,
// Space and some punctuation.
space | print,
punct | print, punct | print, punct | print, punct | print,
punct | print, punct | print, punct | print, punct | print,
punct | print, punct | print, punct | print, punct | print,
punct | print, punct | print, punct | print,
// Decimal digits.
digit | xdigt | print, digit | xdigt | print, digit | xdigt | print,
digit | xdigt | print, digit | xdigt | print, digit | xdigt | print,
digit | xdigt | print, digit | xdigt | print, digit | xdigt | print,
digit | xdigt | print,
// Some punctuation.
punct | print, punct | print, punct | print, punct | print,
punct | print, punct | print, punct | print,
// Uppercase alphabet.
upper | xdigt | print, upper | xdigt | print, upper | xdigt | print,
upper | xdigt | print, upper | xdigt | print, upper | xdigt | print,
upper | print, upper | print, upper | print, upper | print,
upper | print, upper | print, upper | print, upper | print,
upper | print, upper | print, upper | print, upper | print,
upper | print, upper | print, upper | print, upper | print,
upper | print, upper | print, upper | print, upper | print,
// Other punctuation symbols.
punct | print, punct | print, punct | print, punct | print,
punct | print, punct | print,
// Lowercase alphabet.
lower | xdigt | print, lower | xdigt | print, lower | xdigt | print,
lower | xdigt | print, lower | xdigt | print, lower | xdigt | print,
lower | print, lower | print, lower | print, lower | print,
lower | print, lower | print, lower | print, lower | print,
lower | print, lower | print, lower | print, lower | print,
lower | print, lower | print, lower | print, lower | print,
lower | print, lower | print, lower | print, lower | print,
// Last punctuation characters and DEL.
punct | print, punct | print, punct | print, punct | print,
cntrl
};

View file

@ -5,11 +5,11 @@
text_configure() text_configure()
Sets the font and mode to use for the following print operations. Sets the font and mode to use for the following print operations.
*/ */
void text_configure(struct Font *next_font, enum Color next_color) void text_configure(struct Font *next_font, color_t next_operator)
{ {
extern Font gint_font_system; extern Font gint_font_system;
if(next_font) font = next_font; if(next_font) font = next_font;
else font = &gint_font_system; else font = &gint_font_system;
color = next_color; operator = next_operator;
} }

View file

@ -4,42 +4,61 @@
void operate_gray(OPERATE_ARGS) void operate_gray(OPERATE_ARGS)
{ {
int *vl = gray_lightVRAM(); size_t vram_offset = (x >> 5) + (y << 2);
int *vd = gray_darkVRAM(); uint32_t *light = gray_lightVRAM() + vram_offset;
int vram_offset = (x >> 5) + (y << 2); uint32_t *dark = gray_darkVRAM() + vram_offset;
uint32_t op; uint32_t op, old_light;
int i;
for(i = 0; i < height; i++) for(int i = 0; i < height; i++)
{ {
op = operators[i]; op = operators[i];
switch(color) switch(operator)
{ {
case Color_White: case color_white:
vl[vram_offset] &= ~op; *light &= ~op;
vd[vram_offset] &= ~op; *dark &= ~op;
break; break;
case Color_Light: case color_light:
vl[vram_offset] |= op; *light |= op;
vd[vram_offset] &= ~op; *dark &= ~op;
break; break;
case Color_Dark: case color_dark:
vl[vram_offset] &= ~op; *light &= ~op;
vd[vram_offset] |= op; *dark |= op;
break; break;
case Color_Black: case color_black:
vl[vram_offset] |= op; *light |= op;
vd[vram_offset] |= op; *dark |= op;
break; break;
case Color_Invert: case color_none:
vl[vram_offset] ^= op; return;
vd[vram_offset] ^= op;
case color_invert:
*light ^= op;
*dark ^= op;
break; break;
default: case color_lighten:
old_light = *light;
*light &= *dark | ~op;
*dark = (old_light | ~op) & (op ^ *dark);
break;
case color_lighten2:
*dark &= *light | ~op;
*light &= ~op;
break;
case color_darken:
old_light = *light;
*light |= *dark & op;
*dark = (old_light & op) | (op ^ *dark);
break;
case color_darken2:
*dark |= *light | op;
*light |= op;
break; break;
} }
vram_offset += 4; light += 4;
dark += 4;
} }
} }

View file

@ -5,7 +5,7 @@
#include <gray.h> #include <gray.h>
struct Font *font; struct Font *font;
enum Color color; color_t operator;
/* /*
tales_init() tales_init()
@ -13,7 +13,7 @@ enum Color color;
*/ */
void tales_init(void) void tales_init(void)
{ {
text_configure(NULL, Color_Black); text_configure(NULL, color_black);
} }
/* /*
@ -93,31 +93,36 @@ int getCharacterIndex(int c)
*/ */
void operate_mono(OPERATE_ARGS) void operate_mono(OPERATE_ARGS)
{ {
int *vram = display_getCurrentVRAM(); uint32_t *vram = display_getCurrentVRAM();
int vram_offset = (x >> 5) + (y << 2); uint32_t *video = vram + (x >> 5) + (y << 2);
uint32_t op;
int i;
for(i = 0; i < height; i++) switch(operator)
{ {
op = operators[i]; case color_white:
for(int i = 0; i < height; i++)
switch(color)
{ {
case Color_White: *video &= ~operators[i];
vram[vram_offset] &= ~op; video += 4;
break;
case Color_Black:
vram[vram_offset] |= op;
break;
case Color_Invert:
vram[vram_offset] ^= op;
break;
default:
break;
} }
break;
vram_offset += 4; case color_black:
for(int i = 0; i < height; i++)
{
*video |= operators[i];
video += 4;
}
break;
case color_invert:
for(int i = 0; i < height; i++)
{
*video ^= operators[i];
video += 4;
}
break;
default: return;
} }
} }

View file

@ -94,7 +94,7 @@ void timer_callback_event(timer_t *timer)
else else
{ {
event_t event = { event_t event = {
.type = ET_Timer, .type = event_timer_underflow,
.timer = timer .timer = timer
}; };
event_push(event); event_push(event);

View file

@ -38,6 +38,24 @@ timer_t *timer_create(int ms_delay, int repeats)
return timer; return timer;
} }
/*
timer_reload()
Changes a virtual timer's delay. The timer is not stopped nor started:
it keeps running or waiting. Events that were waiting to be handled are
dropped and the number of repeats left is not changed. The timer
restarts counting from 0 regardless of how much time had elapsed since
it last fired.
*/
void timer_reload(timer_t *timer, int new_ms_delay)
{
if(!timer->virtual) return;
timer->ms_delay = new_ms_delay;
timer->ms_elapsed = 0;
vtimer_updateAll();
}
/* /*
timer_destroy() timer_destroy()
Destroys a virtual timer. This virtual timer pointer becomes invalid Destroys a virtual timer. This virtual timer pointer becomes invalid
@ -136,7 +154,7 @@ static void vtimer_update(int new_delay)
timer_stop(timer); timer_stop(timer);
return; return;
} }
uint32_t new_constant = clock_setting(new_delay, Clock_ms); uint32_t new_constant = clock_setting(new_delay, clock_ms);
// The transition needs to be as smooth as possible. We have probably // The transition needs to be as smooth as possible. We have probably
// spent a lot of time calculating this new GCD delay so we want to // spent a lot of time calculating this new GCD delay so we want to
@ -176,7 +194,7 @@ static void vtimer_update(int new_delay)
/* /*
gcd() gcd()
Well, the Euclidean algorithm. That's a O(ln(a)) & O(ln(b)) FWIW. Well, the Euclidean algorithm. That's O(ln(max(a, b))) FWIW.
*/ */
static int gcd(int a, int b) static int gcd(int a, int b)
{ {