Almost ended quality review, and fixed tales-related vram overflows.

This commit is contained in:
lephe 2017-04-19 19:57:31 +02:00
parent 6c0ae34708
commit 3214f6b797
33 changed files with 327 additions and 183 deletions

View file

@ -30,7 +30,7 @@ wr = g1a-wrapper
# Flags for gint
lib-cflags = -m3 -mb -nostdlib -I include -ffreestanding -std=c11 -Os \
-Wall -Wextra @gcc.cfg
-Wall -Wextra @gcc.cfg -g0
# Demo application (could be done better)
demo-src = $(notdir $(wildcard demo/*.[cs]))

9
TODO
View file

@ -3,14 +3,11 @@ Bugs to fix:
- Lost keyboard control at startup (could not reproduce)
- Alignment of ALL .data / .rodata files is required to ensure converted data
is properly aligned
- Reported video ram overflow when rendering text at borderline positions
Things to do before 1.0:
- bopti: Test partial transparency
* core: Allow return to menu
- demo: Try 284x124 at (-60, -28) (all disadvantages)
- display: Implement rectangle-based drawing functions
- project: Add a real build-based version system
- project: Check size of *all* library structures
- project: Clean headers that have some internal definitions
- project: Unify this hellish mess of register access!
@ -24,7 +21,13 @@ Things to do later:
- core: Review interrupt system (again) - this one is too slow
- errno: Introduce errno and use it more or less everywhere
- esper: Cleaner playback, synthesizing
- events: Allow customization of keyboard event system (option to return
| events with modifiers, etc)
- events: Generate keyboard events on-the-fly by reading state arrays,
| allowing both a faster interrupt and avoiding supressing other
| events inside getkey() and multigetkey()
- serial: Implement a driver
- stdio: More serious formatted printing functions
- string: Use cmp/str to implement memchr() (assembler examples)
- string: Do some tests for memcmp() and memcpy()
- usb: Implement a driver

56
demo/asm_syscalls.s Normal file
View file

@ -0,0 +1,56 @@
# int BFile_Remove(const uint16_t *file)
.global _BFile_Remove
_BFile_Remove:
mov.l 1f, r0
mov.l 2f, r1
jmp @r1
mov #0, r5
1: .long 0x0439
# int BFile_Create(const uint16_t *file, enum { file = 1, folder = 5 },
# int *size)
.global _BFile_Create
_BFile_Create:
mov.l 1f, r0
mov.l 2f, r1
jmp @r1
nop
1: .long 0x0434
# int BFile_Open(const uint16_t *file, int mode)
.global _BFile_Open
_BFile_Open:
mov.l 1f, r0
mov.l 2f, r1
jmp @r1
mov #0, r6
1: .long 0x042c
# int BFile_Close(int handle)
.global _BFile_Close
_BFile_Close:
mov.l 1f, r0
mov.l 2f, r1
jmp @r1
nop
1: .long 0x042d
# int BFile_Write(int handle, void *ram_buffer, int even_size)
.global _BFile_Write
_BFile_Write:
mov.l 1f, r0
mov.l 2f, r1
jmp @r1
nop
1: .long 0x0435
.align 4
# Syscall branch address
2: .long 0x80010070

View file

@ -434,6 +434,10 @@ void main_menu(int *category, int *app)
index = 0;
scroll = 0;
break;
case KEY_F6:;
void screen(void);
screen();
break;
case KEY_UP:
if(list && list_len > 1)
@ -533,3 +537,48 @@ void crash(void)
"trapa #1 "
);
}
void screen(void)
{
enum { File = 1, Folder = 5 };
enum { Read = 0x01, Write = 0x02, ReadWrite = Read | Write };
int BFile_Remove(const uint16_t *file);
int BFile_Create(const uint16_t *file, int type, int *size);
int BFile_Open(const uint16_t *file, int mode);
int BFile_Close(int handle);
int BFile_Write(int handle, const void *ram_buffer, int even_size);
const uint8_t bmp_header[0x7e] = {
0x42, 0x4d, 0x7e, 0x04, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x7e, 0x00, 0x00, 0x00, 0x6c, 0x00,
0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x40, 0x00,
0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x13, 0x0b,
0x00, 0x00, 0x13, 0x0b, 0x00, 0x00, 0x01, 0x00,
0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x42, 0x47,
0x52, 0x73, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0xff, 0xff, 0xff, 0x00,
};
const uint16_t file[] = { '\\', '\\', 'f', 'l', 's', '0', '\\', 'g',
's', 'c', 'r', 'e', 'e', 'n', '.', 'b', 'm', 'p', 0x00 };
int size = 0x7e + 1024;
BFile_Remove(file);
BFile_Create(file, File, &size);
int handle = BFile_Open(file, Write);
BFile_Write(handle, bmp_header, 0x7e);
void *vram = (void *)display_getCurrentVRAM() + 1024;
for(int i = 1; i <= 64; i++)
{
BFile_Write(handle, vram - 16 * i, 16);
}
BFile_Close(handle);
}

View file

@ -53,14 +53,15 @@ static void draw_keyboard(volatile uint8_t *state)
typedef struct {
event_type_t type;
int key;
uint32_t key;
int repeats;
} enhanced_event_t;
static void push_history(enhanced_event_t *history, int size, event_t event)
{
#define event_eq(x, y) ((x).type == (y).type && (x).key == (y).key)
#define event_eq(x, y) \
((x).type == (y).type && (x).key == (y).key.code)
// Determining where the history ends.
int length = 0;
@ -82,7 +83,7 @@ static void push_history(enhanced_event_t *history, int size, event_t event)
// Adding a new entry to the history.
history[length].type = event.type;
history[length].key = event.key;
history[length].key = event.key.code;
history[length].repeats = 1;
#undef event_eq
@ -135,7 +136,7 @@ void test_keyboard_events(void)
dupdate();
event = waitevent();
if(event.type == event_key_press && event.key == KEY_EXIT)
if(event.type == event_key_press && event.key.code == KEY_EXIT)
break;
push_history(history, history_size, event);
}

View file

@ -10,11 +10,11 @@
Displays some text using different modes and clipping options.
*/
static Font *select(Font *current)
static font_t *select(font_t *current)
{
extern Font res_font_modern;
extern font_t res_font_modern;
struct {
Font *font;
font_t *font;
const char *name;
} fonts[] = {
{ NULL, "gint default" },
@ -83,7 +83,7 @@ void test_tales(void)
color_t colors[] = { color_black, color_dark, color_light, color_white,
color_invert };
extern image_t res_opt_tales;
Font *font = NULL;
font_t *font = NULL;
int black_bg = 0;
int color = 0;
@ -113,10 +113,8 @@ void test_tales(void)
for(int j = 0; j < 16; j++) str[j] = 32 + (i << 4) + j;
str[16] = 0;
gtext(-3, -3 + i * height, str);
gtext(1, 1 + i * height, str);
}
gtext(120, 60, "Hello, World!");
gtext(-40, 2, "Try");
gimage(0, 56, &res_opt_tales);

View file

@ -41,6 +41,23 @@ typedef enum
} event_type_t;
/*
key_event_t
Keyboard events. "I think the user wants something."
*/
typedef struct
{
// This is the key code as defined in <keyboard.h> (a matrix code), and
// probably what you need.
uint32_t code;
// This is a "compact id" which can be used for array subscript. There
// are only a few holes in the "compact id" numbering.
uint32_t id;
// Character associated with the event key.
int character;
} key_event_t;
/*
event_t
Wake up, something's going on. The union member that holds information
@ -55,7 +72,7 @@ typedef struct
// For event_user.
void *data;
// For event_key_press, event_key_repeat and event_key_release.
int key;
key_event_t key;
// For event_timer_underflow.
timer_t *timer;
};

View file

@ -23,6 +23,7 @@ __attribute__((always_inline)) static inline uint32_t swap32(uint32_t longw)
"swap.b r0, %0 \n\t"
: "=r"(result)
: "r"(longw)
: "r0"
);
return result;
}

View file

@ -15,11 +15,16 @@
#include <stdint.h>
#include <stddef.h>
// This one is defined by the linked and consists in four fields:
// The version symbol is defined through the linker and consists of four
// fields:
// - Version type, an ascii char ('a'lpha, 'b'eta, 'd'ev, 'r'elease), 8 bits
// - Major version number, 4 bits
// - Minor version numer, 4 bits
// - Minor version number, 4 bits
// - Build number, 16 bits
// Please note that the version number is the *ADDRESS* of GINT_VERSION, which
// you definitely want to cast to uint32_t. Evaluating GINT_VERSION is illegal
// (dereferencing a pointer which is actually a four-field version number just
// cannot work) and will certainly crash your program.
extern uint32_t GINT_VERSION;
//---

View file

@ -1,12 +1,3 @@
//---
//
// standard library module: stdio
//
// Handles most input/output for the program. This module does not
// interact with the file system directly.
//
//---
#ifndef _INTERNALS_STDIO_H
#define _INTERNALS_STDIO_H

View file

@ -3,10 +3,11 @@
#include <tales.h>
#include <stdint.h>
#include <stddef.h>
#define OPERATE_ARGS uint32_t *operators, int height, int x, int y
extern struct Font *font;
extern font_t *font;
extern color_t operator;
/*

View file

@ -3,14 +3,14 @@
#include <stdint.h>
// Padding is just empty space, we don't want to give it a name. There's also
// Padding is just empty space, we don't want to give it a type. There's also
// some subtle preprocessor trick to automatically add a (supposedly) unique
// name to each padding member. For instance the substitution may operate as:
// name(__LINE__) -> namesub(78) -> _##78 -> _78
#define namesub(x) _##x
#define name(x) namesub(x)
#define pad(bytes) \
uint8_t name(__LINE__)[bytes] \
uint8_t name(__LINE__)[bytes]
// Fixed-width types for bit field are totally meaningless.
typedef unsigned uint;

View file

@ -7,7 +7,7 @@
// which is stored in a global variable MPU_CURRENT and determined at
// startup.
//
// If you need to do MPU-dependant jobs, use isSH3() or (possibly) isSH4()
// If you need to do MPU-dependent jobs, use isSH3() or (possibly) isSH4()
// instead of testing the value of MPU_CURRENT because these macros take
// care of assuming unknown MPUs are SH4, which is the more reasonable
// option.

View file

@ -10,9 +10,11 @@
#ifndef _SETJMP_H
#define _SETJMP_H
#include <stdint.h>
// There are 16 CPU registers that *must* be saved to ensure a basically
// safe jump.
typedef unsigned int jmp_buf[16];
typedef uint32_t jmp_buf[16];

View file

@ -19,42 +19,43 @@
//---
/*
enum ImageFormat
font_format_t
This type holds information about the characters in the font. Each bit
represents various characters, and the type itself is a combination of
several of those bits.
Bits represent the following characters (lsb right):
-- -- -- non-print | special capitals lower numbers
*/
enum FontFormat
typedef enum
{
FontFormat_Unknown = 0x00,
FontFormat_Numeric = 0x01,
FontFormat_LowerCase = 0x02,
FontFormat_UpperCase = 0x04,
FontFormat_Letters = 0x06,
FontFormat_Common = 0x07,
FontFormat_Print = 0x0f,
FontFormat_Ascii = 0x1f,
};
font_format_unknown = 0x00,
font_format_numeric = 0x01,
font_format_lower = 0x02,
font_format_upper = 0x04,
font_format_letters = 0x06,
font_format_common = 0x07,
font_format_print = 0x0f,
font_format_ascii = 0x1f,
} font_format_t;
/*
struct FontGlyph
font_glyph_t
Holds a glyph's data. The width is used for spacing, and the raw data
is encoded line after line, from to to bottom, by appending bits
without consideration of the byte boundaries.
This structure is actually never used, because data is read directly
as a longword array (hence the 4-byte alignment).
*/
struct FontGlyph
typedef struct
{
unsigned char width;
uint8_t width;
const uint8_t data[];
const unsigned char data[];
} __attribute__((aligned(4)));
} __attribute__((packed, aligned(4))) font_glyph_t;
/*
struct Font
font_t
Holds a font's data. Data is accessed using longword operations, hence
the 4-alignment attributes. The line height is the one given in the
font image header line, which may be used by applications that write
@ -65,13 +66,12 @@ struct FontGlyph
The name field may not be NUL-terminated when the name contains 28
characters. When the name is shorter, the field is padded with zeros.
*/
struct Font
typedef struct
{
unsigned char magic;
unsigned char format;
unsigned char line_height;
unsigned char data_height;
uint8_t magic;
uint8_t format;
uint8_t line_height;
uint8_t data_height;
// Warning : this field may not be NUL-terminated.
char name[28];
@ -80,9 +80,7 @@ struct Font
__attribute__((aligned(4))) const uint32_t glyphs[];
} __attribute__((aligned(4)));
// Useful shorthand for user code.
typedef struct Font Font;
} __attribute__((packed, aligned(4))) font_t;
@ -95,7 +93,7 @@ typedef struct Font Font;
Sets the font and color to use for subsequent text operations. Pass
font = NULL to use the default font.
*/
void text_configure(struct Font *font, color_t operator);
void text_configure(font_t *font, color_t operator);
/*
text_length()

View file

@ -11,14 +11,18 @@
#include <timer.h>
#include <mpu.h>
// Additional video rams used by the gray engine.
static uint32_t internal_vrams[3][256];
static uint32_t *vrams[4];
// Current vram set (0 or 1), delays of the light and dark frames respectively.
static int current = 0;
static int delays[2];
// Is the engine currently running?
static int runs = 0;
// Hardware timer used to run the engine.
static timer_t *gray_timer = NULL;
@ -87,6 +91,9 @@ void gray_stop(void)
timer_stop(gray_timer);
runs = 0;
/* TODO This may not be very wise considering the fact that the user
may have specified another monochrome vram address. This raises again
the idea of a parameter stack. */
display_useVRAM(display_getLocalVRAM());
}

View file

@ -1,24 +0,0 @@
#include <internals/keyboard.h>
/*
getPressedKey()
Finds a pressed key in the keyboard state and returns it.
*/
int getPressedKey(volatile uint8_t *keyboard_state)
{
int row = 1, column = 0;
int state;
if(keyboard_state[0] & 1) return KEY_AC_ON;
while(row <= 9 && !keyboard_state[row]) row++;
if(row > 9) return KEY_NONE;
state = keyboard_state[row];
while(!(state & 1))
{
state >>= 1;
column++;
}
return (column << 4) | row;
}

View file

@ -53,7 +53,7 @@ int getkey_opt(getkey_option_t options, int delay_ms)
break;
case event_key_press:
key = event.key;
key = event.key.code;
if(options & getkey_manage_backlight && key == KEY_OPTN
&& (modifier & MOD_SHIFT))
@ -79,7 +79,7 @@ int getkey_opt(getkey_option_t options, int delay_ms)
return key | modifier;
case event_key_repeat:
key = event.key;
key = event.key.code;
if(key != last_key) continue;
// Checking that this type of repetition is allowed.
@ -98,7 +98,7 @@ int getkey_opt(getkey_option_t options, int delay_ms)
break;
case event_key_release:
if(event.key != last_key) break;
if((int)event.key.code != last_key) break;
last_key = KEY_NONE;
last_repeats = 0;
last_time = 0;

View file

@ -36,27 +36,39 @@ timer_t *vtimer = NULL;
static inline void push_press(int keycode)
{
uint32_t id = key_id(keycode);
event_t event = {
.type = event_key_press,
.key = keycode,
.key.code = keycode,
.key.id = id,
.key.character = key_char(keycode),
};
event_push(event);
}
static inline void push_repeat(int keycode)
{
uint32_t id = key_id(keycode);
event_t event = {
.type = event_key_repeat,
.key = keycode,
.key.code = keycode,
.key.id = id,
.key.character = key_char(keycode),
};
event_push(event);
}
static inline void push_release(int keycode)
{
uint32_t id = key_id(keycode);
event_t event = {
.type = event_key_release,
.key = keycode,
.key.code = keycode,
.key.id = id,
.key.character = key_char(keycode),
};
event_push(event);
}

View file

@ -30,8 +30,8 @@
many keys on the same column are pressed, other keys of the same column
may be triggered.
(The following values do not apply to the latest tests, even if the
trend remains the same.)
(The following values do not apply to the latest tests, but the trend
remains the same.)
- Less Bad key detection.
- 8 Very few column effects. Most often, three keys may be pressed
simultaneously. However, [UP] has latencies and is globally not
@ -63,7 +63,7 @@ static void kdelay(void)
Reads a keyboard row. Works like krow() for SH7705; see source file
keyboard_7705.c for more details.
*/
static int krow(int row)
static uint8_t krow(int row)
{
volatile uint16_t *injector1 = (void *)0xa4050116;
volatile uint8_t *data1 = (void *)0xa4050136;
@ -79,9 +79,9 @@ static int krow(int row)
uint16_t smask;
uint8_t cmask;
int result = 0;
uint8_t result = 0;
if(row < 0 || row > 9) return 0;
if(row < 0 || row > 9) return 0x00;
// Additional configuration for SH7305.
*detector = 0xaaaa;

View file

@ -66,16 +66,16 @@ static void kdelay(void)
krow()
Reads a keyboard row.
*/
static int krow(int row)
static uint8_t krow(int row)
{
// '11' on the active row, '00' everywhere else.
unsigned short smask = 0x0003 << ((row % 8) * 2);
uint16_t smask = 0x0003 << ((row % 8) * 2);
// '0' on the active row, '1' everywhere else.
unsigned char cmask = ~(1 << (row % 8));
uint8_t cmask = ~(1 << (row % 8));
// Line results.
int result = 0;
uint8_t result = 0;
if(row < 0 || row > 9) return 0;
if(row < 0 || row > 9) return 0x00;
// Initial configuration.
PFC.PBCR.WORD = 0xaaaa;

View file

@ -1,6 +1,5 @@
#include <internals/mmu.h>
#pragma GCC diagnostic ignored "-Wunused-but-set-variable"
#include <stdint.h>
/*
mmu_pseudoTLBInit()
@ -15,15 +14,16 @@
*/
void mmu_pseudoTLBInit(void)
{
extern unsigned int romdata;
unsigned int address = 0x00300000;
unsigned int x;
extern uint32_t romdata;
uint32_t limit = (uint32_t)&romdata;
while(address <= (unsigned int)&romdata)
uint32_t address = 0x00300000;
// Access every page to make the system load everything in the TLB (and
// just hope it works and the add-in fits in).
while(address <= limit)
{
x = *((volatile unsigned int *)address);
*((volatile uint32_t *)address);
address += 0x1000;
}
}
#pragma GCC diagnostic pop

View file

@ -1,4 +1,5 @@
#include <screen.h>
#include <stdint.h>
#include <mpu.h>
/*
@ -9,13 +10,13 @@ void screen_setBacklight(int on)
{
if(isSH3())
{
volatile unsigned char *PGDR = (void *)0xa400012c;
volatile uint8_t *PGDR = (void *)0xa400012c;
if(on) *PGDR |= 0x80;
else *PGDR &= ~0x80;
}
else
{
volatile unsigned char *PNDR = (void *)0xa4050138;
volatile uint8_t *PNDR = (void *)0xa4050138;
if(on) *PNDR |= 0x10;
else *PNDR &= ~0x10;
}
@ -29,12 +30,12 @@ void screen_toggleBacklight(void)
{
if(isSH3())
{
volatile unsigned char *PGDR = (void *)0xa400012c;
volatile uint8_t *PGDR = (void *)0xa400012c;
*PGDR ^= 0x80;
}
else
{
volatile unsigned char *PNDR = (void *)0xa4050138;
volatile uint8_t *PNDR = (void *)0xa4050138;
*PNDR ^= 0x10;
}
}

View file

@ -1,4 +1,5 @@
#include <screen.h>
#include <stdint.h>
/*
screen_display()
@ -9,12 +10,11 @@
*/
void screen_display(const void *ptr)
{
const char *vram = (const char *)ptr;
volatile char *selector = (char *)0xb4000000;
volatile char *data = (char *)0xb4010000;
int line, bytes;
volatile uint8_t *selector = (void *)0xb4000000;
volatile uint8_t *data = (void *)0xb4010000;
const uint8_t *vram = ptr;
for(line = 0; line < 64; line++)
for(int line = 0; line < 64; line++)
{
// Setting the x-address register.
*selector = 4;
@ -29,7 +29,27 @@ void screen_display(const void *ptr)
*data = 0;
// Selecting data write register 7 and sending a line's bytes.
// We could loop but I suspect it will be faster to iterate.
*selector = 7;
for(bytes = 0; bytes < 16; bytes++) *data = *vram++;
/* TODO Manually load the video-ram line into the cache? */
*data = *vram++;
*data = *vram++;
*data = *vram++;
*data = *vram++;
*data = *vram++;
*data = *vram++;
*data = *vram++;
*data = *vram++;
*data = *vram++;
*data = *vram++;
*data = *vram++;
*data = *vram++;
*data = *vram++;
*data = *vram++;
*data = *vram++;
*data = *vram++;
}
}

View file

@ -1,22 +1,19 @@
/*
standard library module: setjmp
Long jumps. The register contents are saved in a buffer when setjmp()
is called and restored at any time when longjmp() performs the jump.
This is based on a trick that uses pr ; the user program is resumed
after the setjmp() call when longjmp() is invoked but this is not
setjmp() that returns. longjmp() restores the pr value that was saved
by setjmp() and performs an rts instruction.
setjmp() returns 0 when called to set up the jump point and a non-zero
value when invoked through a long jump. If 0 is given as return value
to longjmp(), then 1 is returned.
*/
.global _setjmp
.global _longjmp
/*
setjmp()
This function is implemented using a trick that changes the value of
pr (so that it points just after the call to setjmp()) within the
longjmp() function, which lets the rts function perform the actual
jump. This value is obtained from the save buffer.
setjmp() returns 0 when called to set up the jump point and a non-zero
value when invoked through a long jump. If 0 is provided as argument to
longjmp(), 1 is returned instead.
*/
_setjmp:
/* Getting some free space. */
add #64, r4
@ -42,8 +39,8 @@ _setjmp:
sts.l pr, @-r4
/* This function always return 0. The cases where setjmp() seems to
return non-zero values, when a long jump has just been performed, is
actually handled by longjmp(). */
return non-zero values, when a long jump has just been performed, are
those when the longjmp() function returns. */
rts
mov #0, r0
@ -52,7 +49,7 @@ _setjmp:
_longjmp:
/* Restoring the system and control registers. Restoring pr is actually
what performs the jump -- and makes the user program think that
setjmp() has returned. */
setjmp() has just returned. */
lds.l @r4+, pr
lds.l @r4+, macl
lds.l @r4+, mach

View file

@ -14,7 +14,6 @@ int snprintf(char *str, size_t size, const char *format, ...)
int x = __printf(size, format, args);
memcpy(str, __stdio_buffer, x + 1);
va_end(args);
return x;
}

View file

@ -9,6 +9,7 @@ void srand(unsigned int new_seed)
int rand(void)
{
/* TODO Or maybe seed = (16807 * seed) % ((1 << 31) - 1); */
seed = seed * 1103515245 + 12345;
return seed & 0x7fffffff;
}

View file

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

View file

@ -4,12 +4,15 @@
void operate_gray(OPERATE_ARGS)
{
size_t vram_offset = (x >> 5) + (y << 2);
uint32_t *light = gray_lightVRAM() + vram_offset;
uint32_t *dark = gray_darkVRAM() + vram_offset;
if(x < 0) return;
int start = (y < 0) ? (-y) : (0);
size_t vram_offset = (x >> 5) + ((y + start) << 2);
uint32_t *light = (uint32_t *)gray_lightVRAM() + vram_offset;
uint32_t *dark = (uint32_t *)gray_darkVRAM() + vram_offset;
uint32_t op, old_light;
for(int i = 0; i < height; i++)
for(int i = start; i < height; i++)
{
op = operators[i];

View file

@ -4,7 +4,7 @@
#include <ctype.h>
#include <gray.h>
struct Font *font;
font_t *font = NULL;
color_t operator;
/*
@ -27,8 +27,8 @@ int getCharacterIndex(int c)
{
const char *data = (const char *)&font->glyphs;
int index, current;
int offset;
int width, bits;
size_t offset;
c &= 0x7f;
@ -36,33 +36,33 @@ int getCharacterIndex(int c)
// Getting the character index in the glyph array.
if(font->format == FontFormat_Ascii) index = c;
else if(font->format == FontFormat_Print) index = c - 32;
if(font->format == font_format_ascii) index = c;
else if(font->format == font_format_print) index = c - 32;
else switch(font->format)
{
case FontFormat_Numeric:
case font_format_numeric:
if(!isdigit(c)) return -1;
index = c - '0';
break;
case FontFormat_LowerCase:
case font_format_lower:
if(!islower(c)) return -1;
index = c - 'a';
break;
case FontFormat_UpperCase:
case font_format_upper:
if(!isupper(c)) return -1;
index = c - 'A';
break;
case FontFormat_Letters:
case font_format_letters:
if(!isalpha(c)) return -1;
index = c - 'A' - ('a' - 'Z') * (c >= 'a');
break;
case FontFormat_Common:
case font_format_common:
if(!isalnum(c)) return -1;
index = c - '0' - ('A' - '9') * (c >= 'A') -
('a' - 'Z') * (c >= 'a');
break;
case FontFormat_Unknown:
case font_format_unknown:
default:
return -1;
}
@ -93,13 +93,16 @@ int getCharacterIndex(int c)
*/
void operate_mono(OPERATE_ARGS)
{
if(x < 0) return;
uint32_t *vram = display_getCurrentVRAM();
uint32_t *video = vram + (x >> 5) + (y << 2);
int start = (y < 0) ? (-y) : (0);
uint32_t *video = vram + (x >> 5) + ((y + start) << 2);
switch(operator)
{
case color_white:
for(int i = 0; i < height; i++)
for(int i = start; i < height; i++)
{
*video &= ~operators[i];
video += 4;
@ -107,7 +110,7 @@ void operate_mono(OPERATE_ARGS)
break;
case color_black:
for(int i = 0; i < height; i++)
for(int i = start; i < height; i++)
{
*video |= operators[i];
video += 4;
@ -115,7 +118,7 @@ void operate_mono(OPERATE_ARGS)
break;
case color_invert:
for(int i = 0; i < height; i++)
for(int i = start; i < height; i++)
{
*video ^= operators[i];
video += 4;
@ -163,7 +166,7 @@ int update(uint32_t *operators, int height, int available, uint32_t *glyph)
shift = 32 - available;
// Getting the next 'width' bits. In some cases these bits will
// intersect two different longs.
// intersect two different longs...
line = data & glyph_mask;
line = (shift >= 0) ? (line >> shift) : (line << -shift);
operators[i] |= line;
@ -171,7 +174,7 @@ int update(uint32_t *operators, int height, int available, uint32_t *glyph)
data <<= width;
bits_available -= width;
// Continue until they do.
// ... continue looping until they do.
if(bits_available >= 0) continue;
// Computing a special mask that extracts just the number of
@ -180,6 +183,10 @@ int update(uint32_t *operators, int height, int available, uint32_t *glyph)
data = glyph[data_index++];
shift += width + bits_available;
// In case this condition is not verified, the program invokes
// undefined behavior because of the bit shift. Anyway, it
// means that the end of the end of the operators was reached,
// in which case the function should not continue writing.
if(shift <= 31)
{
line = data & partial_mask;
@ -202,34 +209,32 @@ int update(uint32_t *operators, int height, int available, uint32_t *glyph)
*/
void render(int x, int y, const char *str, void (*op)(OPERATE_ARGS))
{
if(!font) return;
// Operator data, and number of available bits in the operators (which
// is the same for all operators, since they are treated equally).
uint32_t *operators;
int available;
// Raw glyph data, each glyph being represented by one or several
// Raw glyph data, each glyph being represented as one or several
// longwords, and an index in this array.
uint32_t *data = (uint32_t *)font->glyphs;
int index;
// Height of each glyph. This value is constant because the storage
// format requires it: it allows greater optimization.
int height;
int i;
// Storage height of each glyph. This is a constant for all glyphs
// because the format required it. It makes this routine consequently
// faster.
int height = font->data_height;
if(!font) return;
// Allocating data. There will be one operator for each line.
height = font->data_height;
if(x > 127 || y > 63 || y <= -height) return;
if(y + height > 64) height = 64 - y;
operators = alloca(height * sizeof(uint32_t));
for(i = 0; i < height; i++) operators[i] = 0;
if(!operators) return;
for(int i = 0; i < height; i++) operators[i] = 0;
// Computing the initial operator offset to have 32-aligned operators.
// This allows to write directly video ram longs instead of having to
@ -263,22 +268,23 @@ void render(int x, int y, const char *str, void (*op)(OPERATE_ARGS))
x += 32;
if(x > 96) break;
memset(operators, 0, height << 2);
for(int i = 0; i < height; i++) operators[i] = 0;
if(available >= 0)
{
available = 31 + available;
available += 31;
continue;
}
// Finishing update, in case it has been only partially done,
// because there was not enough bits available to fit all the
// information. Also adding a space, assuming that characters
// aren't more than 30 bits wide.
// Finishing update, in cases where it has been only partially
// done because there was not enough bits available to fit all
// the information. Also adding a space, assuming that
// characters aren't more than 30 bits wide. (=p)
available += 32 + (data[index] >> 24);
available = update(operators, height, available, data + index);
available--;
}
// Final operation.
if(x <= 96 && available < 32) (*op)(operators, height, x, y);
// Final operation. This condition allows a single bit of the operators
// to be used - that's because the loop will add a final spacing pixel.
if(x <= 96 && available < 31) (*op)(operators, height, x, y);
}

View file

@ -1 +1 @@
beta-0.9-305
beta-0.9-349