Added clock frequency computations/measurements, and frequency-delay/timer expression.
4
Makefile
|
@ -12,7 +12,7 @@
|
|||
#---
|
||||
|
||||
# Modules
|
||||
modules-gint = core keyboard mmu mpu rtc screen timer \
|
||||
modules-gint = core clock keyboard mmu mpu rtc screen timer \
|
||||
bopti display gray tales
|
||||
modules-libc = setjmp string stdio stdlib time
|
||||
|
||||
|
@ -173,7 +173,7 @@ mrproper: clean
|
|||
distclean: mrproper
|
||||
|
||||
install:
|
||||
CasioUsbUploader -f $(target-g1a) -w -l 1
|
||||
p7 send -f $(target-g1a)
|
||||
|
||||
|
||||
.PHONY: all clean mrproper distclean
|
||||
|
|
65
TODO
|
@ -1,44 +1,27 @@
|
|||
Bugs to fix:
|
||||
- Left-vram overflow when rendering text
|
||||
- A few key hits ignored after leaving the application (could not reproduce)
|
||||
- Lost keyboard control at startup (could not reproduce)
|
||||
- Back-light issues
|
||||
|
||||
--------------
|
||||
Lots of things to do
|
||||
--------------
|
||||
Simple improvements:
|
||||
- bopti: Monochrome bitmaps blending modes
|
||||
- bopti: Partial transparency
|
||||
- clock: Compute clock frequencies
|
||||
- demo: Try 284x124 at (-60, -28) (all disadvantages)
|
||||
- display: Rectangle-based drawing functions
|
||||
- tales: Test all font encodings
|
||||
- time: Compute CLOCKS_PER_SEC
|
||||
- timer: Add duration and frequency settings
|
||||
- core: Add VBR handlers debugging information (if possible)
|
||||
- core: Implement all callbacks and a complete user API
|
||||
|
||||
@ known bugs
|
||||
+ simple improvements
|
||||
- important milestones
|
||||
~ needs investigation
|
||||
Modules to implement:
|
||||
- Serial communication
|
||||
- Sound playback and synthesizing
|
||||
|
||||
|
||||
@ possibility of vram left-overflow with text
|
||||
@ garbage displayed as year in the clock, possibly during edition (could not
|
||||
reproduce)
|
||||
@ a few key hits ignored after leaving the application
|
||||
|
||||
+ rect functions
|
||||
+ bitmap blending modes
|
||||
+ minimize use of 7705.h and 7305.h; use local structures instead
|
||||
+ partial transparency
|
||||
+ gint vs. ML with 248x124 at (-60, -28)
|
||||
+ call exit handlers
|
||||
+ compute frequencies and CLOCKS_PER_SEC
|
||||
+ test all font encodings
|
||||
|
||||
- improve exception handler debugging information (if possible)
|
||||
- callbacks and complete user API
|
||||
|
||||
~ packed bit fields
|
||||
~ exhaustive save for setjmp()
|
||||
~ registers that need to be saved when configuring gint
|
||||
~ possible bug when -O2'ing __attribute__((interrupt_handler))
|
||||
|
||||
|
||||
|
||||
Some notes
|
||||
----------
|
||||
|
||||
Test cases for bitmap drawing:
|
||||
- 32-alignment
|
||||
- monochrome / gray
|
||||
- small / large
|
||||
- clipping
|
||||
# blending modes
|
||||
Things to investigate:
|
||||
- Packed bit fields alignment
|
||||
- Registers that may need to be saved within setjmp()
|
||||
- Registers that may need to be saved and restored by gint
|
||||
- Possible bug when optimizing __attribute__((interrupt_handler))
|
||||
|
|
|
@ -305,7 +305,7 @@ void main_menu(int *category, int *app)
|
|||
"Image rendering",
|
||||
"Text rendering",
|
||||
"Real-time clock",
|
||||
"Text formatting",
|
||||
"Clocks and timers",
|
||||
NULL
|
||||
};
|
||||
const char *list_perfs[] = {
|
||||
|
@ -511,7 +511,7 @@ int main(void)
|
|||
test_rtc();
|
||||
break;
|
||||
case 0x0106:
|
||||
// test_printf();
|
||||
test_timer();
|
||||
break;
|
||||
|
||||
case 0x0201:
|
||||
|
|
|
@ -76,10 +76,10 @@ void test_tales(void);
|
|||
void test_rtc(void);
|
||||
|
||||
/*
|
||||
test_printf()
|
||||
Some text formatting.
|
||||
test_timer()
|
||||
Clock timer and timer precision.
|
||||
*/
|
||||
void test_printf(void);
|
||||
void test_timer(void);
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -3,12 +3,9 @@
|
|||
output. Note how symbols romdata, bbss, ebss, bdata and edata are used
|
||||
in the initialization routine (crt0.c) to initialize the application.
|
||||
|
||||
Two ram areas are specified. It happens, if I'm not wrong, that the
|
||||
"real ram" is accessed directly while the "common" ram is accessed
|
||||
through the mmu. The interrupt handler resides in "real ram" because it
|
||||
couldn't execute well in ram. While SH7335 and SH7355 had no problems,
|
||||
executing the interrupt handler in the common ram on SH7305-based new
|
||||
models caused trouble to the OS, apparently overwriting ram data.
|
||||
Two ram areas are specified. The "real ram" is accessed direcly while
|
||||
the other area is virtualized. It is not possible to execute code in
|
||||
virtualized ram.
|
||||
*/
|
||||
|
||||
OUTPUT_ARCH(sh3)
|
||||
|
|
BIN
demo/resources/clock_7305.bmp
Normal file
After Width: | Height: | Size: 12 KiB |
BIN
demo/resources/clock_7705.bmp
Normal file
After Width: | Height: | Size: 12 KiB |
BIN
demo/resources/clock_chars.bmp
Normal file
After Width: | Height: | Size: 1.1 KiB |
Before Width: | Height: | Size: 3.1 KiB After Width: | Height: | Size: 3.1 KiB |
BIN
demo/resources/opt_timer.bmp
Normal file
After Width: | Height: | Size: 3.1 KiB |
|
@ -178,7 +178,6 @@ void test_bopti(void)
|
|||
dupdate();
|
||||
}
|
||||
|
||||
leave = 1;
|
||||
do
|
||||
{
|
||||
leave = 1;
|
||||
|
|
|
@ -11,25 +11,30 @@
|
|||
static void draw(int delay1, int delay2, int selected)
|
||||
{
|
||||
extern Image res_opt_gray_start;
|
||||
int *vl = gray_lightVRAM();
|
||||
int *vd = gray_darkVRAM();
|
||||
unsigned int *vl = gray_lightVRAM();
|
||||
unsigned int *vd = gray_darkVRAM();
|
||||
|
||||
gclear();
|
||||
locate(1, 1, "Gray engine");
|
||||
|
||||
for(int i = 0; i < 63; i++)
|
||||
for(int i = 0; i < 36; i++)
|
||||
{
|
||||
int o = (i << 2) + 2;
|
||||
vl[o] = vl[o + 1] = -((i & 31) < 16);
|
||||
vd[o] = vd[o + 1] = -(i < 32);
|
||||
int o = ((i + 12) << 2) + 2;
|
||||
unsigned light = -((i % 24) < 12);
|
||||
unsigned dark = -(i < 24);
|
||||
vl[o] = light >> 8;
|
||||
vl[o + 1] = light << 8;
|
||||
vd[o] = dark >> 8;
|
||||
vd[o + 1] = dark << 8;
|
||||
}
|
||||
|
||||
locate(3, 2, "light");
|
||||
print(3, 3, "%d", delay1);
|
||||
locate(3, 3, "light");
|
||||
print(4, 4, "%d", delay1);
|
||||
|
||||
locate(3, 5, "dark");
|
||||
print(3, 6, "%d", delay2);
|
||||
print(4, 6, "%d", delay2);
|
||||
|
||||
locate(2, selected ? 6 : 3, "\x02");
|
||||
locate(3, selected ? 6 : 4, "\x02");
|
||||
|
||||
gimage(0, 56, &res_opt_gray_start);
|
||||
gupdate();
|
||||
|
@ -64,10 +69,14 @@ void test_gray(void)
|
|||
selected = !selected;
|
||||
break;
|
||||
case KEY_F2:
|
||||
delays[0] = isSH3() ? 985 : 994;
|
||||
delays[1] = 1609;
|
||||
delays[0] = 912;
|
||||
delays[1] = 1343;
|
||||
break;
|
||||
case KEY_F3:
|
||||
delays[0] = 993;
|
||||
delays[1] = 1609;
|
||||
break;
|
||||
case KEY_F4:
|
||||
delays[0] = 860;
|
||||
delays[1] = 1298;
|
||||
break;
|
||||
|
|
|
@ -179,7 +179,7 @@ static void set(void)
|
|||
|
||||
if(n == 7)
|
||||
{
|
||||
int month = k + 4 * slide - 1;
|
||||
int month = k + 4 * slide - 2;
|
||||
set_region(&time, n, month);
|
||||
n++;
|
||||
slide = 0;
|
||||
|
|
284
demo/test_timer.c
Normal file
|
@ -0,0 +1,284 @@
|
|||
#include "gintdemo.h"
|
||||
#include <stddef.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <internals/timer.h>
|
||||
#include <timer.h>
|
||||
#include <display.h>
|
||||
#include <keyboard.h>
|
||||
#include <clock.h>
|
||||
#include <mpu.h>
|
||||
#include <rtc.h>
|
||||
|
||||
static void draw(int new_tab);
|
||||
static struct ClockConfig conf;
|
||||
|
||||
//---
|
||||
// Timer-RTC comparison.
|
||||
// The precision of the timer is measured by comparing it to the RTC.
|
||||
//---
|
||||
|
||||
static volatile int elapsed_timer = -1;
|
||||
static volatile int elapsed_rtc = -1;
|
||||
|
||||
static void timing_rtc(void)
|
||||
{
|
||||
elapsed_rtc++;
|
||||
}
|
||||
|
||||
static void timing_timer(void)
|
||||
{
|
||||
elapsed_timer++;
|
||||
}
|
||||
|
||||
static void timing_start(void)
|
||||
{
|
||||
timer_start(TIMER_USER, clock_setting(16, Clock_Hz), TIMER_Po_4,
|
||||
timing_timer, 0);
|
||||
rtc_setCallback(timing_rtc, RTCFreq_16Hz);
|
||||
|
||||
elapsed_timer = 0;
|
||||
elapsed_rtc = 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
//---
|
||||
// Drawing.
|
||||
//---
|
||||
|
||||
/*
|
||||
small_text()
|
||||
Renders small text using a minimalist bitmap-based font.
|
||||
*/
|
||||
static void small_text(int x, int y, const char *text, int alignment)
|
||||
{
|
||||
extern Image res_clock_chars_start;
|
||||
Image *chars = &res_clock_chars_start;
|
||||
const char *table = "0123456789kMHz*/";
|
||||
|
||||
if(alignment) x -= 2 * strlen(text) - 1, y -= 2;
|
||||
int c;
|
||||
|
||||
while(*text)
|
||||
{
|
||||
const char *ptr = strchr(table, *text++);
|
||||
if(!ptr) continue;
|
||||
c = ptr - table;
|
||||
|
||||
dimage_part(x, y, chars, c << 2, 0, 3, 5);
|
||||
x += 4;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
getFreq()
|
||||
Prints the given frequency in a string on the form:
|
||||
|
||||
332kHz
|
||||
<-><->
|
||||
3 3
|
||||
|
||||
There are 1, 2 or 3 characters for the value, and 2 or 3
|
||||
characters for the unit. The string is compacted.
|
||||
*/
|
||||
void getFreq(char *str, int freq)
|
||||
{
|
||||
if(freq < 1000)
|
||||
{
|
||||
sprintf(str, "%dHz", freq);
|
||||
return;
|
||||
}
|
||||
if(freq < 1000000)
|
||||
{
|
||||
sprintf(str, "%dkHz", (freq + 500) / 1000);
|
||||
return;
|
||||
}
|
||||
sprintf(str, "%dMHz", (freq + 500000) / 1000000);
|
||||
}
|
||||
|
||||
/*
|
||||
dislay_freq()
|
||||
Displays a frequency value a unit, in an simple form.
|
||||
*/
|
||||
static void display_freq(int x, int y, int freq)
|
||||
{
|
||||
int ratio, letter, dot, i;
|
||||
char buffer[10];
|
||||
|
||||
if(freq <= 0)
|
||||
{
|
||||
dtext(x, y, "Unknown");
|
||||
return;
|
||||
}
|
||||
if(freq < 10000)
|
||||
{
|
||||
dprint(x, y, "%5d", freq);
|
||||
small_text(x + 31, y + 2, "Hz", 0);
|
||||
return;
|
||||
}
|
||||
|
||||
if(freq < 10000000) ratio = 1, letter = 'k';
|
||||
else ratio = 1000, letter = 'M';
|
||||
|
||||
dot = 1 + (freq >= 10000 * ratio) + (freq >= 100000 * ratio);
|
||||
freq += (ratio * (1 + 9 * (dot >= 2) + 90 * (dot >= 3))) / 2;
|
||||
snprintf(buffer, 6, "%d", freq);
|
||||
|
||||
for(i = 4; i > dot; i--) buffer[i] = buffer[i - 1];
|
||||
buffer[dot] = '.';
|
||||
|
||||
dprint(x, y, buffer);
|
||||
sprintf(buffer, "%cHz", letter);
|
||||
small_text(x + 31, y + 2, buffer, 0);
|
||||
}
|
||||
|
||||
/*
|
||||
draw()
|
||||
Draws the test interface.
|
||||
*/
|
||||
static void draw(int tab)
|
||||
{
|
||||
extern Image res_opt_timer_start;
|
||||
extern Image res_clock_7705_start;
|
||||
extern Image res_clock_7305_start;
|
||||
|
||||
char buffer[16];
|
||||
|
||||
dclear();
|
||||
dimage(0, 56, &res_opt_timer_start);
|
||||
|
||||
if(!tab)
|
||||
{
|
||||
locate(1, 1, "Clock frequency");
|
||||
dtext(7, 20, "B\x1e");
|
||||
display_freq(24, 20, conf.Bphi_f);
|
||||
dtext(7, 28, "I\x1e");
|
||||
display_freq(24, 28, conf.Iphi_f);
|
||||
dtext(7, 36, "P\x1e");
|
||||
display_freq(24, 36, conf.Pphi_f);
|
||||
|
||||
if(isSH3())
|
||||
{
|
||||
dimage(64, 0, &res_clock_7705_start);
|
||||
|
||||
getFreq(buffer, conf.CKIO_f);
|
||||
small_text(84, 16, buffer, 1);
|
||||
|
||||
sprintf(buffer, "*%d", conf.PLL1);
|
||||
small_text(84, 34, buffer, 1);
|
||||
|
||||
if(conf.Iphi_div1 == 1)
|
||||
dline(85, 43, 99, 43, Color_Black);
|
||||
else
|
||||
{
|
||||
sprintf(buffer, "/%d", conf.Iphi_div1);
|
||||
small_text(89, 41, buffer, 0);
|
||||
}
|
||||
if(conf.Pphi_div1 == 1)
|
||||
dline(85, 50, 99, 50, Color_Black);
|
||||
else
|
||||
{
|
||||
sprintf(buffer, "/%d", conf.Pphi_div1);
|
||||
small_text(89, 48, buffer, 0);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
dimage(64, 0, &res_clock_7305_start);
|
||||
|
||||
getFreq(buffer, conf.RTCCLK_f);
|
||||
small_text(84, 14, buffer, 1);
|
||||
|
||||
sprintf(buffer, "*%d", conf.FLL);
|
||||
small_text(84, 25, buffer, 1);
|
||||
|
||||
sprintf(buffer, "*%d", conf.PLL);
|
||||
small_text(84, 36, buffer, 1);
|
||||
|
||||
sprintf(buffer, "/%d", conf.Bphi_div1);
|
||||
small_text(89, 43, buffer, 0);
|
||||
sprintf(buffer, "/%d", conf.Iphi_div1);
|
||||
small_text(89, 50, buffer, 0);
|
||||
sprintf(buffer, "/%d", conf.Pphi_div1);
|
||||
small_text(89, 57, buffer, 0);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
locate(1, 1, "Timer/RTC comparison");
|
||||
|
||||
locate(2, 3, "Timer");
|
||||
if(elapsed_timer >= 0) print(12, 3, "%04x", elapsed_timer);
|
||||
else locate(12, 3, "...");
|
||||
|
||||
locate(2, 4, "RTC");
|
||||
if(elapsed_rtc >= 0) print(12, 4, "%04x", elapsed_rtc);
|
||||
else locate(12, 4, "...");
|
||||
|
||||
// We define the accuracy of the timer as the square of the
|
||||
// ratio between the two counters.
|
||||
locate(2, 5, "Accuracy");
|
||||
if(elapsed_rtc > 0 && elapsed_timer > 0)
|
||||
{
|
||||
int ratio;
|
||||
if(elapsed_timer <= elapsed_rtc)
|
||||
ratio = (10000 * elapsed_timer) / elapsed_rtc;
|
||||
else
|
||||
ratio = (10000 * elapsed_rtc) / elapsed_timer;
|
||||
ratio = (ratio * ratio) / 10000;
|
||||
|
||||
print(12, 5, "%d.%02d %%", ratio / 100, ratio % 100);
|
||||
}
|
||||
else locate(12, 5, "...");
|
||||
}
|
||||
|
||||
dupdate();
|
||||
}
|
||||
|
||||
|
||||
|
||||
//---
|
||||
// Timer/clock test.
|
||||
//---
|
||||
|
||||
/*
|
||||
test_timer()
|
||||
Clock timer and timer precision.
|
||||
*/
|
||||
void test_timer(void)
|
||||
{
|
||||
int tab = 0;
|
||||
|
||||
clock_measure();
|
||||
clock_measure_end();
|
||||
|
||||
conf = clock_config();
|
||||
|
||||
elapsed_timer = -1;
|
||||
elapsed_rtc = -1;
|
||||
rtc_setCallback(timing_start, RTCFreq_16Hz);
|
||||
|
||||
text_configure(NULL, Color_Black);
|
||||
|
||||
while(1)
|
||||
{
|
||||
draw(tab);
|
||||
|
||||
switch(getkey_opt(Getkey_NoOption, 1))
|
||||
{
|
||||
case KEY_EXIT:
|
||||
timer_stop(TIMER_USER);
|
||||
rtc_setCallback(NULL, RTCFreq_1Hz);
|
||||
return;
|
||||
|
||||
case KEY_F1:
|
||||
tab = 0;
|
||||
break;
|
||||
case KEY_F2:
|
||||
tab = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
123
include/clock.h
Normal file
|
@ -0,0 +1,123 @@
|
|||
//---
|
||||
//
|
||||
// gint core module: clock
|
||||
//
|
||||
// Measures the frequency of the MPU clocks. This module assumes that the
|
||||
// clock mode is 3 on SH7305 (as does FTune).
|
||||
//
|
||||
//---
|
||||
|
||||
//---
|
||||
// Some type declarations.
|
||||
//---
|
||||
|
||||
enum Clock
|
||||
{
|
||||
Clock_CKIO = 0, // SH7705
|
||||
Clock_RTCCLK = 1, // SH7305
|
||||
Clock_Bphi = 2,
|
||||
Clock_Iphi = 3,
|
||||
Clock_Pphi = 4,
|
||||
};
|
||||
|
||||
enum ClockUnit
|
||||
{
|
||||
Clock_us = 0,
|
||||
Clock_ms = 1,
|
||||
Clock_s = 2,
|
||||
|
||||
Clock_Hz = 10,
|
||||
Clock_kHz = 11,
|
||||
Clock_MHz = 12,
|
||||
};
|
||||
|
||||
struct ClockConfig
|
||||
{
|
||||
union
|
||||
{
|
||||
int PLL1; // SH7705
|
||||
int FLL; // SH7305
|
||||
};
|
||||
union
|
||||
{
|
||||
int PLL2; // SH7705
|
||||
int PLL; // SH7305
|
||||
};
|
||||
|
||||
int Bphi_div1;
|
||||
int Iphi_div1;
|
||||
int Pphi_div1;
|
||||
|
||||
union
|
||||
{
|
||||
int CKIO_f; // SH7705
|
||||
int RTCCLK_f; // SH7305
|
||||
};
|
||||
|
||||
int Bphi_f;
|
||||
int Iphi_f;
|
||||
int Pphi_f;
|
||||
};
|
||||
|
||||
//---
|
||||
// Public API.
|
||||
//---
|
||||
|
||||
/*
|
||||
clock_frequency()
|
||||
Returns the approximate frequency, in Hz, of the given clock. The
|
||||
measurements need to have been done. Returns a negative number on
|
||||
error.
|
||||
*/
|
||||
int clock_frequency(enum Clock clock);
|
||||
|
||||
/*
|
||||
clock_setting()
|
||||
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
|
||||
very high frequencies or very short delays will yield important errors.
|
||||
*/
|
||||
int clock_setting(int duration, enum ClockUnit unit);
|
||||
|
||||
/*
|
||||
clock_config()
|
||||
Returns a copy of the clock configuration.
|
||||
*/
|
||||
struct ClockConfig clock_config(void);
|
||||
|
||||
/*
|
||||
sleep()
|
||||
Sleeps until an interrupt is accepted.
|
||||
*/
|
||||
void sleep(void);
|
||||
|
||||
/*
|
||||
sleep_us()
|
||||
Sleeps for the given number of us using the user timer. The result will
|
||||
always be slightly less than required.
|
||||
*/
|
||||
void sleep_us(int us_delay);
|
||||
|
||||
|
||||
|
||||
//---
|
||||
// Internal API.
|
||||
// Referenced for documentation purposes only. Do not use.
|
||||
//---
|
||||
|
||||
/*
|
||||
clock_measure()
|
||||
Begins the frequency measurements. The measurements will end
|
||||
automatically. While doing measurements, do not use the RTC interrupt
|
||||
or the user timer.
|
||||
Call clock_measure_end() to wait until the measurements are finished.
|
||||
It is possible to execute code during the measurements, so that less
|
||||
time is spent.
|
||||
*/
|
||||
void clock_measure(void);
|
||||
|
||||
/*
|
||||
clock_measure_end()
|
||||
Waits until the measurements are finished. This may be immediate.
|
||||
*/
|
||||
void clock_measure_end(void);
|
|
@ -65,11 +65,11 @@ void gray_getDelays(int *light, int *dark);
|
|||
|
||||
values stability stripes colors
|
||||
---------------------------------------------------------
|
||||
860, 1298 excellent worst static good
|
||||
912, 1343 bad none very good
|
||||
993, 1609 medium light fast good (default)
|
||||
860, 1298 excellent worst static good
|
||||
912, 1343 bad none very good (default)
|
||||
993, 1609 medium light fast good
|
||||
1325, 1607 bad light fast excellent
|
||||
---------------------------------------------------------
|
||||
|
||||
*/
|
||||
void gray_setDelays(int light, int dark);
|
||||
|
||||
|
|
|
@ -2,11 +2,20 @@
|
|||
//
|
||||
// gint core module: mpu
|
||||
//
|
||||
// Determines which kind of MPU is running the program. This module only
|
||||
// provides the following test:
|
||||
// Determines which kind of MPU is running the program. This module
|
||||
// provides macro tests isSH3(), isSH4(), and the identifier of the MPU,
|
||||
// which is stored in a global variable MPU_CURRENT.
|
||||
//
|
||||
// if(isSH3()) { ... }
|
||||
// else { ... }
|
||||
// If you need to do MPU-dependant jobs, prefer the following alternative:
|
||||
//
|
||||
// if(isSH3())
|
||||
// {
|
||||
// ...
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// ...
|
||||
// }
|
||||
//
|
||||
//---
|
||||
|
||||
|
|
288
src/clock/clock.c
Normal file
|
@ -0,0 +1,288 @@
|
|||
#include <clock.h>
|
||||
#include <timer.h>
|
||||
#include <internals/timer.h>
|
||||
#include <rtc.h>
|
||||
#include <stddef.h>
|
||||
#include <mpu.h>
|
||||
|
||||
static struct ClockConfig conf = {
|
||||
.FLL = -1, .PLL = -1,
|
||||
.Bphi_div1 = -1, .Iphi_div1 = -1, .Pphi_div1 = -1,
|
||||
.CKIO_f = -1,
|
||||
.Bphi_f = -1, .Iphi_f = -1, .Pphi_f = -1
|
||||
};
|
||||
|
||||
/*
|
||||
clock_frequency()
|
||||
Returns the approximate frequency, in Hz, of the given clock. The
|
||||
measurements need to have been done. Returns a negative number on
|
||||
error.
|
||||
*/
|
||||
int clock_frequency(enum Clock clock)
|
||||
{
|
||||
switch(clock)
|
||||
{
|
||||
case Clock_CKIO:
|
||||
return conf.CKIO_f;
|
||||
case Clock_RTCCLK:
|
||||
return conf.RTCCLK_f;
|
||||
case Clock_Bphi:
|
||||
return conf.Bphi_f;
|
||||
case Clock_Iphi:
|
||||
return conf.Iphi_f;
|
||||
case Clock_Pphi:
|
||||
return conf.Pphi_f;
|
||||
|
||||
default:
|
||||
return -2;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
clock_setting()
|
||||
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
|
||||
very high frequencies or very short delays will yield important errors.
|
||||
*/
|
||||
int clock_setting(int duration, enum ClockUnit unit)
|
||||
{
|
||||
if(conf.Pphi_f <= 0) return -1;
|
||||
int f = conf.Pphi_f >> 2;
|
||||
|
||||
switch(unit)
|
||||
{
|
||||
case Clock_us:
|
||||
return (duration * f) / 1000000;
|
||||
case Clock_ms:
|
||||
return (duration * f) / 1000;
|
||||
case Clock_s:
|
||||
return (duration * f);
|
||||
|
||||
case Clock_Hz:
|
||||
return f / duration;
|
||||
case Clock_kHz:
|
||||
return f / (duration * 1000);
|
||||
case Clock_MHz:
|
||||
return f / (duration * 1000000);
|
||||
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
clock_config()
|
||||
Returns a copy of the clock configuration.
|
||||
*/
|
||||
struct ClockConfig clock_config(void)
|
||||
{
|
||||
return conf;
|
||||
}
|
||||
|
||||
/*
|
||||
sleep()
|
||||
Sleeps until an interrupt is accepted.
|
||||
*/
|
||||
void sleep(void)
|
||||
{
|
||||
__asm__(
|
||||
"sleep\n\t"
|
||||
);
|
||||
}
|
||||
|
||||
/*
|
||||
sleep_us()
|
||||
Sleeps for the given number of us using the user timer. The result will
|
||||
always be slightly less than required.
|
||||
*/
|
||||
|
||||
static volatile int sleep_us_done = 0;
|
||||
|
||||
static void sleep_us_callback(void)
|
||||
{
|
||||
sleep_us_done = 1;
|
||||
}
|
||||
|
||||
void sleep_us(int us_delay)
|
||||
{
|
||||
sleep_us_done = 0;
|
||||
timer_start(TIMER_USER, clock_setting(us_delay, Clock_us), TIMER_Po_4,
|
||||
sleep_us_callback, 1);
|
||||
do sleep();
|
||||
while(!sleep_us_done);
|
||||
}
|
||||
|
||||
|
||||
|
||||
//---
|
||||
// Clock frequency measurements -- Public API.
|
||||
//---
|
||||
|
||||
// Indicates whether the measurements are finished.
|
||||
static volatile int clock_measure_done = 0;
|
||||
// Once again SH7705 and SH7305 need different methods...
|
||||
static void clock_measure_7705();
|
||||
static void clock_compute_7305();
|
||||
|
||||
/*
|
||||
clock_measure()
|
||||
Measures or computes the clock frequencies.
|
||||
*/
|
||||
void clock_measure(void)
|
||||
{
|
||||
// On SH7705 we cannot have the value of CKIO simply, so we measure
|
||||
// P_phi using a timer/RTC combination, and we deduce CKIO.
|
||||
if(isSH3())
|
||||
{
|
||||
// We prepare the timer manually, without starting it, so that
|
||||
// we only have to push the running bit to start it when the
|
||||
// measurements begin. This might look of little effect but the
|
||||
// timer configuration time (lost from the measurement) would
|
||||
// make the precision no more than 97-98%.
|
||||
volatile struct mod_tmu *tmu;
|
||||
timer_get(TIMER_USER, &tmu, NULL);
|
||||
|
||||
tmu->TCOR = 0xffffffff;
|
||||
tmu->TCNT = tmu->TCOR;
|
||||
tmu->TCR.TPSC = TIMER_Po_4;
|
||||
|
||||
tmu->TCR.UNF = 0;
|
||||
tmu->TCR.UNIE = 1;
|
||||
tmu->TCR.CKEG = 0;
|
||||
|
||||
timers[TIMER_USER].callback = NULL;
|
||||
timers[TIMER_USER].repeats = 0;
|
||||
|
||||
rtc_setCallback(clock_measure_7705, RTCFreq_256Hz);
|
||||
}
|
||||
|
||||
// On SH7305, assuming clock mode 3, we can compute the clock
|
||||
// frequencies because we know that RTC_CLK oscillates at 32768 Hz.
|
||||
else
|
||||
{
|
||||
clock_compute_7305();
|
||||
clock_measure_done = 1;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
clock_measure_end()
|
||||
Waits until the measurements are finished. This may be immediate.
|
||||
*/
|
||||
void clock_measure_end(void)
|
||||
{
|
||||
while(!clock_measure_done) sleep();
|
||||
}
|
||||
|
||||
//---
|
||||
// Clock frequency measurements -- SH7305.
|
||||
//---
|
||||
|
||||
/*
|
||||
clock_compute_7305()
|
||||
Computes the clock frequencies according to the CPG parameters.
|
||||
*/
|
||||
static void clock_compute_7305(void)
|
||||
{
|
||||
volatile unsigned int *FRQCRA = (void *)0xa4150000;
|
||||
volatile unsigned int *PLLCR = (void *)0xa4150024;
|
||||
volatile unsigned int *FLLFRQ = (void *)0xa4150050;
|
||||
|
||||
// Surely the documentation of SH7724 does not meet the specification
|
||||
// if SH7305 for the PLL setting, because the register accepts other
|
||||
// values than the ones specified by SH7724. The formula given by
|
||||
// Sentaro21 (thanks again!) yields good results though.
|
||||
int pll = (*FRQCRA >> 24) & 0x3f; // Raw setting
|
||||
pll = pll + 1; // Resulting multiplier
|
||||
conf.PLL = pll;
|
||||
|
||||
// This one is simpler. The FLL ratio is actually the setting value.
|
||||
int fll = *FLLFRQ & 0x7ff; // Raw setting = multiplier
|
||||
if(*FLLFRQ & (1 << 14)) fll >>= 1; // Halve-output flag
|
||||
conf.FLL = fll;
|
||||
|
||||
// The divider1 ratios are NOT those of SH7724. The relation between
|
||||
// the values below and the divider ratios is given by Sentaro21
|
||||
// (thanks to him!) and satisfies ratio = 1 / (2 ** (setting + 1)).
|
||||
int div1_bphi = (*FRQCRA >> 8) & 0xf;
|
||||
int div1_iphi = (*FRQCRA >> 20) & 0xf;
|
||||
int div1_pphi = (*FRQCRA ) & 0xf;
|
||||
|
||||
conf.Bphi_div1 = 1 << (div1_bphi + 1);
|
||||
conf.Iphi_div1 = 1 << (div1_iphi + 1);
|
||||
conf.Pphi_div1 = 1 << (div1_pphi + 1);
|
||||
|
||||
// Computing the frequency of the signal, which is input to divider 1.
|
||||
int base = 32768;
|
||||
if(*PLLCR & (1 << 12)) base *= fll;
|
||||
if(*PLLCR & (1 << 14)) base *= pll;
|
||||
|
||||
conf.RTCCLK_f = 32768;
|
||||
conf.Bphi_f = base >> (div1_bphi + 1);
|
||||
conf.Iphi_f = base >> (div1_iphi + 1);
|
||||
conf.Pphi_f = base >> (div1_pphi + 1);
|
||||
}
|
||||
|
||||
//---
|
||||
// Clock frequency measurements -- SH7705.
|
||||
//---
|
||||
|
||||
/*
|
||||
clock_measure_7705_finalize()
|
||||
Given the number of P_phi / 4 timer ticks elapsed between two RTC
|
||||
256 Hz interrupts, determines the clock configuration.
|
||||
*/
|
||||
static void clock_measure_7705_finalize(int elapsed)
|
||||
{
|
||||
volatile unsigned int *FRQCR = (void *)0xffffff80;
|
||||
|
||||
conf.Pphi_f = elapsed * 4 * 256;
|
||||
if(conf.Pphi_f <= 0) return;
|
||||
|
||||
conf.PLL1 = ((*FRQCR >> 8) & 0x03) + 1;
|
||||
conf.PLL2 = -1;
|
||||
|
||||
conf.Bphi_div1 = 0;
|
||||
conf.Iphi_div1 = ((*FRQCR >> 4) & 0x03) + 1;
|
||||
conf.Pphi_div1 = ((*FRQCR ) & 0x03) + 1;
|
||||
|
||||
conf.CKIO_f = (conf.Pphi_f * conf.Pphi_div1) / conf.PLL1;
|
||||
conf.Bphi_f = conf.CKIO_f;
|
||||
conf.Iphi_f = (conf.CKIO_f * conf.PLL1) / conf.Iphi_div1;
|
||||
}
|
||||
|
||||
/*
|
||||
clock_measure_7705_callback()
|
||||
Starts measurements. Measurements will end automatically. Do not use
|
||||
RTC interrupt or the user timer will doing measurements.
|
||||
Call clock_measure_end() when you need to use those, to ensure
|
||||
measurements are finished.
|
||||
*/
|
||||
static void clock_measure_7705_callback(void)
|
||||
{
|
||||
timer_stop(TIMER_USER);
|
||||
rtc_setCallback(NULL, RTCFreq_1Hz);
|
||||
|
||||
volatile struct mod_tmu *tmu;
|
||||
timer_get(TIMER_USER, &tmu, NULL);
|
||||
int elapsed = 0xffffffff - tmu->TCNT;
|
||||
|
||||
clock_measure_7705_finalize(elapsed);
|
||||
clock_measure_done = 1;
|
||||
}
|
||||
|
||||
/*
|
||||
clock_measure_7705()
|
||||
Programs the clock measurements. We need to have the user timer and the
|
||||
RTC synchronized for this operation, so we wait for an RTC interrupt
|
||||
and we prepare the timer beforehand to avoid losing processor time in
|
||||
configuring the registers.
|
||||
*/
|
||||
static void clock_measure_7705(void)
|
||||
{
|
||||
volatile unsigned char *tstr;
|
||||
timer_get(TIMER_USER, NULL, &tstr);
|
||||
|
||||
*tstr |= (1 << TIMER_USER);
|
||||
rtc_setCallback(clock_measure_7705_callback, RTCFreq_256Hz);
|
||||
}
|
|
@ -3,6 +3,7 @@
|
|||
#include <setjmp.h>
|
||||
|
||||
#include <gint.h>
|
||||
#include <clock.h>
|
||||
#include <internals/mmu.h>
|
||||
|
||||
void __Hmem_SetMMU(unsigned int, unsigned int, int);
|
||||
|
@ -57,8 +58,14 @@ int start(void)
|
|||
|
||||
mmu_pseudoTLBInit();
|
||||
|
||||
// Initializing everything.
|
||||
// Initializing gint.
|
||||
gint_init();
|
||||
|
||||
// Measure clock frequencies.
|
||||
clock_measure();
|
||||
clock_measure_end();
|
||||
|
||||
// Calling global constructors.
|
||||
init();
|
||||
|
||||
|
||||
|
|
|
@ -113,7 +113,7 @@ void gint_quit(void)
|
|||
#define print(str, x, y) dtext(6 * (x) - 5, 8 * (y) - 7, str)
|
||||
#define hexdigit(n) ((n) + '0' + 39 * ((n) > 9))
|
||||
|
||||
void hex(unsigned int x, int digits, char *str)
|
||||
static void hex(unsigned int x, int digits, char *str)
|
||||
{
|
||||
str[0] = '0';
|
||||
str[1] = 'x';
|
||||
|
@ -127,6 +127,13 @@ void hex(unsigned int x, int digits, char *str)
|
|||
}
|
||||
}
|
||||
|
||||
static void reverse(void)
|
||||
{
|
||||
int *vram = display_getCurrentVRAM();
|
||||
int i;
|
||||
for(i = 0; i < 36; i++) vram[i] = ~vram[i];
|
||||
}
|
||||
|
||||
/*
|
||||
gint_exc()
|
||||
Handles exceptions.
|
||||
|
@ -144,7 +151,7 @@ void gint_exc(void)
|
|||
|
||||
dclear();
|
||||
print("Exception raised!", 3, 1);
|
||||
dreverse_area(0, 0, 127, 8);
|
||||
reverse();
|
||||
print(gint_strerror(0), 2, 3);
|
||||
|
||||
print("expevt", 2, 4);
|
||||
|
@ -181,7 +188,7 @@ void gint_tlb(void)
|
|||
|
||||
dclear();
|
||||
print("TLB error!", 6, 1);
|
||||
dreverse_area(0, 0, 127, 8);
|
||||
reverse();
|
||||
print(gint_strerror(1), 2, 3);
|
||||
|
||||
print("expevt", 2, 4);
|
||||
|
|
|
@ -7,7 +7,6 @@
|
|||
|
||||
.global ___GLibAddinAplExecutionCheck
|
||||
.global _malloc
|
||||
.global _calloc
|
||||
.global _free
|
||||
|
||||
|
||||
|
|
Before Width: | Height: | Size: 32 KiB After Width: | Height: | Size: 32 KiB |
|
@ -152,6 +152,6 @@ void gray_init(void)
|
|||
vrams[2] = (const void *)internal_vrams[1];
|
||||
vrams[3] = (const void *)internal_vrams[2];
|
||||
|
||||
delays[0] = isSH3() ? 985 : 994;
|
||||
delays[1] = 1609;
|
||||
delays[0] = 912;
|
||||
delays[1] = 1343;
|
||||
}
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
#include <internals/keyboard.h>
|
||||
#include <keyboard.h>
|
||||
#include <clock.h>
|
||||
#include <timer.h>
|
||||
|
||||
/*
|
||||
keyboard_setFrequency()
|
||||
|
@ -7,6 +9,9 @@
|
|||
*/
|
||||
void keyboard_setFrequency(int frequency)
|
||||
{
|
||||
int setting = clock_setting(frequency, Clock_Hz);
|
||||
if(setting <= 0) return;
|
||||
timer_reload(TIMER_KEYBOARD, setting);
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
#include <keyboard.h>
|
||||
#include <timer.h>
|
||||
#include <mpu.h>
|
||||
#include <clock.h>
|
||||
|
||||
#include <internals/keyboard.h>
|
||||
|
||||
|
@ -42,7 +43,8 @@ void keyboard_interrupt(void)
|
|||
*/
|
||||
void keyboard_init(void)
|
||||
{
|
||||
timer_start(TIMER_KEYBOARD, 1700, TIMER_Po_256, keyboard_interrupt, 0);
|
||||
int delay = clock_setting(16, Clock_Hz);
|
||||
timer_start(TIMER_KEYBOARD, delay, TIMER_Po_4, keyboard_interrupt, 0);
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
|
@ -1,13 +0,0 @@
|
|||
#include <internals/keyboard.h>
|
||||
|
||||
/*
|
||||
sleep()
|
||||
Puts the CPU to sleep and waits for an interrupt.
|
||||
*/
|
||||
void sleep(void)
|
||||
{
|
||||
__asm__
|
||||
(
|
||||
"sleep\n\t"
|
||||
);
|
||||
}
|
|
@ -1,5 +1,7 @@
|
|||
#include <internals/mmu.h>
|
||||
|
||||
#pragma GCC diagnostic ignored "-Wunused-but-set-variable"
|
||||
|
||||
/*
|
||||
mmu_pseudoTLBInit()
|
||||
We need the system to fill the TLB for us, so that it knows what
|
||||
|
@ -23,3 +25,5 @@ void mmu_pseudoTLBInit(void)
|
|||
address += 0x1000;
|
||||
}
|
||||
}
|
||||
|
||||
#pragma GCC diagnostic pop
|
||||
|
|
|
@ -12,7 +12,8 @@ int snprintf(char *str, size_t size, const char *format, ...)
|
|||
va_start(args, format);
|
||||
|
||||
int x = __printf(size, format, args);
|
||||
strncpy(str, __stdio_buffer, size);
|
||||
memcpy(str, __stdio_buffer, x + 1);
|
||||
|
||||
|
||||
va_end(args);
|
||||
return x;
|
||||
|
|
|
@ -12,7 +12,7 @@ int sprintf(char *str, const char *format, ...)
|
|||
va_start(args, format);
|
||||
|
||||
int x = __printf(0, format, args);
|
||||
strncpy(str, __stdio_buffer, __stdio_buffer_size);
|
||||
memcpy(str, __stdio_buffer, x + 1);
|
||||
|
||||
va_end(args);
|
||||
return x;
|
||||
|
|
|
@ -401,8 +401,7 @@ int __printf(size_t size, const char *string, va_list args)
|
|||
struct Format format;
|
||||
|
||||
// Avoiding overflow by adjusting the size argument.
|
||||
if(!size || size > __stdio_buffer_size)
|
||||
size = __stdio_buffer_size;
|
||||
if(!size || size > __stdio_buffer_size) size = __stdio_buffer_size;
|
||||
|
||||
// Initializing character() variables.
|
||||
written = 0;
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
int vsnprintf(char *str, size_t size, const char *format, va_list args)
|
||||
{
|
||||
int x = __printf(size, format, args);
|
||||
strncpy(str, __stdio_buffer, size);
|
||||
memcpy(str, __stdio_buffer, x + 1);
|
||||
|
||||
return x;
|
||||
}
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
int vsprintf(char *str, const char *format, va_list args)
|
||||
{
|
||||
int x = __printf(0, format, args);
|
||||
strncpy(str, __stdio_buffer, __stdio_buffer_size);
|
||||
memcpy(str, __stdio_buffer, x + 1);
|
||||
|
||||
return x;
|
||||
}
|
||||
|
|
|
@ -1,4 +0,0 @@
|
|||
#include <internals/tales.h>
|
||||
#include <internals/stdio.h>
|
||||
#include <stdarg.h>
|
||||
|
45
src/tales/tales_gray.c
Normal file
|
@ -0,0 +1,45 @@
|
|||
#include <internals/tales.h>
|
||||
#include <display.h>
|
||||
#include <gray.h>
|
||||
|
||||
void operate_gray(OPERATE_ARGS)
|
||||
{
|
||||
int *vl = gray_lightVRAM();
|
||||
int *vd = gray_darkVRAM();
|
||||
int vram_offset = (x >> 5) + (y << 2);
|
||||
uint32_t op;
|
||||
int i;
|
||||
|
||||
for(i = 0; i < height; i++)
|
||||
{
|
||||
op = operators[i];
|
||||
|
||||
switch(color)
|
||||
{
|
||||
case Color_White:
|
||||
vl[vram_offset] &= ~op;
|
||||
vd[vram_offset] &= ~op;
|
||||
break;
|
||||
case Color_Light:
|
||||
vl[vram_offset] |= op;
|
||||
vd[vram_offset] &= ~op;
|
||||
break;
|
||||
case Color_Dark:
|
||||
vl[vram_offset] &= ~op;
|
||||
vd[vram_offset] |= op;
|
||||
break;
|
||||
case Color_Black:
|
||||
vl[vram_offset] |= op;
|
||||
vd[vram_offset] |= op;
|
||||
break;
|
||||
case Color_Invert:
|
||||
vl[vram_offset] ^= op;
|
||||
vd[vram_offset] ^= op;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
vram_offset += 4;
|
||||
}
|
||||
}
|
|
@ -120,47 +120,6 @@ void operate_mono(OPERATE_ARGS)
|
|||
vram_offset += 4;
|
||||
}
|
||||
}
|
||||
void operate_gray(OPERATE_ARGS)
|
||||
{
|
||||
int *vl = gray_lightVRAM();
|
||||
int *vd = gray_darkVRAM();
|
||||
int vram_offset = (x >> 5) + (y << 2);
|
||||
uint32_t op;
|
||||
int i;
|
||||
|
||||
for(i = 0; i < height; i++)
|
||||
{
|
||||
op = operators[i];
|
||||
|
||||
switch(color)
|
||||
{
|
||||
case Color_White:
|
||||
vl[vram_offset] &= ~op;
|
||||
vd[vram_offset] &= ~op;
|
||||
break;
|
||||
case Color_Light:
|
||||
vl[vram_offset] |= op;
|
||||
vd[vram_offset] &= ~op;
|
||||
break;
|
||||
case Color_Dark:
|
||||
vl[vram_offset] &= ~op;
|
||||
vd[vram_offset] |= op;
|
||||
break;
|
||||
case Color_Black:
|
||||
vl[vram_offset] |= op;
|
||||
vd[vram_offset] |= op;
|
||||
break;
|
||||
case Color_Invert:
|
||||
vl[vram_offset] ^= op;
|
||||
vd[vram_offset] ^= op;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
vram_offset += 4;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
update()
|
||||
|
|
|
@ -1,9 +1,6 @@
|
|||
#include <internals/tales.h>
|
||||
#include <tales.h>
|
||||
|
||||
#include <display.h>
|
||||
#include <gray.h>
|
||||
|
||||
/*
|
||||
dtext()
|
||||
Prints the given string, without any analysis.
|
||||
|
|