mirror of
https://git.planet-casio.com/Lephenixnoir/gint.git
synced 2024-12-29 13:03:36 +01:00
Startup logs instead of diagnostics, created .gint_bss, updated demo.
This commit is contained in:
parent
5e66efc560
commit
f6133c26fc
45 changed files with 689 additions and 1710 deletions
41
Makefile
41
Makefile
|
@ -11,7 +11,7 @@ include Makefile.cfg
|
|||
#---
|
||||
|
||||
# Modules
|
||||
modules-gint = bopti clock core display events gray keyboard mmu rtc \
|
||||
modules-gint = bopti clock core display events gray init keyboard mmu rtc \
|
||||
screen tales timer
|
||||
modules-libc = ctype setjmp stdio stdlib string time
|
||||
|
||||
|
@ -19,7 +19,6 @@ modules-libc = ctype setjmp stdio stdlib string time
|
|||
target-lib = libgint.a
|
||||
target-std = libc.a
|
||||
target-g1a = gintdemo.g1a
|
||||
target-dbg = gintdbg.g1a
|
||||
|
||||
# Tools
|
||||
cc = sh3eb-elf-gcc
|
||||
|
@ -44,17 +43,6 @@ demo-ldflags = $(demo-cflags) -T demo/gintdemo.ld -L. -lgint -lc -lgcc
|
|||
demo-cflags = -m3 -mb -nostdlib -I include -ffreestanding -std=c11 -Os \
|
||||
-Wall -Wextra
|
||||
|
||||
# Debugger application (displays past diagnostics without running gint)
|
||||
debug-src = $(notdir $(wildcard debug/*.[cs]))
|
||||
debug-dep = $(wildcard debug/*.h)
|
||||
debug-icon = debug/icon.bmp
|
||||
debug-obj = $(debug-src:%=build/debug_%.o)
|
||||
debug-elf = build/gintdbg.elf
|
||||
debug-bin = build/gintdbg.bin
|
||||
debug-ldflags = $(debug-cflags) -T debug/addin.ld -L debug -lfx -lgcc
|
||||
debug-cflags = -m3 -mb -nostdlib -ffreestanding -I debug/include -I \
|
||||
include -std=c11 -Os -Wall -Wextra
|
||||
|
||||
# Specific objects
|
||||
obj-lib-spec = build/display_font_system.bmp.o
|
||||
obj-std-spec =
|
||||
|
@ -159,7 +147,7 @@ debug-ldflags += -Wl,--defsym,_GINT_VERSION=$(version_symbol)
|
|||
|
||||
all-lib: $(config) $(target-std) $(target-lib)
|
||||
|
||||
all: $(config) $(target-std) $(target-lib) $(target-g1a) $(target-dbg)
|
||||
all: $(config) $(target-std) $(target-lib) $(target-g1a)
|
||||
|
||||
build:
|
||||
$(if $(VERBOSE),,@ printf '\e[35;1mdir \u00bb\e[0m mkdir $@\n')
|
||||
|
@ -196,17 +184,6 @@ $(target-g1a): $(config) $(target-std) $(target-lib) $(demo-obj)
|
|||
@ printf $$(stat -c %s $@)
|
||||
@ printf ' bytes)\n\n'
|
||||
|
||||
$(target-dbg): $(config) $(debug-obj)
|
||||
$(if $(VERBOSE),,@ printf '\e[35;1mexe \u00bb\e[0m ld -o $(debug-elf)\n')
|
||||
$(if $(VERBOSE),,@) $(cc) -o $(debug-elf) $(debug-obj) $(debug-ldflags)
|
||||
$(if $(VERBOSE),,@ printf '\e[35;1mexe \u00bb\e[0m objcopy -o $(debug-bin)\n')
|
||||
$(if $(VERBOSE),,@) $(ob) -R .comment -R .bss -O binary $(debug-elf) $(debug-bin)
|
||||
$(if $(VERBOSE),,@ printf '\e[35;1mexe \u00bb\e[0m g1a-wrapper -o $@\n')
|
||||
$(if $(VERBOSE),,@) $(wr) $(debug-bin) -o $@ -i $(debug-icon)
|
||||
@ printf '\e[32;1mmsg \u00bb\e[0m Succesfully built debug application ('
|
||||
@ printf $$(stat -c %s $@)
|
||||
@ printf ' bytes)\n\n'
|
||||
|
||||
# Automated rules
|
||||
|
||||
$(foreach mod,$(modules), \
|
||||
|
@ -244,16 +221,6 @@ build/demo_%.bmp.o: demo/resources/%.bmp
|
|||
$(if $(VERBOSE),,@ printf '\e[36;1mres \u00bb\e[0m fxconv $<\n')
|
||||
$(if $(VERBOSE),,@) fxconv $< -o $@ -n $(patsubst demo/resources/%.bmp,res_%,$<)
|
||||
|
||||
# Debug application
|
||||
|
||||
build/debug_%.s.o: debug/%.s $(config)
|
||||
$(if $(VERBOSE),,@ printf '\e[34;1msrc \u00bb\e[0m as $<\n')
|
||||
$(if $(VERBOSE),,@) $(as) -c $< -o $@
|
||||
|
||||
build/debug_%.c.o: debug/%.c $(hdr-dep) $(debug-dep) $(config)
|
||||
$(if $(VERBOSE),,@ printf '\e[34;1msrc \u00bb\e[0m cc $<\n')
|
||||
$(if $(VERBOSE),,@) $(cc) -c $< -o $@ $(debug-cflags)
|
||||
|
||||
|
||||
|
||||
#---
|
||||
|
@ -282,9 +249,7 @@ endif
|
|||
|
||||
install-demo: all
|
||||
p7 send -f $(target-g1a)
|
||||
install-debug: all
|
||||
p7 send -f $(target-dbg)
|
||||
|
||||
.PHONY: all-lib all help
|
||||
.PHONY: clean mrproper distclean
|
||||
.PHONY: install install-demo install-debug
|
||||
.PHONY: install install-demo
|
||||
|
|
14
TODO
14
TODO
|
@ -1,10 +1,11 @@
|
|||
Bugs to fix:
|
||||
- A few key hits ignored after leaving the application (could not reproduce)
|
||||
- Lost keyboard control at startup (could not reproduce)
|
||||
- Alignment of ALL .data / .rodata files is required to ensure converted data
|
||||
is properly aligned
|
||||
- Unpreditcable crap happens when more than 10 keys are pressed simultaneously
|
||||
- Ensure heap data is freed when a task-switch results in leaving the app
|
||||
|
||||
Things to do before 1.0:
|
||||
- init: Move qdiv10() somewhere else
|
||||
- bopti: Test partial transparency
|
||||
- demo: Try 284x124 at (-60, -28) (all disadvantages)
|
||||
- project: Check size of *all* library structures
|
||||
|
@ -33,8 +34,15 @@ Things to do later:
|
|||
- usb: Implement a driver
|
||||
|
||||
Things to investigate:
|
||||
- Packed bit fields alignment
|
||||
- Registers that may need to be saved within setjmp()
|
||||
- Optimizing core/gint.c leads to raising of an illegal slot exception when
|
||||
running the interrupt handler, although it ends on rte; lds.l @r15+, mach,
|
||||
which is totally not an illegal slot.
|
||||
|
||||
Possibly useful modules:
|
||||
- DMAC
|
||||
- SCIF, SCIFA
|
||||
- TPU
|
||||
- USB
|
||||
- CMT on SH7305, WDT on SH7705
|
||||
- ACD (SH7705 only)
|
||||
|
|
20
configure
vendored
20
configure
vendored
|
@ -7,9 +7,10 @@
|
|||
declare -A conf
|
||||
|
||||
# Behavior
|
||||
conf[GINT_DIAGNOSTICS]=
|
||||
conf[GINT_STARTUP_LOG]=
|
||||
conf[GINT_NO_SYSCALLS]=
|
||||
conf[GINT_EXTENDED_LIBC]=
|
||||
conf[GINT_STATIC_GRAY]=
|
||||
|
||||
# Size limits
|
||||
conf[ATEXIT_MAX]=16
|
||||
|
@ -37,15 +38,18 @@ help()
|
|||
Configuration script for the gint library.
|
||||
|
||||
Options that affect the behavior of the library:
|
||||
$Cr--diagnostics $Cg[default:$Cp false$Cg]$C0
|
||||
Use gint in debug mode, where the library outputs some diagnostics in
|
||||
memory or briefly on screen to diagnose incompatibilites or crashes.
|
||||
$Cr--startup-log $Cg[default:$Cp false$Cg]$C0
|
||||
Enable a on-screen log at startup if a key is kept pressed while launching
|
||||
the add-in, allowing easy debug and crash diagnoses.
|
||||
$Cr--no-syscalls $Cg[default:$Cp false$Cg]$C0
|
||||
Never use syscalls. Expect some trouble with the malloc() function... do
|
||||
Never use syscalls. Expect trouble with malloc() and the gray engine. Do
|
||||
not trigger this switch unless you know what you are doing.
|
||||
$Cr--extended-libc $Cg[default:$Cp false$Cg]$C0
|
||||
Enable specific C99 headers/features that are normally not required by
|
||||
calculator programs. This may allow porting programs from other platforms.
|
||||
$Cr--static-gray-engine $Cg[default:$Cp false$Cg]$C0
|
||||
Place the gray engine vram in static ram instead of using the heap. Always
|
||||
use this option when using both the gray engine and --no-syscalls.
|
||||
|
||||
Options that customize size limits:
|
||||
$Cr--atexit-max$C0=$Cy<integer>$Cg [default:$Cp 16$Cg]$C0
|
||||
|
@ -66,9 +70,10 @@ EOF
|
|||
fail=false
|
||||
for arg; do case "$arg" in
|
||||
-h | --help) help;;
|
||||
--diagnostics) conf[GINT_DIAGNOSTICS]=true;;
|
||||
--startup-log) conf[GINT_STARTUP_LOG]=true;;
|
||||
--no-syscalls) conf[GINT_NO_SYSCALLS]=true;;
|
||||
--extended-libc) conf[GINT_EXTENDED_LIBC]=true;;
|
||||
--static-gray-engine) conf[GINT_STATIC_GRAY]=true;;
|
||||
|
||||
--atexit-max=*)
|
||||
size=${arg#*=}
|
||||
|
@ -103,9 +108,10 @@ esac; done
|
|||
|
||||
output_config_gcc()
|
||||
{
|
||||
[ "${conf[GINT_DIAGNOSTICS]}" != "" ] && echo "-D GINT_DIAGNOSTICS"
|
||||
[ "${conf[GINT_STARTUP_LOG]}" != "" ] && echo "-D GINT_STARTUP_LOG"
|
||||
[ "${conf[GINT_NO_SYSCALLS]}" != "" ] && echo "-D GINT_NO_SYSCALLS"
|
||||
[ "${conf[GINT_EXTENDED_LIBC]}" != "" ] && echo "-D GINT_EXTENDED_LIBC"
|
||||
[ "${conf[GINT_STATIC_GRAY]}" != "" ] && echo "-D GINT_STATIC_GRAY"
|
||||
|
||||
echo "-D ATEXIT_MAX=${conf[ATEXIT_MAX]}"
|
||||
echo "-D TIMER_SLOTS=${conf[TIMER_SLOTS]}"
|
||||
|
|
|
@ -1,31 +0,0 @@
|
|||
OUTPUT_ARCH(sh3)
|
||||
ENTRY(initialize)
|
||||
MEMORY
|
||||
{
|
||||
rom : o = 0x00300200, l = 512k
|
||||
ram : o = 0x08100000, l = 64k /* pretty safe guess */
|
||||
}
|
||||
SECTIONS
|
||||
{
|
||||
.text : {
|
||||
*(.pretext) /* init stuff */
|
||||
*(.text)
|
||||
} > rom
|
||||
.rodata : {
|
||||
*(.rodata)
|
||||
*(.rodata.str1.4)
|
||||
_romdata = . ; /* symbol for initialization data */
|
||||
} > rom
|
||||
.bss : {
|
||||
_bbss = . ;
|
||||
_bssdatasize = . ;
|
||||
LONG(0); /* bssdatasize */
|
||||
*(.bss) *(COMMON);
|
||||
_ebss = . ;
|
||||
} > ram
|
||||
.data : AT(_romdata) {
|
||||
_bdata = . ;
|
||||
*(.data);
|
||||
_edata = . ;
|
||||
} > ram
|
||||
}
|
172
debug/crt0.s
172
debug/crt0.s
|
@ -1,172 +0,0 @@
|
|||
.section .pretext
|
||||
.global initialize
|
||||
initialize:
|
||||
sts.l pr, @-r15
|
||||
|
||||
! set up TLB
|
||||
mov.l Hmem_SetMMU, r3
|
||||
mov.l address_one, r4 ! 0x8102000
|
||||
mov.l address_two, r5 ! 0x8801E000
|
||||
jsr @r3 ! _Hmem_SetMMU
|
||||
mov #108, r6
|
||||
|
||||
! clear the BSS
|
||||
mov.l bbss, r4 ! start
|
||||
mov.l ebss, r5 ! end
|
||||
bra L_check_bss
|
||||
mov #0, r6
|
||||
L_zero_bss:
|
||||
mov.l r6, @r4 ! zero and advance
|
||||
add #4, r4
|
||||
L_check_bss:
|
||||
cmp/hs r5, r4
|
||||
bf L_zero_bss
|
||||
|
||||
! Copy the .data
|
||||
mov.l bdata, r4 ! dest
|
||||
mov.l edata, r5 ! dest limit
|
||||
mov.l romdata, r6 ! source
|
||||
bra L_check_data
|
||||
nop
|
||||
L_copy_data:
|
||||
mov.l @r6+, r3
|
||||
mov.l r3, @r4
|
||||
add #4, r4
|
||||
L_check_data:
|
||||
cmp/hs r5, r4
|
||||
bf L_copy_data
|
||||
|
||||
mov.l bbss, r4
|
||||
mov.l edata, r5
|
||||
sub r4, r5 ! size of .bss and .data sections
|
||||
add #4, r5
|
||||
mov.l bssdatasize, r4
|
||||
mov.l r5, @r4
|
||||
|
||||
mov.l GLibAddinAplExecutionCheck, r2
|
||||
mov #0, r4
|
||||
mov #1, r5
|
||||
jsr @r2 ! _GLibAddinAplExecutionCheck(0,1,1);
|
||||
mov r5, r6
|
||||
|
||||
mov.l CallbackAtQuitMainFunction, r3
|
||||
mov.l exit_handler, r4
|
||||
jsr @r3 ! _CallbackAtQuitMainFunction(&exit_handler)
|
||||
nop
|
||||
mov.l main, r3
|
||||
jmp @r3 ! _main()
|
||||
lds.l @r15+, pr
|
||||
|
||||
_exit_handler:
|
||||
mov.l r14, @-r15
|
||||
mov.l r13, @-r15
|
||||
mov.l r12, @-r15
|
||||
sts.l pr, @-r15
|
||||
|
||||
mov.l Bdel_cychdr, r14
|
||||
jsr @r14 ! _Bdel_cychdr
|
||||
mov #6, r4
|
||||
jsr @r14 ! _Bdel_cychdr
|
||||
mov #7, r4
|
||||
jsr @r14 ! _Bdel_cychdr
|
||||
mov #8, r4
|
||||
jsr @r14 ! _Bdel_cychdr
|
||||
mov #9, r4
|
||||
jsr @r14 ! _Bdel_cychdr
|
||||
mov #10, r4
|
||||
|
||||
mov.l BfileFLS_CloseFile, r12
|
||||
mov #4, r14
|
||||
mov #0, r13
|
||||
L_close_files:
|
||||
jsr @r12 ! _BfileFLS_CloseFile
|
||||
mov r13, r4
|
||||
add #1, r13
|
||||
cmp/ge r14, r13
|
||||
bf L_close_files
|
||||
|
||||
mov.l flsFindClose, r12
|
||||
mov #0, r13
|
||||
L_close_finds:
|
||||
jsr @r12 ! _flsFindClose
|
||||
mov r13, r4
|
||||
add #1, r13
|
||||
cmp/ge r14, r13
|
||||
bf L_close_finds
|
||||
|
||||
lds.l @r15+, pr
|
||||
mov.l @r15+, r12
|
||||
mov.l @r15+, r13
|
||||
mov.l Bkey_Set_RepeatTime_Default, r2
|
||||
jmp @r2 ! _Bkey_Set_RepeatTime_Default
|
||||
mov.l @r15+, r14
|
||||
|
||||
.align 4
|
||||
address_two: .long 0x8801E000
|
||||
address_one: .long 0x8102000
|
||||
Hmem_SetMMU: .long _Hmem_SetMMU
|
||||
GLibAddinAplExecutionCheck: .long _GLibAddinAplExecutionCheck
|
||||
CallbackAtQuitMainFunction: .long _CallbackAtQuitMainFunction
|
||||
Bdel_cychdr: .long _Bdel_cychdr
|
||||
BfileFLS_CloseFile: .long _BfileFLS_CloseFile
|
||||
flsFindClose: .long _flsFindClose
|
||||
Bkey_Set_RepeatTime_Default: .long _Bkey_Set_RepeatTime_Default
|
||||
bbss: .long _bbss
|
||||
ebss: .long _ebss
|
||||
edata: .long _edata
|
||||
bdata: .long _bdata
|
||||
romdata: .long _romdata
|
||||
bssdatasize: .long _bssdatasize
|
||||
|
||||
exit_handler: .long _exit_handler
|
||||
main: .long _main
|
||||
|
||||
_Hmem_SetMMU:
|
||||
mov.l sc_addr, r2
|
||||
mov.l 1f, r0
|
||||
jmp @r2
|
||||
nop
|
||||
1: .long 0x3FA
|
||||
|
||||
_Bdel_cychdr:
|
||||
mov.l sc_addr, r2
|
||||
mov.l 1f, r0
|
||||
jmp @r2
|
||||
nop
|
||||
1: .long 0x119
|
||||
|
||||
_BfileFLS_CloseFile:
|
||||
mov.l sc_addr, r2
|
||||
mov.l 1f, r0
|
||||
jmp @r2
|
||||
nop
|
||||
1: .long 0x1E7
|
||||
|
||||
_Bkey_Set_RepeatTime_Default:
|
||||
mov.l sc_addr, r2
|
||||
mov.l 1f, r0
|
||||
jmp @r2
|
||||
nop
|
||||
1: .long 0x244
|
||||
|
||||
_CallbackAtQuitMainFunction:
|
||||
mov.l sc_addr, r2
|
||||
mov.l 1f, r0
|
||||
jmp @r2
|
||||
nop
|
||||
1: .long 0x494
|
||||
|
||||
_flsFindClose:
|
||||
mov.l sc_addr, r2
|
||||
mov.l 1f, r0
|
||||
jmp @r2
|
||||
nop
|
||||
1: .long 0x218
|
||||
|
||||
_GLibAddinAplExecutionCheck:
|
||||
mov.l sc_addr, r2
|
||||
mov #0x13, r0
|
||||
jmp @r2
|
||||
nop
|
||||
sc_addr: .long 0x80010070
|
||||
.end
|
178
debug/gintdbg.c
178
debug/gintdbg.c
|
@ -1,178 +0,0 @@
|
|||
//---
|
||||
//
|
||||
// gintdbg
|
||||
//
|
||||
// A simple debugger for gint applications, providing diagnoses to
|
||||
// determine what went bad.
|
||||
//
|
||||
//---
|
||||
|
||||
// Just for structure definitions, gint does not run here.
|
||||
#define GINT_DIAGNOSTICS
|
||||
#include <internals/gint.h>
|
||||
#include <mpu.h>
|
||||
|
||||
#include <fxlib.h>
|
||||
#include <stdarg.h>
|
||||
#include <stddef.h>
|
||||
|
||||
// Some functions from other files...
|
||||
int vsprintf(char *buffer, const char *format, va_list args);
|
||||
int sprintf(char *buffer, const char *format, ...);
|
||||
|
||||
//---
|
||||
// Some util...
|
||||
//---
|
||||
|
||||
static int print_row = 1;
|
||||
|
||||
void print(int col, const char *format, ...)
|
||||
{
|
||||
int row = print_row;
|
||||
print_row++;
|
||||
if(row < 1 || row > 8) return;
|
||||
|
||||
char buffer[256];
|
||||
va_list args;
|
||||
va_start(args, format);
|
||||
vsprintf(buffer, format, args);
|
||||
va_end(args);
|
||||
|
||||
locate(col, row);
|
||||
Print((unsigned char *)buffer);
|
||||
}
|
||||
|
||||
void newline(void)
|
||||
{
|
||||
print_row++;
|
||||
}
|
||||
|
||||
void nothing_found(void)
|
||||
{
|
||||
unsigned int key;
|
||||
|
||||
Bdisp_AllClr_VRAM();
|
||||
PopUpWin(6);
|
||||
print_row = 2;
|
||||
print(3, "Apparently there");
|
||||
print(3, "is no diagnostic.");
|
||||
print(3, "");
|
||||
print(3, "Show anyway?");
|
||||
print(3, " Yes:[F1]");
|
||||
print(3, " No :[MENU]");
|
||||
|
||||
do GetKey(&key);
|
||||
while(key != KEY_CTRL_F1);
|
||||
}
|
||||
|
||||
void show_diagnostics(void)
|
||||
{
|
||||
volatile gint_diagnostics_t *dg = gint_diagnostics();
|
||||
|
||||
const char *stages[] = {
|
||||
"Startup", "Sections", "MMU", "Gint", "Clock", "Constructors",
|
||||
"Running", "Leaving", "Destructors", "Terminated",
|
||||
};
|
||||
const char *mpus[] = {
|
||||
"Unknown", "SH7337", "SH7355", "SH7305", "SH7724",
|
||||
};
|
||||
|
||||
print(1, "Gint debugger (%d)", dg->counter);
|
||||
newline();
|
||||
|
||||
print(1, "General information");
|
||||
print(2, "Magic 0x%02x", dg->magic);
|
||||
print(2, "Stage %s", dg->stage <= 9 ? stages[dg->stage] : "-");
|
||||
if(dg->stage >= stage_gint)
|
||||
{
|
||||
print(2, "MPU %s", dg->mpu <= 4 ? mpus[dg->mpu] : "-");
|
||||
}
|
||||
print(2, "Version %08x", dg->version);
|
||||
newline();
|
||||
|
||||
print(1, "Memory map");
|
||||
print(2, "%08x romdata", dg->romdata);
|
||||
print(2, "%08x vbr", dg->vbr_address);
|
||||
print(2, "%08x:%05x text", dg->section_text.address,
|
||||
dg->section_text.length);
|
||||
print(2, "%08x:%05x data", dg->section_data.address,
|
||||
dg->section_data.length);
|
||||
print(2, "%08x:%05x bss", dg->section_bss.address,
|
||||
dg->section_bss.length);
|
||||
|
||||
print(2, "%08x:%05x gint", dg->section_gint.address,
|
||||
dg->section_gint.length);
|
||||
newline();
|
||||
|
||||
print(1, "Exception records");
|
||||
size_t len = sizeof dg->except_vect;
|
||||
char line[19];
|
||||
for(size_t i = 0; i < len; i += 6)
|
||||
{
|
||||
for(size_t n = 0; n < 6 && i + n < len; n++)
|
||||
{
|
||||
size_t index = (dg->excepts + i + n) % len;
|
||||
sprintf(line + 3 * n, " %02x", dg->except_vect[index]);
|
||||
}
|
||||
|
||||
print(1, "%s", line);
|
||||
}
|
||||
print(2, "SPC %08x", dg->spc);
|
||||
print(2, "SSR %08x", dg->ssr);
|
||||
print(2, "EXPEVT %08x", dg->expevt);
|
||||
print(2, "TEA %08x", dg->tea);
|
||||
newline();
|
||||
|
||||
if(dg->stage >= stage_clock)
|
||||
{
|
||||
print(1, "Clock frequencies");
|
||||
print(2, "Bus clock %d MHz", dg->Bphi_f);
|
||||
print(2, "Peripheral %d MHz", dg->Pphi_f);
|
||||
print(2, "Processor %d MHz", dg->Iphi_f);
|
||||
newline();
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
main()
|
||||
Let's do this!
|
||||
*/
|
||||
int main(void)
|
||||
{
|
||||
volatile gint_diagnostics_t *dg = gint_diagnostics();
|
||||
unsigned int key;
|
||||
|
||||
if(dg->magic != GINT_DIAGNOSTICS_MAGIC
|
||||
|| dg->stage > 9
|
||||
|| dg->mpu > 4
|
||||
) nothing_found();
|
||||
|
||||
int total_height = -1;
|
||||
int y = 0;
|
||||
|
||||
while(1)
|
||||
{
|
||||
Bdisp_AllClr_VRAM();
|
||||
print_row = 1 - y;
|
||||
|
||||
show_diagnostics();
|
||||
if(total_height < 0) total_height = print_row - 1;
|
||||
|
||||
// Drawing a scrollbar.
|
||||
if(total_height > 8)
|
||||
{
|
||||
int base = (64 * y) / total_height;
|
||||
int height = (64 * 8) / total_height;
|
||||
Bdisp_DrawLineVRAM(127, base, 127, base + height);
|
||||
}
|
||||
Bdisp_PutDisp_DD();
|
||||
|
||||
do GetKey(&key);
|
||||
while(key != KEY_CTRL_UP && key != KEY_CTRL_DOWN);
|
||||
|
||||
if(key == KEY_CTRL_UP && y > 0) y--;
|
||||
else if(key == KEY_CTRL_DOWN && y + 8 < total_height) y++;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
BIN
debug/icon.bmp
BIN
debug/icon.bmp
Binary file not shown.
Before Width: | Height: | Size: 1.8 KiB |
|
@ -1,97 +0,0 @@
|
|||
/*****************************************************************/
|
||||
/* */
|
||||
/* CASIO fx-9860G SDK Library */
|
||||
/* */
|
||||
/* File name : dispbios.h */
|
||||
/* */
|
||||
/* Copyright (c) 2006 CASIO COMPUTER CO., LTD. */
|
||||
/* */
|
||||
/*****************************************************************/
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#ifndef __DISPBIOS_H__
|
||||
#define __DISPBIOS_H__
|
||||
|
||||
|
||||
// Defines
|
||||
|
||||
#define IM_VRAM_WIDTH 128
|
||||
#define IM_VRAM_HEIGHT 64
|
||||
|
||||
#define IM_VRAM_SIZE 1024
|
||||
|
||||
#define IM_CHARACTERS_MAX_LINE 21
|
||||
#define IM_BYTES_MAX_LINE (IM_CHARACTERS_MAX_LINE*2)
|
||||
|
||||
#define SAVEDISP_PAGE1 1
|
||||
#define SAVEDISP_PAGE2 5
|
||||
#define SAVEDISP_PAGE3 6
|
||||
|
||||
#define MINI_OVER 0x10
|
||||
#define MINI_OR 0x11
|
||||
#define MINI_REV 0x12
|
||||
#define MINI_REVOR 0x13
|
||||
|
||||
#define IM_BIOS_DD_WIDTH IM_VRAM_WIDTH
|
||||
#define IM_BIOS_DD_HEIGHT IM_VRAM_HEIGHT
|
||||
|
||||
#define WRITEKIND unsigned char
|
||||
#define IMB_WRITEKIND_OVER 0x01
|
||||
#define IMB_WRITEKIND_OR 0x02
|
||||
#define IMB_WRITEKIND_AND 0x03
|
||||
#define IMB_WRITEKIND_XOR 0x04
|
||||
|
||||
#define WRITEMODIFY unsigned char
|
||||
#define IMB_WRITEMODIFY_NORMAL 0x01
|
||||
#define IMB_WRITEMODIFY_REVERCE 0x02
|
||||
#define IMB_WRITEMODIFY_MESH 0x03
|
||||
|
||||
#define AREAKIND unsigned char
|
||||
#define IMB_AREAKIND_OVER 0x01
|
||||
#define IMB_AREAKIND_MESH 0x02
|
||||
#define IMB_AREAKIND_CLR 0x03
|
||||
#define IMB_AREAKIND_REVERSE 0x04
|
||||
|
||||
#define EFFECTWIN unsigned char
|
||||
#define IMB_EFFECTWIN_OK 0x01
|
||||
#define IMB_EFFECTWIN_NG 0x02
|
||||
|
||||
|
||||
// Structs
|
||||
|
||||
typedef struct tag_DISPBOX{
|
||||
int left;
|
||||
int top;
|
||||
int right;
|
||||
int bottom;
|
||||
} DISPBOX;
|
||||
|
||||
typedef struct tag_GRAPHDATA{
|
||||
int width;
|
||||
int height;
|
||||
unsigned char *pBitmap;
|
||||
} GRAPHDATA;
|
||||
|
||||
typedef struct tag_RECTANGLE{
|
||||
DISPBOX LineArea;
|
||||
AREAKIND AreaKind;
|
||||
EFFECTWIN EffectWin;
|
||||
} RECTANGLE;
|
||||
|
||||
typedef struct tag_DISPGRAPH{
|
||||
int x;
|
||||
int y;
|
||||
GRAPHDATA GraphData;
|
||||
WRITEMODIFY WriteModify;
|
||||
WRITEKIND WriteKind;
|
||||
} DISPGRAPH;
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
|
@ -1,31 +0,0 @@
|
|||
/*****************************************************************/
|
||||
/* */
|
||||
/* CASIO fx-9860G SDK Library */
|
||||
/* */
|
||||
/* File name : endian.h */
|
||||
/* */
|
||||
/* Copyright (c) 2006 CASIO COMPUTER CO., LTD. */
|
||||
/* */
|
||||
/*****************************************************************/
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#ifndef __ENDIAN_H__
|
||||
#define __ENDIAN_H__
|
||||
|
||||
|
||||
// Macros
|
||||
|
||||
#define UtlSwapWord(w) (unsigned short)((((w) & 0x00ff) << 8) | (((w) & 0xff00) >> 8))
|
||||
#define UtlSwapDword(l) (unsigned long)((((l) & 0x000000ff) << 24) | (((l) & 0x0000ff00) << 8) | (((l) & 0xff000000) >> 24) | (((l) & 0x00ff0000) >> 8))
|
||||
#define UtlSwapInteger(i) UtlSwapDword(i)
|
||||
#define UtlSwapPointer(p) (void*)((((unsigned long)(p) & 0x000000ff) << 24) | (((unsigned long)(p) & 0x0000ff00) << 8) | (((unsigned long)(p) & 0xff000000) >> 24) | (((unsigned long)(p) & 0x00ff0000) >> 8))
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
|
@ -1,116 +0,0 @@
|
|||
/*****************************************************************/
|
||||
/* */
|
||||
/* CASIO fx-9860G SDK Library */
|
||||
/* */
|
||||
/* File name : filebios.h */
|
||||
/* */
|
||||
/* Copyright (c) 2006 CASIO COMPUTER CO., LTD. */
|
||||
/* */
|
||||
/*****************************************************************/
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#ifndef __FILEBIOS_H__
|
||||
#define __FILEBIOS_H__
|
||||
|
||||
|
||||
// Defines
|
||||
|
||||
#define FONTCHARACTER unsigned short
|
||||
|
||||
#define _OPENMODE_READ 0x01
|
||||
#define _OPENMODE_READ_SHARE 0x80
|
||||
#define _OPENMODE_WRITE 0x02
|
||||
#define _OPENMODE_READWRITE 0x03
|
||||
#define _OPENMODE_READWRITE_SHARE 0x83
|
||||
|
||||
#define _CREATEMODE_BINARY 1
|
||||
#define _CREATEMODE_DIRECTORY 5
|
||||
|
||||
enum DEVICE_TYPE{
|
||||
DEVICE_MAIN_MEMORY,
|
||||
DEVICE_STORAGE,
|
||||
DEVICE_SD_CARD, // only fx-9860G SD model
|
||||
};
|
||||
|
||||
|
||||
// File system standard error code
|
||||
#define IML_FILEERR_NOERROR 0
|
||||
#define IML_FILEERR_ENTRYNOTFOUND -1
|
||||
#define IML_FILEERR_ILLEGALPARAM -2
|
||||
#define IML_FILEERR_ILLEGALPATH -3
|
||||
#define IML_FILEERR_DEVICEFULL -4
|
||||
#define IML_FILEERR_ILLEGALDEVICE -5
|
||||
#define IML_FILEERR_ILLEGALFILESYS -6
|
||||
#define IML_FILEERR_ILLEGALSYSTEM -7
|
||||
#define IML_FILEERR_ACCESSDENYED -8
|
||||
#define IML_FILEERR_ALREADYLOCKED -9
|
||||
#define IML_FILEERR_ILLEGALTASKID -10
|
||||
#define IML_FILEERR_PERMISSIONERROR -11
|
||||
#define IML_FILEERR_ENTRYFULL -12
|
||||
#define IML_FILEERR_ALREADYEXISTENTRY -13
|
||||
#define IML_FILEERR_READONLYFILE -14
|
||||
#define IML_FILEERR_ILLEGALFILTER -15
|
||||
#define IML_FILEERR_ENUMRATEEND -16
|
||||
#define IML_FILEERR_DEVICECHANGED -17
|
||||
//#define IML_FILEERR_NOTRECORDFILE -18 // Not used
|
||||
#define IML_FILEERR_ILLEGALSEEKPOS -19
|
||||
#define IML_FILEERR_ILLEGALBLOCKFILE -20
|
||||
//#define IML_FILEERR_DEVICENOTEXIST -21 // Not used
|
||||
//#define IML_FILEERR_ENDOFFILE -22 // Not used
|
||||
#define IML_FILEERR_NOTMOUNTDEVICE -23
|
||||
#define IML_FILEERR_NOTUNMOUNTDEVICE -24
|
||||
#define IML_FILEERR_CANNOTLOCKSYSTEM -25
|
||||
#define IML_FILEERR_RECORDNOTFOUND -26
|
||||
//#define IML_FILEERR_NOTDUALRECORDFILE -27 // Not used
|
||||
#define IML_FILEERR_NOTALARMSUPPORT -28
|
||||
#define IML_FILEERR_CANNOTADDALARM -29
|
||||
#define IML_FILEERR_FILEFINDUSED -30
|
||||
#define IML_FILEERR_DEVICEERROR -31
|
||||
#define IML_FILEERR_SYSTEMNOTLOCKED -32
|
||||
#define IML_FILEERR_DEVICENOTFOUND -33
|
||||
#define IML_FILEERR_FILETYPEMISMATCH -34
|
||||
#define IML_FILEERR_NOTEMPTY -35
|
||||
#define IML_FILEERR_BROKENSYSTEMDATA -36
|
||||
#define IML_FILEERR_MEDIANOTREADY -37
|
||||
#define IML_FILEERR_TOOMANYALARMITEM -38
|
||||
#define IML_FILEERR_SAMEALARMEXIST -39
|
||||
#define IML_FILEERR_ACCESSSWAPAREA -40
|
||||
#define IML_FILEERR_MULTIMEDIACARD -41
|
||||
#define IML_FILEERR_COPYPROTECTION -42
|
||||
#define IML_FILEERR_ILLEGALFILEDATA -43
|
||||
|
||||
// FILE_INFO.type
|
||||
#define DT_DIRECTORY 0x0000 // Directory
|
||||
#define DT_FILE 0x0001 // File
|
||||
#define DT_ADDIN_APP 0x0002 // Add-In application
|
||||
#define DT_EACT 0x0003 // eActivity
|
||||
#define DT_LANGUAGE 0x0004 // Language
|
||||
#define DT_BITMAP 0x0005 // Bitmap
|
||||
#define DT_MAINMEM 0x0006 // Main Memory data
|
||||
#define DT_TEMP 0x0007 // Temporary data
|
||||
#define DT_DOT 0x0008 // . (Current directory)
|
||||
#define DT_DOTDOT 0x0009 // .. (Parent directory)
|
||||
#define DT_VOLUME 0x000A // Volume label
|
||||
|
||||
|
||||
// Structs
|
||||
|
||||
typedef struct tag_FILE_INFO
|
||||
{
|
||||
unsigned short id;
|
||||
unsigned short type;
|
||||
unsigned long fsize; // File size
|
||||
unsigned long dsize; // Data size
|
||||
unsigned int property; // The file has not been completed, except when property is 0.
|
||||
unsigned long address;
|
||||
} FILE_INFO;
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
|
@ -1,100 +0,0 @@
|
|||
/*****************************************************************/
|
||||
/* */
|
||||
/* CASIO fx-9860G SDK Library */
|
||||
/* */
|
||||
/* File name : fxlib.h */
|
||||
/* */
|
||||
/* Copyright (c) 2006 CASIO COMPUTER CO., LTD. */
|
||||
/* */
|
||||
/*****************************************************************/
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#ifndef __FXLIB_H__
|
||||
#define __FXLIB_H__
|
||||
|
||||
#include "dispbios.h"
|
||||
#include "filebios.h"
|
||||
#include "keybios.h"
|
||||
|
||||
|
||||
// Prototypes
|
||||
|
||||
void Bdisp_AllClr_DD(void);
|
||||
void Bdisp_AllClr_VRAM(void);
|
||||
void Bdisp_AllClr_DDVRAM(void);
|
||||
void Bdisp_AreaClr_DD(const DISPBOX *pArea);
|
||||
void Bdisp_AreaClr_VRAM(const DISPBOX *pArea);
|
||||
void Bdisp_AreaClr_DDVRAM(const DISPBOX *pArea);
|
||||
void Bdisp_AreaReverseVRAM(int x1, int y1, int x2, int y2);
|
||||
void Bdisp_GetDisp_DD(unsigned char *pData);
|
||||
void Bdisp_GetDisp_VRAM(unsigned char *pData);
|
||||
void Bdisp_PutDisp_DD(void);
|
||||
void Bdisp_PutDispArea_DD(const DISPBOX *PutDispArea);
|
||||
void Bdisp_SetPoint_DD(int x, int y, unsigned char point);
|
||||
void Bdisp_SetPoint_VRAM(int x, int y, unsigned char point);
|
||||
void Bdisp_SetPoint_DDVRAM(int x, int y, unsigned char point);
|
||||
int Bdisp_GetPoint_VRAM(int x, int y);
|
||||
void Bdisp_WriteGraph_DD(const DISPGRAPH *WriteGraph);
|
||||
void Bdisp_WriteGraph_VRAM(const DISPGRAPH *WriteGraph);
|
||||
void Bdisp_WriteGraph_DDVRAM(const DISPGRAPH *WriteGraph);
|
||||
void Bdisp_ReadArea_DD(const DISPBOX *ReadArea, unsigned char *ReadData);
|
||||
void Bdisp_ReadArea_VRAM(const DISPBOX *ReadArea, unsigned char *ReadData);
|
||||
void Bdisp_DrawLineVRAM(int x1, int y1, int x2, int y2);
|
||||
void Bdisp_ClearLineVRAM(int x1, int y1, int x2, int y2);
|
||||
|
||||
void locate(int x, int y);
|
||||
void Print(const unsigned char *str);
|
||||
void PrintRev(const unsigned char *str);
|
||||
void PrintC(const unsigned char *c);
|
||||
void PrintRevC(const unsigned char *str);
|
||||
void PrintLine(const unsigned char *str, int max);
|
||||
void PrintRLine(const unsigned char *str, int max);
|
||||
void PrintXY(int x, int y, const unsigned char *str, int type);
|
||||
int PrintMini(int x, int y, const unsigned char *str, int type);
|
||||
void SaveDisp(unsigned char num);
|
||||
void RestoreDisp(unsigned char num);
|
||||
void PopUpWin(int n);
|
||||
|
||||
int Bfile_OpenFile(const FONTCHARACTER *filename, int mode);
|
||||
int Bfile_OpenMainMemory(const unsigned char *name);
|
||||
int Bfile_ReadFile(int HANDLE, void *buf, int size, int readpos);
|
||||
int Bfile_WriteFile(int HANDLE, const void *buf, int size);
|
||||
int Bfile_SeekFile(int HANDLE, int pos);
|
||||
int Bfile_CloseFile(int HANDLE);
|
||||
int Bfile_GetMediaFree(enum DEVICE_TYPE devicetype, int *freebytes);
|
||||
int Bfile_GetFileSize(int HANDLE);
|
||||
int Bfile_CreateFile(const FONTCHARACTER *filename, int size);
|
||||
int Bfile_CreateDirectory(const FONTCHARACTER *pathname);
|
||||
int Bfile_CreateMainMemory(const unsigned char *name);
|
||||
int Bfile_RenameMainMemory(const unsigned char *oldname, const unsigned char *newname);
|
||||
int Bfile_DeleteFile(const FONTCHARACTER *filename);
|
||||
int Bfile_DeleteDirectory(const FONTCHARACTER *pathname);
|
||||
int Bfile_DeleteMainMemory(const unsigned char *name);
|
||||
int Bfile_FindFirst(const FONTCHARACTER *pathname, int *FindHandle, FONTCHARACTER *foundfile, FILE_INFO *fileinfo);
|
||||
int Bfile_FindNext(int FindHandle, FONTCHARACTER *foundfile, FILE_INFO *fileinfo);
|
||||
int Bfile_FindClose(int FindHandle);
|
||||
|
||||
void Bkey_Set_RepeatTime(long FirstCount, long NextCount);
|
||||
void Bkey_Get_RepeatTime(long *FirstCount, long *NextCount);
|
||||
void Bkey_Set_RepeatTime_Default(void);
|
||||
int GetKeyWait(int sel, int time, int menu, unsigned int *keycode);
|
||||
int IsKeyDown(int keycode);
|
||||
int IsKeyUp(int keycode);
|
||||
int GetKey(unsigned int *keycode);
|
||||
|
||||
int SetTimer(int ID, int elapse, void (*hander)(void));
|
||||
int KillTimer(int ID);
|
||||
void Sleep(int millisecond);
|
||||
|
||||
void SetQuitHandler(void (*callback)(void));
|
||||
int INIT_ADDIN_APPLICATION(int isAppli, unsigned short OptionNum);
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
|
@ -1,158 +0,0 @@
|
|||
/*****************************************************************/
|
||||
/* */
|
||||
/* CASIO fx-9860G SDK Library */
|
||||
/* */
|
||||
/* File name : keybios.h */
|
||||
/* */
|
||||
/* Copyright (c) 2006 CASIO COMPUTER CO., LTD. */
|
||||
/* */
|
||||
/*****************************************************************/
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#ifndef __KEYBIOS_H__
|
||||
#define __KEYBIOS_H__
|
||||
|
||||
|
||||
// Defines
|
||||
|
||||
// Character codes
|
||||
#define KEY_CHAR_0 0x30
|
||||
#define KEY_CHAR_1 0x31
|
||||
#define KEY_CHAR_2 0x32
|
||||
#define KEY_CHAR_3 0x33
|
||||
#define KEY_CHAR_4 0x34
|
||||
#define KEY_CHAR_5 0x35
|
||||
#define KEY_CHAR_6 0x36
|
||||
#define KEY_CHAR_7 0x37
|
||||
#define KEY_CHAR_8 0x38
|
||||
#define KEY_CHAR_9 0x39
|
||||
#define KEY_CHAR_DP 0x2e
|
||||
#define KEY_CHAR_EXP 0x0f
|
||||
#define KEY_CHAR_PMINUS 0x87
|
||||
#define KEY_CHAR_PLUS 0x89
|
||||
#define KEY_CHAR_MINUS 0x99
|
||||
#define KEY_CHAR_MULT 0xa9
|
||||
#define KEY_CHAR_DIV 0xb9
|
||||
#define KEY_CHAR_FRAC 0xbb
|
||||
#define KEY_CHAR_LPAR 0x28
|
||||
#define KEY_CHAR_RPAR 0x29
|
||||
#define KEY_CHAR_COMMA 0x2c
|
||||
#define KEY_CHAR_STORE 0x0e
|
||||
#define KEY_CHAR_LOG 0x95
|
||||
#define KEY_CHAR_LN 0x85
|
||||
#define KEY_CHAR_SIN 0x81
|
||||
#define KEY_CHAR_COS 0x82
|
||||
#define KEY_CHAR_TAN 0x83
|
||||
#define KEY_CHAR_SQUARE 0x8b
|
||||
#define KEY_CHAR_POW 0xa8
|
||||
#define KEY_CHAR_IMGNRY 0x7f50
|
||||
#define KEY_CHAR_LIST 0x7f51
|
||||
#define KEY_CHAR_MAT 0x7f40
|
||||
#define KEY_CHAR_EQUAL 0x3d
|
||||
#define KEY_CHAR_PI 0xd0
|
||||
#define KEY_CHAR_ANS 0xc0
|
||||
#define KEY_CHAR_LBRCKT 0x5b
|
||||
#define KEY_CHAR_RBRCKT 0x5d
|
||||
#define KEY_CHAR_LBRACE 0x7b
|
||||
#define KEY_CHAR_RBRACE 0x7d
|
||||
#define KEY_CHAR_CR 0x0d
|
||||
#define KEY_CHAR_CUBEROOT 0x96
|
||||
#define KEY_CHAR_RECIP 0x9b
|
||||
#define KEY_CHAR_ANGLE 0x7f54
|
||||
#define KEY_CHAR_EXPN10 0xb5
|
||||
#define KEY_CHAR_EXPN 0xa5
|
||||
#define KEY_CHAR_ASIN 0x91
|
||||
#define KEY_CHAR_ACOS 0x92
|
||||
#define KEY_CHAR_ATAN 0x93
|
||||
#define KEY_CHAR_ROOT 0x86
|
||||
#define KEY_CHAR_POWROOT 0xb8
|
||||
#define KEY_CHAR_SPACE 0x20
|
||||
#define KEY_CHAR_DQUATE 0x22
|
||||
#define KEY_CHAR_VALR 0xcd
|
||||
#define KEY_CHAR_THETA 0xce
|
||||
#define KEY_CHAR_A 0x41
|
||||
#define KEY_CHAR_B 0x42
|
||||
#define KEY_CHAR_C 0x43
|
||||
#define KEY_CHAR_D 0x44
|
||||
#define KEY_CHAR_E 0x45
|
||||
#define KEY_CHAR_F 0x46
|
||||
#define KEY_CHAR_G 0x47
|
||||
#define KEY_CHAR_H 0x48
|
||||
#define KEY_CHAR_I 0x49
|
||||
#define KEY_CHAR_J 0x4a
|
||||
#define KEY_CHAR_K 0x4b
|
||||
#define KEY_CHAR_L 0x4c
|
||||
#define KEY_CHAR_M 0x4d
|
||||
#define KEY_CHAR_N 0x4e
|
||||
#define KEY_CHAR_O 0x4f
|
||||
#define KEY_CHAR_P 0x50
|
||||
#define KEY_CHAR_Q 0x51
|
||||
#define KEY_CHAR_R 0x52
|
||||
#define KEY_CHAR_S 0x53
|
||||
#define KEY_CHAR_T 0x54
|
||||
#define KEY_CHAR_U 0x55
|
||||
#define KEY_CHAR_V 0x56
|
||||
#define KEY_CHAR_W 0x57
|
||||
#define KEY_CHAR_X 0x58
|
||||
#define KEY_CHAR_Y 0x59
|
||||
#define KEY_CHAR_Z 0x5a
|
||||
|
||||
|
||||
// Control codes
|
||||
#define KEY_CTRL_NOP 0
|
||||
#define KEY_CTRL_EXE 30004
|
||||
#define KEY_CTRL_DEL 30025
|
||||
#define KEY_CTRL_AC 30015
|
||||
#define KEY_CTRL_FD 30046
|
||||
#define KEY_CTRL_XTT 30001
|
||||
#define KEY_CTRL_EXIT 30002
|
||||
#define KEY_CTRL_SHIFT 30006
|
||||
#define KEY_CTRL_ALPHA 30007
|
||||
#define KEY_CTRL_OPTN 30008
|
||||
#define KEY_CTRL_VARS 30016
|
||||
#define KEY_CTRL_UP 30018
|
||||
#define KEY_CTRL_DOWN 30023
|
||||
#define KEY_CTRL_LEFT 30020
|
||||
#define KEY_CTRL_RIGHT 30021
|
||||
#define KEY_CTRL_F1 30009
|
||||
#define KEY_CTRL_F2 30010
|
||||
#define KEY_CTRL_F3 30011
|
||||
#define KEY_CTRL_F4 30012
|
||||
#define KEY_CTRL_F5 30013
|
||||
#define KEY_CTRL_F6 30014
|
||||
#define KEY_CTRL_CATALOG 30100
|
||||
#define KEY_CTRL_CAPTURE 30055
|
||||
#define KEY_CTRL_CLIP 30050
|
||||
#define KEY_CTRL_PASTE 30036
|
||||
#define KEY_CTRL_INS 30033
|
||||
#define KEY_CTRL_MIXEDFRAC 30054
|
||||
#define KEY_CTRL_FRACCNVRT 30026
|
||||
#define KEY_CTRL_QUIT 30029
|
||||
#define KEY_CTRL_PRGM 30028
|
||||
#define KEY_CTRL_SETUP 30037
|
||||
#define KEY_CTRL_PAGEUP 30052
|
||||
#define KEY_CTRL_PAGEDOWN 30053
|
||||
#define KEY_CTRL_MENU 30003
|
||||
#define KEY_CTRL_RESERVE1 30060
|
||||
#define KEY_CTRL_RESERVE2 30061
|
||||
#define KEY_CTRL_RESERVE3 30062
|
||||
|
||||
|
||||
// in Bkey_GetKeyWait function
|
||||
#define KEYWAIT_HALTON_TIMEROFF 0
|
||||
#define KEYWAIT_HALTOFF_TIMEROFF 1
|
||||
#define KEYWAIT_HALTON_TIMERON 2
|
||||
|
||||
#define KEYREP_NOEVENT 0
|
||||
#define KEYREP_KEYEVENT 1
|
||||
#define KEYREP_TIMEREVENT 2
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
|
@ -1,31 +0,0 @@
|
|||
/*****************************************************************/
|
||||
/* */
|
||||
/* CASIO fx-9860G SDK Library */
|
||||
/* */
|
||||
/* File name : timer.h */
|
||||
/* */
|
||||
/* Copyright (c) 2006 CASIO COMPUTER CO., LTD. */
|
||||
/* */
|
||||
/*****************************************************************/
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#ifndef __TIMER_H__
|
||||
#define __TIMER_H__
|
||||
|
||||
|
||||
// Defines
|
||||
|
||||
#define ID_USER_TIMER1 1
|
||||
#define ID_USER_TIMER2 2
|
||||
#define ID_USER_TIMER3 3
|
||||
#define ID_USER_TIMER4 4
|
||||
#define ID_USER_TIMER5 5
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
BIN
debug/libfx.a
BIN
debug/libfx.a
Binary file not shown.
160
debug/vsprintf.c
160
debug/vsprintf.c
|
@ -1,160 +0,0 @@
|
|||
//---
|
||||
// vsprintf()
|
||||
//
|
||||
// Unfortunately this function, which is part of fxlib, was broken by a
|
||||
// terribly un-professional port (incidentally, I am responsible for
|
||||
// this, so I can't complain). So we'll need something simple...
|
||||
//
|
||||
// Format Flags Character count
|
||||
// %d none no
|
||||
// %x 0 yes
|
||||
// %p none no
|
||||
// %c none no
|
||||
// %s none no
|
||||
//---
|
||||
|
||||
#include <stdarg.h>
|
||||
#include <stdint.h>
|
||||
|
||||
void vsprintf_int(char **buffer_ptr, int n)
|
||||
{
|
||||
char *buffer = *buffer_ptr;
|
||||
if(!n)
|
||||
{
|
||||
*buffer++ = '0';
|
||||
*buffer_ptr = buffer;
|
||||
return;
|
||||
}
|
||||
if(n < 0)
|
||||
{
|
||||
*buffer++ = '-';
|
||||
n = -n;
|
||||
}
|
||||
|
||||
int digits = 0, x = n, copy;
|
||||
while(x) digits++, x /= 10;
|
||||
copy = digits;
|
||||
|
||||
while(digits)
|
||||
{
|
||||
buffer[--digits] = n % 10 + '0';
|
||||
n /= 10;
|
||||
}
|
||||
|
||||
*buffer_ptr = buffer + copy;
|
||||
}
|
||||
|
||||
void vsprintf_hexa(char **buffer_ptr, uint32_t val, int digits, int zero)
|
||||
{
|
||||
char *buffer = *buffer_ptr;
|
||||
if(!val)
|
||||
{
|
||||
while(digits-- > 1) *buffer++ = (zero) ? '0' : ' ';
|
||||
*buffer++ = '0';
|
||||
*buffer_ptr = buffer;
|
||||
return;
|
||||
}
|
||||
|
||||
if(digits <= 0)
|
||||
{
|
||||
uint32_t x = val;
|
||||
while(x) digits++, x >>= 4;
|
||||
}
|
||||
int copy = digits;
|
||||
|
||||
while(val && digits)
|
||||
{
|
||||
buffer[--digits] = (val & 15) + '0' + 39 * ((val & 15) > 9);
|
||||
val >>= 4;
|
||||
}
|
||||
while(digits)
|
||||
{
|
||||
buffer[--digits] = (zero) ? '0': ' ';
|
||||
}
|
||||
|
||||
*buffer_ptr = buffer + copy;
|
||||
}
|
||||
|
||||
void vsprintf_ptr(char **buffer_ptr, void *ptr)
|
||||
{
|
||||
vsprintf_hexa(buffer_ptr, (uint32_t)ptr, 8, 1);
|
||||
}
|
||||
|
||||
void vsprintf_char(char **buffer_ptr, int c)
|
||||
{
|
||||
char *buffer = *buffer_ptr;
|
||||
*buffer++ = c;
|
||||
*buffer_ptr = buffer;
|
||||
}
|
||||
|
||||
void vsprintf_str(char **buffer_ptr, const char *str)
|
||||
{
|
||||
char *buffer = *buffer_ptr;
|
||||
while(*str) *buffer++ = *str++;
|
||||
*buffer_ptr = buffer;
|
||||
}
|
||||
|
||||
int vsprintf(char *buffer, const char *format, va_list args)
|
||||
{
|
||||
char *save = buffer;
|
||||
int zero, count;
|
||||
|
||||
while(*format)
|
||||
{
|
||||
if(*format != '%')
|
||||
{
|
||||
*buffer++ = *format++;
|
||||
continue;
|
||||
}
|
||||
if(!*++format) break;
|
||||
|
||||
zero = 0;
|
||||
count = 0;
|
||||
|
||||
if(*format == '0') zero = 1, format++;
|
||||
while(*format >= '0' && *format <= '9')
|
||||
{
|
||||
count *= 10;
|
||||
count += (*format++ - '0');
|
||||
}
|
||||
if(!*format) break;
|
||||
|
||||
switch(*format)
|
||||
{
|
||||
case 'd':
|
||||
vsprintf_int(&buffer, va_arg(args, int));
|
||||
break;
|
||||
case 'x':
|
||||
vsprintf_hexa(&buffer, va_arg(args, uint32_t), count,
|
||||
zero);
|
||||
break;
|
||||
case 'p':
|
||||
vsprintf_ptr(&buffer, va_arg(args, void *));
|
||||
break;
|
||||
case 'c':
|
||||
vsprintf_char(&buffer, va_arg(args, int));
|
||||
break;
|
||||
case 's':
|
||||
vsprintf_str(&buffer, va_arg(args, const char *));
|
||||
break;
|
||||
default:
|
||||
*buffer++ = *format;
|
||||
break;
|
||||
}
|
||||
|
||||
format++;
|
||||
}
|
||||
|
||||
*buffer = 0;
|
||||
return buffer - save;
|
||||
}
|
||||
|
||||
int sprintf(char *buffer, const char *format, ...)
|
||||
{
|
||||
va_list args;
|
||||
va_start(args, format);
|
||||
int x = vsprintf(buffer, format, args);
|
||||
va_end(args);
|
||||
|
||||
return x;
|
||||
}
|
|
@ -277,8 +277,7 @@ void tlb_debug(void)
|
|||
}
|
||||
*/
|
||||
|
||||
#include <modules/rtc.h>
|
||||
#include <modules/interrupts.h>
|
||||
#include <internals/timer.h>
|
||||
|
||||
/*
|
||||
main_menu()
|
||||
|
@ -435,14 +434,11 @@ void main_menu(int *category, int *app)
|
|||
index = 0;
|
||||
scroll = 0;
|
||||
break;
|
||||
case KEY_F5:
|
||||
gint_switch();
|
||||
break;
|
||||
case KEY_F6:;
|
||||
/* case KEY_F6:;
|
||||
void screen(void);
|
||||
screen();
|
||||
break;
|
||||
|
||||
*/
|
||||
case KEY_UP:
|
||||
if(list && list_len > 1)
|
||||
{
|
||||
|
@ -484,11 +480,6 @@ void main_menu(int *category, int *app)
|
|||
if(app) *app = index + 1;
|
||||
return;
|
||||
|
||||
/* case KEY_MENU:
|
||||
if(category) *category = 0;
|
||||
if(app) *app = 0;
|
||||
return;
|
||||
*/
|
||||
default:
|
||||
leave = 0;
|
||||
}
|
||||
|
@ -533,7 +524,7 @@ int main(void)
|
|||
return 0;
|
||||
}
|
||||
|
||||
void crash(void)
|
||||
static void crash(void)
|
||||
{
|
||||
__asm__(
|
||||
"mov #0, r0 \n\t"
|
||||
|
@ -542,7 +533,7 @@ void crash(void)
|
|||
);
|
||||
}
|
||||
|
||||
void screen(void)
|
||||
static void screen(void)
|
||||
{
|
||||
enum { File = 1, Folder = 5 };
|
||||
enum { Read = 0x01, Write = 0x02, ReadWrite = Read | Write };
|
||||
|
|
|
@ -13,36 +13,32 @@ ENTRY(_start)
|
|||
|
||||
MEMORY
|
||||
{
|
||||
/* System-managed mappings map this area into the add-in data */
|
||||
rom : o = 0x00300200, l = 512k
|
||||
/* 0x0810000 is apparently mapped to 0x8801c0000. */
|
||||
/* 0x0810000 is apparently mapped to 0x8801c0000 */
|
||||
ram : o = 0x08100000, l = 8k
|
||||
/* RAM section from P1 area, no MMU involved */
|
||||
realram : o = 0x8800d000, l = 12k
|
||||
}
|
||||
|
||||
SECTIONS
|
||||
{
|
||||
/*
|
||||
ROM sections : binary code and read-only data.
|
||||
*/
|
||||
/* ROM sections: binary code and read-only data */
|
||||
|
||||
.text : {
|
||||
_btext = . ;
|
||||
|
||||
/* Initialization code. */
|
||||
*(.pretext.entry)
|
||||
*(.pretext)
|
||||
|
||||
_bctors = . ;
|
||||
_bctors = ABSOLUTE(.);
|
||||
*(.ctors)
|
||||
_ectors = . ;
|
||||
_bdtors = . ;
|
||||
_ectors = ABSOLUTE(.);
|
||||
_bdtors = ABSOLUTE(.);
|
||||
*(.dtors)
|
||||
_edtors = . ;
|
||||
_edtors = ABSOLUTE(.);
|
||||
|
||||
*(.text)
|
||||
*(.text.*)
|
||||
|
||||
_etext = . ;
|
||||
} > rom
|
||||
|
||||
.rodata : {
|
||||
|
@ -55,26 +51,23 @@ SECTIONS
|
|||
_romdata = ALIGN(4) ;
|
||||
} > rom
|
||||
|
||||
|
||||
|
||||
/*
|
||||
RAM sections : bss section and read/write data.
|
||||
The BSS section is meant to be stripped from the ELF file (to
|
||||
reduce the binary size) and initialized with zeros in the
|
||||
initialization routine, therefore its location is undefined.
|
||||
*/
|
||||
/* RAM sections: bss section and read/write data
|
||||
The .bss section is to be stripped from the ELF file and wiped at
|
||||
startup, hence its location is undefined */
|
||||
|
||||
.bss : {
|
||||
_bbss = . ;
|
||||
_bbss = ABSOLUTE(.);
|
||||
_sbss = ABSOLUTE(SIZEOF(.bss));
|
||||
|
||||
*(.bss)
|
||||
_ebss = . ;
|
||||
} > ram
|
||||
|
||||
.data : AT(_romdata) ALIGN(4) {
|
||||
_bdata = . ;
|
||||
_bdata = ABSOLUTE(.);
|
||||
_sdata = ABSOLUTE(SIZEOF(.data));
|
||||
|
||||
*(.data)
|
||||
*(.data.*)
|
||||
_edata = . ;
|
||||
} > ram
|
||||
|
||||
.cc : AT(_romdata + SIZEOF(.data)) ALIGN(4) {
|
||||
|
@ -82,33 +75,39 @@ SECTIONS
|
|||
*(.jcr)
|
||||
|
||||
. = ALIGN(4);
|
||||
_gint_data = _romdata + SIZEOF(.data) + SIZEOF(.cc) ;
|
||||
} > ram
|
||||
|
||||
/* Real RAM sections: interrupt handlers and some *uninitialized* gint
|
||||
data */
|
||||
|
||||
|
||||
/*
|
||||
Real RAM : interrupt, exception and TLB miss handlers.
|
||||
*/
|
||||
_gint_data = _romdata + SIZEOF(.data) + SIZEOF(.cc) ;
|
||||
|
||||
.gint : AT(_gint_data) ALIGN(4) {
|
||||
/* The vbr needs to be 0x100-aligned because of an ld issue. */
|
||||
/* The vbr needs to be 0x100-aligned because of an ld issue */
|
||||
. = ALIGN(0x100) ;
|
||||
_gint_vbr = . ;
|
||||
_bgint = . ;
|
||||
|
||||
/* Exception handler. */
|
||||
_gint_vbr = . ;
|
||||
_bgint = ABSOLUTE(.) ;
|
||||
|
||||
/* Exception handler */
|
||||
. = _gint_vbr + 0x100 ;
|
||||
*(.gint.exc)
|
||||
|
||||
/* TLB miss handler. */
|
||||
/* TLB miss handler */
|
||||
. = _gint_vbr + 0x400 ;
|
||||
*(.gint.tlb)
|
||||
|
||||
/* Interrupt handler. */
|
||||
/* Interrupt handler */
|
||||
. = _gint_vbr + 0x600 ;
|
||||
*(.gint.int)
|
||||
|
||||
_egint = . ;
|
||||
. = ALIGN(4);
|
||||
_egint = ABSOLUTE(.) ;
|
||||
} > realram
|
||||
|
||||
.gint_bss : AT(_gint_data + SIZEOF(.gint)) ALIGN(4) {
|
||||
_bgbss = ABSOLUTE(.) ;
|
||||
*(.gint.bss)
|
||||
_egbss = ABSOLUTE(.) ;
|
||||
} > realram
|
||||
}
|
||||
|
|
Binary file not shown.
Before Width: | Height: | Size: 24 KiB After Width: | Height: | Size: 24 KiB |
Binary file not shown.
Before Width: | Height: | Size: 3.1 KiB After Width: | Height: | Size: 3.1 KiB |
|
@ -11,27 +11,26 @@
|
|||
static void draw(int delay1, int delay2, int selected)
|
||||
{
|
||||
extern image_t res_opt_gray;
|
||||
extern font_t res_font_modern;
|
||||
uint32_t *vl = gray_lightVRAM();
|
||||
uint32_t *vd = gray_darkVRAM();
|
||||
|
||||
gclear();
|
||||
locate(1, 1, "Gray engine");
|
||||
|
||||
for(int i = 0; i < 36; i++)
|
||||
for(int i = 0; i < 64; i++)
|
||||
{
|
||||
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;
|
||||
int offset = (i << 2) + 3;
|
||||
vl[offset] = -(!(i & 16));
|
||||
vd[offset] = -(i < 32);
|
||||
}
|
||||
|
||||
locate(3, 3, "light");
|
||||
print(4, 4, "%d", delay1);
|
||||
text_configure(&res_font_modern, color_black);
|
||||
gtext(13, 17, "light");
|
||||
gtext(13, 31, "dark");
|
||||
|
||||
locate(3, 5, "dark");
|
||||
text_configure(NULL, color_black);
|
||||
print(4, 4, "%d", delay1);
|
||||
print(4, 6, "%d", delay2);
|
||||
|
||||
locate(3, selected ? 6 : 4, "\x02");
|
||||
|
|
|
@ -4,9 +4,8 @@
|
|||
#include <stdlib.h>
|
||||
#include <events.h>
|
||||
|
||||
static int draw_keyboard(volatile uint8_t *state)
|
||||
static void draw_keyboard(volatile uint8_t *state)
|
||||
{
|
||||
int pressed_keys = 0;
|
||||
int i, j, k, l;
|
||||
int x, y;
|
||||
|
||||
|
@ -30,7 +29,6 @@ static int draw_keyboard(volatile uint8_t *state)
|
|||
// Drawing a filled shape when the key is pressed.
|
||||
if(state[i] & (0x80 >> j))
|
||||
{
|
||||
pressed_keys++;
|
||||
for(k = -2; k <= 2; k++) for(l = -2; l <= 2; l++)
|
||||
if(abs(k) + abs(l) <= 2)
|
||||
dpixel(x + k, y + l, color_black);
|
||||
|
@ -51,8 +49,6 @@ static int draw_keyboard(volatile uint8_t *state)
|
|||
|
||||
// An horizontal line to separate parts of the keyboard.
|
||||
dline(5, 28, 32, 28, color_black);
|
||||
|
||||
return pressed_keys;
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
|
@ -66,7 +62,7 @@ typedef struct {
|
|||
int pressed;
|
||||
} history_t;
|
||||
|
||||
static int pressed_keys = -1;
|
||||
static int pressed_keys = 0;
|
||||
static int releases = 0;
|
||||
|
||||
static void history_push(history_t *h, event_t event)
|
||||
|
@ -120,19 +116,25 @@ static void draw_events(history_t *h)
|
|||
};
|
||||
|
||||
int y = 3;
|
||||
int y_limit = 8 - (h->pressed > h->size);
|
||||
|
||||
print(8, 2, "%d %d %d", pressed_keys, h->pressed, releases);
|
||||
extern font_t res_font_modern;
|
||||
text_configure(&res_font_modern, color_black);
|
||||
dtext(49, 12, "Key");
|
||||
dtext(89, 12, "Rep.");
|
||||
// dprint(49, 7, "%d %d %d", h->pressed, pressed_keys, releases);
|
||||
text_configure(NULL, color_black);
|
||||
dline(45, 18, 120, 18, color_black);
|
||||
|
||||
for(int i = 0; i < h->size; i++)
|
||||
for(int i = 0; i < h->size && y < y_limit; i++) if(h->data[i].key)
|
||||
{
|
||||
if(!h->data[i].key) continue;
|
||||
print(8, y, "%s", key_names[key_id(h->data[i].key)]);
|
||||
dtext(49, y * 8 - 4, key_names[key_id(h->data[i].key)]);
|
||||
if(h->data[i].repeats > 1)
|
||||
print(18, y, "%d", h->data[i].repeats);
|
||||
dprint(89, y * 8 - 4, "%d", h->data[i].repeats);
|
||||
y++;
|
||||
}
|
||||
|
||||
if(h->pressed > h->size) print(8, 8, "(more)");
|
||||
if(h->pressed > h->size) dtext(49, 52, "(more)");
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -149,15 +151,37 @@ void test_keyboard_events(void)
|
|||
};
|
||||
event_t event;
|
||||
|
||||
// There may be keys pressed when this test starts. We need to detect
|
||||
// which and create a valid history for them.
|
||||
volatile const uint8_t *buffer = keyboard_stateBuffer();
|
||||
|
||||
// For the purpose of this function we don't need to calculate the
|
||||
// other fields.
|
||||
event_t push_event = {
|
||||
.type = event_key_press,
|
||||
.key.code = KEY_AC_ON,
|
||||
};
|
||||
|
||||
// Finding all the pressed keys and pushing them in the history.
|
||||
if(buffer[0] & 1) history_push(&history, push_event);
|
||||
for(int row = 1; row <= 9; row++) for(int col = 0; col < 8; col++)
|
||||
{
|
||||
// Eliminate non-existing keys.
|
||||
if(col > 6 || col <= (row <= 4)) continue;
|
||||
|
||||
if(buffer[row] & (1 << col))
|
||||
{
|
||||
push_event.key.code = (col << 4) | row;
|
||||
history_push(&history, push_event);
|
||||
}
|
||||
}
|
||||
|
||||
while(1)
|
||||
{
|
||||
dclear();
|
||||
locate(1, 1, "Keyboard and events");
|
||||
|
||||
// There may be more than zero keys pressed when this test
|
||||
// starts. We need to detect this count automatically.
|
||||
int x = draw_keyboard(keyboard_stateBuffer());
|
||||
if(pressed_keys < 0) pressed_keys = x;
|
||||
draw_keyboard(keyboard_stateBuffer());
|
||||
|
||||
draw_events(&history);
|
||||
dupdate();
|
||||
|
|
|
@ -49,14 +49,14 @@ typedef struct
|
|||
{
|
||||
// This is the key code as defined in <keyboard.h> (a matrix code), and
|
||||
// probably what you need.
|
||||
uint32_t code;
|
||||
uint16_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;
|
||||
uint16_t id;
|
||||
// Character associated with the event key.
|
||||
int character;
|
||||
|
||||
} key_event_t;
|
||||
} __attribute__((packed, aligned(4))) key_event_t;
|
||||
|
||||
/*
|
||||
event_t
|
||||
|
@ -77,7 +77,7 @@ typedef struct
|
|||
timer_t *timer;
|
||||
};
|
||||
|
||||
} event_t;
|
||||
} __attribute__((packed, aligned(4))) event_t;
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
#define _INTERNALS_GINT_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <mpu.h>
|
||||
#include <gint.h>
|
||||
|
||||
//---
|
||||
|
@ -45,7 +46,7 @@ void gint_setvbr(uint32_t vbr, void (*setup)(void));
|
|||
Initializes gint. Loads the interrupt handler into the memory and sets
|
||||
the new vbr address.
|
||||
*/
|
||||
void gint_init(void);
|
||||
void gint_init(mpu_t mpu);
|
||||
|
||||
/*
|
||||
gint_quit()
|
||||
|
@ -135,101 +136,4 @@ void gint_restore_and_unlock_7305(environment_7305_t *env);
|
|||
volatile void *gint_reg_7705(gint_register_t reg);
|
||||
volatile void *gint_reg_7305(gint_register_t reg);
|
||||
|
||||
|
||||
|
||||
//---
|
||||
// Diagnostics
|
||||
// When diagnostics are enabled, gint saves runtime information to a
|
||||
// buffer in RAM, which by chance is held if the application crashes (I
|
||||
// don't really know why). This allows deeper debugging.
|
||||
//---
|
||||
|
||||
#ifdef GINT_DIAGNOSTICS
|
||||
|
||||
#include <mpu.h>
|
||||
|
||||
// Determining whether the SaveDisp() buffer actually contains gint diagnostics
|
||||
// is performed by checking a magic number (1/256 chance of failure) and the
|
||||
// validity of all fields in the diagnostic information. Formally, a picture
|
||||
// taken by SaveDisp() could fool the checks but this is *very* unlikely and
|
||||
// the diagnostics should only be read just after gint crashes or stops anyway.
|
||||
#define GINT_DIAGNOSTICS_MAGIC 0xb7
|
||||
|
||||
typedef enum
|
||||
{
|
||||
stage_startup = 0,
|
||||
stage_sections = 1,
|
||||
stage_mmu = 2,
|
||||
stage_gint = 3,
|
||||
stage_clock = 4,
|
||||
stage_ctors = 5,
|
||||
stage_running = 6,
|
||||
stage_leaving = 7,
|
||||
stage_dtors = 8,
|
||||
stage_terminated = 9,
|
||||
|
||||
} gint_stage_t;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
uint32_t address;
|
||||
uint32_t length;
|
||||
|
||||
} gint_memsection_t;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
// Magic number to check whether there is a diagnostic.
|
||||
uint8_t magic :8;
|
||||
// Unique counter that is incremented at each execution, allowing to
|
||||
// distinguish diagnostics output at different times if the application
|
||||
// crashes repeatedly.
|
||||
uint8_t counter :8;
|
||||
// How many work of initialization had been successfully done before
|
||||
// the application crashed.
|
||||
gint_stage_t stage :8;
|
||||
// What kind of MPU the library detected (undefined if the
|
||||
// initialization stage does not reach stage_gint).
|
||||
mpu_t mpu :8;
|
||||
|
||||
// Frequency of the main clocks, in MHz.
|
||||
uint8_t Bphi_f :8;
|
||||
uint8_t Iphi_f :8;
|
||||
uint8_t Pphi_f :8;
|
||||
|
||||
// What kind of exceptions occurred last.
|
||||
uint8_t excepts :8;
|
||||
uint8_t except_vect[12];
|
||||
// Last values held by registers SPC, SSR, EXPEVT / INTEVT2 / INTEVT,
|
||||
// and TEA.
|
||||
uint32_t spc;
|
||||
uint32_t ssr;
|
||||
uint32_t expevt;
|
||||
uint32_t tea;
|
||||
|
||||
// Gint version number, on the form 0xMMmmbbbb, where MM is the major
|
||||
// version, mm the minor version and bbbb the build number.
|
||||
uint32_t version;
|
||||
// Location of the VBR at the time of execution.
|
||||
uint32_t vbr_address;
|
||||
|
||||
// Memory map.
|
||||
uint32_t romdata;
|
||||
gint_memsection_t section_text;
|
||||
gint_memsection_t section_data;
|
||||
gint_memsection_t section_bss;
|
||||
gint_memsection_t section_gint;
|
||||
|
||||
} gint_diagnostics_t;
|
||||
|
||||
// This is somewhere inside the buffers of SaveDisp(), 3 bytes inside the first
|
||||
// buffer to be exact (that's to be 4-aligned).
|
||||
// This buffer is 1024-byte long so the logs must fit in 1021 bytes to be safe.
|
||||
// It looks like that this RAM area is generally not cleared when the
|
||||
// calculator reboots after a crash, even though it does not seem to work with
|
||||
// manual resets. Maybe it can provide useful information in some cases.
|
||||
#define gint_diagnostics() ((volatile gint_diagnostics_t *)0x88004d90)
|
||||
|
||||
#endif // GINT_DIAGNOSTICS
|
||||
|
||||
#endif // _INTERNALS_GINT_H
|
||||
|
|
37
include/internals/init.h
Normal file
37
include/internals/init.h
Normal file
|
@ -0,0 +1,37 @@
|
|||
//---
|
||||
// gint core module: init
|
||||
// Program initialization and display manipulation for the startup logs.
|
||||
//---
|
||||
|
||||
#ifndef _INTERNALS_INIT_H
|
||||
#define _INTERNALS_INIT_H
|
||||
|
||||
#include <display.h>
|
||||
|
||||
struct qdiv
|
||||
{
|
||||
uint32_t q, r;
|
||||
};
|
||||
|
||||
/* qdiv10() -- quickly divide by 10 */
|
||||
struct qdiv qdiv10(uint32_t n);
|
||||
|
||||
/* init_version() -- get a version string */
|
||||
const char *init_version(void);
|
||||
|
||||
/* init_stage() -- change the current init stage */
|
||||
void init_stage(const char *name);
|
||||
|
||||
/* init_halt() -- halt the program */
|
||||
void init_halt(void);
|
||||
|
||||
/* print() -- print text on a 21*8 grid */
|
||||
#define print(x, y, str) dtext((x) * 6 - 5, (y) * 8 - 8, (str))
|
||||
|
||||
/* print_dec() -- print a number in base 10 */
|
||||
void print_dec(int x, int y, int n, int digits);
|
||||
|
||||
/* print_hex() -- print a number in base 16 */
|
||||
void print_hex(int x, int y, uint32_t n, int digits);
|
||||
|
||||
#endif // _INTERNALS_INIT_H
|
|
@ -39,4 +39,11 @@ int getPressedKeys(volatile uint8_t *keyboard_state, int *keys, int count);
|
|||
void keyboard_updateState_7705(volatile uint8_t *state);
|
||||
void keyboard_updateState_7305(volatile uint8_t *state);
|
||||
|
||||
/*
|
||||
keyboard_interrupt()
|
||||
Answers an interrupt event by updating the keyboard state and
|
||||
generating the associated keyboard events.
|
||||
*/
|
||||
void keyboard_interrupt(void);
|
||||
|
||||
#endif // _INTERNALS_KEYBOARD_H
|
||||
|
|
26
include/internals/syscalls.h
Normal file
26
include/internals/syscalls.h
Normal file
|
@ -0,0 +1,26 @@
|
|||
//---
|
||||
// gint core module: syscalls
|
||||
// Some of the functionality still requires interacting with the system.
|
||||
//---
|
||||
|
||||
#ifndef _INTERNALS_SYSCALLS_H
|
||||
#define _INTERNALS_SYSCALLS_H
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
/* malloc() -- allocate data in heap */
|
||||
void *__malloc(size_t size);
|
||||
|
||||
/* free() -- free data allocated by malloc(), calloc() or realloc() */
|
||||
void __free(void *ptr);
|
||||
|
||||
/* realloc() -- reallocate a chunk of memory */
|
||||
void *__realloc(void *chunk, size_t new_size);
|
||||
|
||||
/* get_os_version() -- write the OS version in format MM.mm.pppp to a string */
|
||||
void __get_os_version(char *str);
|
||||
|
||||
/* system_menu() -- go back to menu, assuming the system has the control */
|
||||
void __system_menu(const void *vram);
|
||||
|
||||
#endif // _INTERNALS_SYSCALLS_H
|
|
@ -10,13 +10,6 @@
|
|||
extern font_t *font;
|
||||
extern color_t operator;
|
||||
|
||||
/*
|
||||
tales_init()
|
||||
Configures tales with the default font (which is part of gint).
|
||||
*/
|
||||
__attribute__((constructor))
|
||||
void tales_init(void);
|
||||
|
||||
/*
|
||||
getCharacterIndex()
|
||||
Returns the index of a character in a font data area depending on the
|
||||
|
|
|
@ -11,11 +11,9 @@
|
|||
*/
|
||||
typedef struct timer_t
|
||||
{
|
||||
// Current delay, how much time elapsed since last interrupt occurred,
|
||||
// and how many repeats are left.
|
||||
int ms_delay;
|
||||
int ms_elapsed;
|
||||
int repeats_left;
|
||||
// Current delay, how much time elapsed since last interrupt occurred.
|
||||
uint32_t ms_delay;
|
||||
uint32_t ms_elapsed;
|
||||
|
||||
// Is the virtual slot free? Is the virtual timer active?
|
||||
uint8_t used :1;
|
||||
|
@ -25,12 +23,14 @@ typedef struct timer_t
|
|||
uint8_t vsupport :1;
|
||||
// How many events do I have received but not executed?
|
||||
uint8_t events :4;
|
||||
// How many repeats are left.
|
||||
uint32_t repeats_left :24;
|
||||
|
||||
// Callback function (NULL for event-firing timers) and its argument.
|
||||
void *callback;
|
||||
void *argument;
|
||||
|
||||
} timer_t;
|
||||
} __attribute__((packed, aligned(4))) timer_t;
|
||||
|
||||
// Hardware timers.
|
||||
extern timer_t htimers[3];
|
||||
|
|
|
@ -12,8 +12,8 @@
|
|||
// care of assuming unknown MPUs are SH4, which is the more reasonable
|
||||
// option.
|
||||
//
|
||||
// In a general way, it is advised to always use the following
|
||||
// alternative (which gint does):
|
||||
// It is advised to always use the following alternative (which gint
|
||||
// does):
|
||||
//
|
||||
// if(isSH3())
|
||||
// {
|
||||
|
@ -54,7 +54,7 @@ extern const mpu_t MPU_CURRENT;
|
|||
// Quick SH3 test. It is safer to assume that an unknown model is SH4 because
|
||||
// SH3-based models are not produced anymore.
|
||||
#define isSH3() (MPU_CURRENT == mpu_sh7337 || MPU_CURRENT == mpu_sh7355)
|
||||
#define isSH4() !isSH3()
|
||||
#define isSH4() (!isSH3())
|
||||
|
||||
|
||||
|
||||
|
|
253
src/core/crt0.c
253
src/core/crt0.c
|
@ -1,253 +0,0 @@
|
|||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <setjmp.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include <internals/gint.h>
|
||||
#include <internals/mmu.h>
|
||||
#include <internals/clock.h>
|
||||
#include <clock.h>
|
||||
#include <gint.h>
|
||||
#include <internals/modules.h>
|
||||
|
||||
int main(void);
|
||||
|
||||
static void init(void);
|
||||
static void fini(void);
|
||||
|
||||
// Symbols provided by the linker script.
|
||||
extern uint32_t
|
||||
etext, btext, // Location of .text section
|
||||
bdata, edata, // Location of .data section
|
||||
bbss, ebss, // Location of .bss section
|
||||
bgint, egint, // Location of interrupt handler and gint data
|
||||
gint_vbr, // VBR address
|
||||
romdata; // ROM address of .data section contents
|
||||
|
||||
// This variable should be overwritten before being returned, so the default
|
||||
// value doesn't matter much.
|
||||
static int exit_code = EXIT_SUCCESS;
|
||||
static jmp_buf env;
|
||||
|
||||
// Exit handlers.
|
||||
void (*atexit_handlers[ATEXIT_MAX])(void);
|
||||
int atexit_index = 0;
|
||||
|
||||
|
||||
|
||||
/*
|
||||
start()
|
||||
Program entry point. Loads the data section into the memory and invokes
|
||||
main(). Also prepares the execution environment by initializing all the
|
||||
modules.
|
||||
*/
|
||||
__attribute__((section(".pretext.entry"))) int start(void)
|
||||
{
|
||||
#ifdef GINT_DIAGNOSTICS
|
||||
|
||||
// OK, so fill as much information as possible so that in case anything
|
||||
// goes wrong we can try to analyze what's been going on.
|
||||
volatile gint_diagnostics_t *dg = gint_diagnostics();
|
||||
|
||||
// All that follows is "safe" information that can be saved immediately
|
||||
// because it will never change anyway.
|
||||
// Basic environment description.
|
||||
dg->magic = GINT_DIAGNOSTICS_MAGIC;
|
||||
dg->counter = dg->counter + 1;
|
||||
dg->mpu = mpu_unknown;
|
||||
dg->version = (uint32_t)&GINT_VERSION;
|
||||
dg->stage = stage_startup;
|
||||
|
||||
// Exception records: what kind of exceptions / TLB faults / interrupts
|
||||
// occurred last to get some trace in case of crash.
|
||||
dg->excepts = 0;
|
||||
for(size_t i = 0; i < sizeof dg->except_vect; i++)
|
||||
dg->except_vect[i] = 0;
|
||||
dg->spc = 0x00000000;
|
||||
dg->ssr = 0x00000000;
|
||||
dg->expevt = 0x00000000;
|
||||
dg->tea = 0x00000000;
|
||||
|
||||
// Memory map: provides information about alignment and the relative
|
||||
// size and position of each section, as well as read-only / read-write
|
||||
// types of addresses.
|
||||
dg->section_text.address = (uint32_t)&btext;
|
||||
dg->section_text.length = (uint32_t)&etext - (uint32_t)&btext;
|
||||
dg->section_data.address = (uint32_t)&bdata;
|
||||
dg->section_data.length = (uint32_t)&edata - (uint32_t)&bdata;
|
||||
dg->section_bss.address = (uint32_t)&bbss;
|
||||
dg->section_bss.length = (uint32_t)&ebss - (uint32_t)&bbss;
|
||||
dg->romdata = (uint32_t)&romdata;
|
||||
|
||||
// Basic information about the running library.
|
||||
dg->vbr_address = (uint32_t)&gint_vbr;
|
||||
dg->section_gint.address = (uint32_t)&bgint;
|
||||
dg->section_gint.length = (uint32_t)&egint - (uint32_t)&bgint;
|
||||
#endif
|
||||
|
||||
// Clearing the .bss section.
|
||||
uint32_t *bss = &bbss;
|
||||
while(bss < &ebss) *bss++ = 0;
|
||||
|
||||
// Copying the .data section.
|
||||
uint32_t *data = &bdata, *src = &romdata;
|
||||
while(data < &edata) *data++ = *src++;
|
||||
|
||||
#ifdef GINT_DIAGNOSTICS
|
||||
dg->stage = stage_sections;
|
||||
#endif
|
||||
|
||||
// Trying to get the system to fill the TLB without editing it
|
||||
// directly, so that it does not go on rampage when finding out.
|
||||
mmu_pseudoTLBInit();
|
||||
|
||||
#ifdef GINT_DIAGNOSTICS
|
||||
// At this point it would be wise to include a copy of the TLB, but
|
||||
// it's already a huge array. Maybe later...
|
||||
/* TODO Debug TLB? */
|
||||
dg->stage = stage_mmu;
|
||||
#endif
|
||||
|
||||
// Initializing gint, which does several things:
|
||||
// - Detect the MPU and platform
|
||||
// - Initialize register addresses in a platform-independent way
|
||||
// - Save the current environment information in a large buffer
|
||||
// - Set up the interrupt handler and configure everything gint needs
|
||||
gint_init();
|
||||
|
||||
#ifdef GINT_DIAGNOSTICS
|
||||
dg->mpu = MPU_CURRENT;
|
||||
dg->stage = stage_gint;
|
||||
#endif
|
||||
|
||||
// Measuring clock frequencies.
|
||||
clock_measure();
|
||||
clock_measure_end();
|
||||
|
||||
#ifdef GINT_DIAGNOSTICS
|
||||
clock_config_t clock = clock_config();
|
||||
dg->Bphi_f = clock.Bphi_f / 1000000;
|
||||
dg->Iphi_f = clock.Iphi_f / 1000000;
|
||||
dg->Pphi_f = clock.Pphi_f / 1000000;
|
||||
dg->stage = stage_clock;
|
||||
#endif
|
||||
|
||||
// Calling global constructors.
|
||||
init();
|
||||
|
||||
#ifdef GINT_DIAGNOSTICS
|
||||
dg->stage = stage_ctors;
|
||||
dg->stage = stage_running;
|
||||
#endif
|
||||
|
||||
// Saving the execution state there.
|
||||
int x = setjmp(env);
|
||||
// If the program has just started, executing main(). Otherwise, the
|
||||
// exit code has already been set by abort() or similar.
|
||||
if(!x) exit_code = main();
|
||||
|
||||
|
||||
|
||||
#ifdef GINT_DIAGNOSTICS
|
||||
dg->stage = stage_leaving;
|
||||
#endif
|
||||
|
||||
/* TODO Flush and close opened streams. */
|
||||
|
||||
// Calling exit handlers and destructors.
|
||||
while(atexit_index > 0) (*atexit_handlers[--atexit_index])();
|
||||
fini();
|
||||
|
||||
#ifdef GINT_DIAGNOSTICS
|
||||
dg->stage = stage_dtors;
|
||||
#endif
|
||||
|
||||
// Un-initializing gint.
|
||||
gint_quit();
|
||||
|
||||
#ifdef GINT_DIAGNOSTICS
|
||||
dg->stage = stage_terminated;
|
||||
#endif
|
||||
|
||||
return exit_code;
|
||||
}
|
||||
|
||||
/*
|
||||
init()
|
||||
Calls the constructors.
|
||||
*/
|
||||
static void init(void)
|
||||
{
|
||||
extern void
|
||||
(*bctors)(void),
|
||||
(*ectors)(void);
|
||||
void (**func)(void) = &bctors;
|
||||
|
||||
while(func < &ectors)
|
||||
{
|
||||
(*(*func))();
|
||||
func++;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
fini()
|
||||
Calls the destructors.
|
||||
*/
|
||||
static void fini(void)
|
||||
{
|
||||
extern void
|
||||
(*bdtors)(void),
|
||||
(*edtors)(void);
|
||||
void (**func)(void) = &bdtors;
|
||||
|
||||
while(func < &edtors)
|
||||
{
|
||||
(*(*func))();
|
||||
func++;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
abort()
|
||||
Immediately ends the program without invoking the exit handlers.
|
||||
*/
|
||||
void abort(void)
|
||||
{
|
||||
exit_code = EXIT_FAILURE;
|
||||
|
||||
// Avoiding any exit handler call.
|
||||
atexit_index = 0;
|
||||
|
||||
longjmp(env, 1);
|
||||
}
|
||||
|
||||
/*
|
||||
exit()
|
||||
Ends the program and returns the given exit code status. Calls exit
|
||||
handlers before returning.
|
||||
Usually exit() would ask the operating system to stop the process but
|
||||
the fx-9860G executes only one program at a time and calls it as a
|
||||
function. Reaching the program end point is therefore an efficient way
|
||||
of achieving this goal while minimizing interaction with the operating
|
||||
system.
|
||||
*/
|
||||
void exit(int status)
|
||||
{
|
||||
exit_code = status;
|
||||
longjmp(env, 1);
|
||||
}
|
||||
|
||||
/*
|
||||
atexit()
|
||||
Registers a function to be called at normal program termination.
|
||||
*/
|
||||
int atexit(void (*function)(void))
|
||||
{
|
||||
if(atexit_index >= ATEXIT_MAX) return 1;
|
||||
|
||||
atexit_handlers[atexit_index++] = function;
|
||||
return 0;
|
||||
}
|
|
@ -21,11 +21,11 @@ static void setup(void)
|
|||
isSH3() ? gint_lock_and_setup_7705()
|
||||
: gint_lock_and_setup_7305();
|
||||
}
|
||||
void gint_init(void)
|
||||
void gint_init(mpu_t mpu)
|
||||
{
|
||||
// Detecting the MPU type. I don't like const-casting but this is still
|
||||
// Setting the MPU type. I don't like const-casting but this is still
|
||||
// better than allowing the user to change the variable by mistake.
|
||||
*((mpu_t *)&MPU_CURRENT) = getMPU();
|
||||
*((mpu_t *)&MPU_CURRENT) = mpu;
|
||||
// Loading the register addresses of the current platform.
|
||||
mod_init();
|
||||
|
||||
|
@ -89,23 +89,29 @@ void gint_quit(void)
|
|||
#include <display.h>
|
||||
#include <gray.h>
|
||||
|
||||
/*
|
||||
__system_menu()
|
||||
Updates the system's vram and triggers the calculator's main menu.
|
||||
*/
|
||||
void __system_menu(void *vram);
|
||||
|
||||
// Stores gint's configuration while the user visits the main menu.
|
||||
static environment_t switch_env;
|
||||
|
||||
/*
|
||||
gint_switch()
|
||||
Temporarily returns to the system's main menu.
|
||||
*/
|
||||
static environment_t switch_env;
|
||||
static void switch_(void)
|
||||
static void restore(void)
|
||||
{
|
||||
isSH3() ? gint_restore_and_unlock_7705(&switch_env.env_7705)
|
||||
: gint_restore_and_unlock_7305(&switch_env.env_7305);
|
||||
}
|
||||
void gint_switch(void)
|
||||
{
|
||||
// Save the current environment in a special buffer, so that we can
|
||||
// restore it if we ever come back to gint.
|
||||
isSH3() ? gint_save_7705(&switch_env.env_7705)
|
||||
: gint_save_7305(&switch_env.env_7305);
|
||||
// Give the control back to the system.
|
||||
|
||||
gint_setvbr(gint.system_vbr, stop);
|
||||
|
||||
// When returning to the add-in from the menu, the system displays the
|
||||
|
@ -120,5 +126,5 @@ void gint_switch(void)
|
|||
__system_menu(vram);
|
||||
|
||||
// If the user came back, restore the gint working environment.
|
||||
gint_setvbr(gint.gint_vbr, switch_);
|
||||
gint_setvbr(gint.gint_vbr, restore);
|
||||
}
|
||||
|
|
|
@ -9,7 +9,8 @@
|
|||
#include <mpu.h>
|
||||
#include <stdint.h>
|
||||
|
||||
const mpu_t MPU_CURRENT;
|
||||
/* Located in gint's uninitialized bss section */
|
||||
__attribute__((section(".gint.bss"))) const mpu_t MPU_CURRENT;
|
||||
|
||||
/*
|
||||
getMPU()
|
||||
|
|
|
@ -1,14 +1,25 @@
|
|||
/*
|
||||
gint core module: syscalls
|
||||
|
||||
All the system calls used by the library. Somehow "the less, the
|
||||
better".
|
||||
We have finally gotten rid of every obscure system-related syscalls!
|
||||
System calls (and the like) used by the library. The library should
|
||||
rely the least possible on the system, but sometimes using the syscalls
|
||||
is nothing of a nuisance.
|
||||
|
||||
For instance, using the malloc()-family syscalls is a bit annoying
|
||||
because it "locks" many functionalities. On the other hand, getting the
|
||||
SaveDisp() buffer addresses to store data here is not a problem since
|
||||
such data can very easily be relocated to static RAM.
|
||||
*/
|
||||
|
||||
/* Dynamic allocation */
|
||||
.global ___malloc
|
||||
.global ___free
|
||||
.global ___realloc
|
||||
|
||||
/* OS version */
|
||||
.global ___get_os_version
|
||||
|
||||
/* Return to menu */
|
||||
.global ___system_menu
|
||||
|
||||
|
||||
|
@ -34,6 +45,13 @@ ___realloc:
|
|||
nop
|
||||
1: .long 0xe6d
|
||||
|
||||
___get_os_version:
|
||||
mov.l syscall_table, r2
|
||||
mov.l 1f, r0
|
||||
jmp @r2
|
||||
nop
|
||||
1: .long 0x02ee
|
||||
|
||||
/*
|
||||
__system_menu()
|
||||
Brings one back to the system menu by putting KEY_MENU in the system's
|
||||
|
|
|
@ -1,9 +1,8 @@
|
|||
#include <display.h>
|
||||
|
||||
// Program video ram. It resides in BSS section, therefore it is cleared at
|
||||
// program initialization and stripped from the executable file.
|
||||
static uint32_t local_vram[256];
|
||||
uint32_t *vram = local_vram;
|
||||
/* Add-in monochrome vram in gint's uninitialized bss section */
|
||||
__attribute__((section(".gint.bss"))) static uint32_t vram_local[256];
|
||||
__attribute__((section(".gint.bss"))) uint32_t *vram;
|
||||
|
||||
/*
|
||||
display_getLocalVRAM()
|
||||
|
@ -16,7 +15,7 @@ uint32_t *vram = local_vram;
|
|||
*/
|
||||
inline uint32_t *display_getLocalVRAM(void)
|
||||
{
|
||||
return local_vram;
|
||||
return vram_local;
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -43,6 +42,6 @@ inline uint32_t *display_getCurrentVRAM(void)
|
|||
*/
|
||||
inline void display_useVRAM(uint32_t *ptr)
|
||||
{
|
||||
if((intptr_t)ptr & 3) return;
|
||||
if((uintptr_t)ptr & 3) return;
|
||||
vram = ptr;
|
||||
}
|
||||
|
|
|
@ -10,9 +10,12 @@
|
|||
#include <screen.h>
|
||||
#include <timer.h>
|
||||
#include <mpu.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
// Additional video rams used by the gray engine.
|
||||
static uint32_t internal_vrams[3][256];
|
||||
#ifdef GINT_STATIC_GRAY
|
||||
static uint32_t internals_vrams[3][256];
|
||||
#endif
|
||||
static uint32_t *vrams[4];
|
||||
|
||||
// Current vram set (0 or 1), delays of the light and dark frames respectively.
|
||||
|
@ -31,10 +34,7 @@ static timer_t *gray_timer = NULL;
|
|||
// Interrupt control and initialization.
|
||||
//---
|
||||
|
||||
/*
|
||||
gray_interrupt()
|
||||
Answers a timer interrupt. Swaps the buffers.
|
||||
*/
|
||||
/* gray_interrupt() -- switch buffers and update the screen */
|
||||
void gray_interrupt(void)
|
||||
{
|
||||
htimer_reload(timer_gray, delays[(~current) & 1]);
|
||||
|
@ -43,21 +43,33 @@ void gray_interrupt(void)
|
|||
current ^= 1;
|
||||
}
|
||||
|
||||
/*
|
||||
gray_init()
|
||||
Initializes the gray engine.
|
||||
*/
|
||||
__attribute__((constructor)) void gray_init(void)
|
||||
/* gray_init() -- setup the video ram buffers and timer delays */
|
||||
__attribute__((constructor)) static void gray_init(void)
|
||||
{
|
||||
vrams[0] = display_getLocalVRAM();
|
||||
#ifdef GINT_STATIC_GRAY
|
||||
vrams[1] = internal_vrams[0];
|
||||
vrams[2] = internal_vrams[1];
|
||||
vrams[3] = internal_vrams[2];
|
||||
|
||||
#else
|
||||
vrams[1] = NULL;
|
||||
vrams[2] = NULL;
|
||||
vrams[3] = NULL;
|
||||
#endif
|
||||
delays[0] = 912;
|
||||
delays[1] = 1343;
|
||||
}
|
||||
|
||||
/* gray_quit() -- Free the gray engine's heap-allocated video rams */
|
||||
__attribute__((destructor)) static void gray_quit(void)
|
||||
{
|
||||
#ifndef GINT_STATIC_GRAY
|
||||
free(vrams[1]);
|
||||
free(vrams[2]);
|
||||
free(vrams[3]);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
|
||||
//---
|
||||
|
@ -71,6 +83,14 @@ __attribute__((constructor)) void gray_init(void)
|
|||
*/
|
||||
void gray_start(void)
|
||||
{
|
||||
#ifndef GINT_STATIC_GRAY
|
||||
for(int i = 0; i < 4; i++)
|
||||
{
|
||||
if(!vrams[i]) vrams[i] = malloc(1024);
|
||||
/* Don't continue if any of the buffer is missing */
|
||||
if(!vrams[i]) return;
|
||||
}
|
||||
#endif
|
||||
if(runs) return;
|
||||
|
||||
gray_timer = htimer_setup(timer_gray, delays[0], timer_Po_64, 0);
|
||||
|
|
306
src/init/crt0.c
Normal file
306
src/init/crt0.c
Normal file
|
@ -0,0 +1,306 @@
|
|||
#include <stdlib.h>
|
||||
#include <setjmp.h>
|
||||
|
||||
#include <internals/gint.h>
|
||||
#include <internals/mmu.h>
|
||||
#include <internals/clock.h>
|
||||
|
||||
#include <display.h>
|
||||
#include <internals/init.h>
|
||||
#include <internals/modules.h>
|
||||
|
||||
/* We need some more functionality to generate these */
|
||||
#ifdef GINT_STARTUP_LOG
|
||||
#include <gint.h>
|
||||
#include <clock.h>
|
||||
#include <events.h>
|
||||
#include <internals/keyboard.h>
|
||||
#include <modules/interrupts.h>
|
||||
|
||||
#ifndef GINT_NO_SYSCALLS
|
||||
#include <internals/syscalls.h>
|
||||
#endif
|
||||
#endif
|
||||
|
||||
int main(void);
|
||||
|
||||
static void init(void);
|
||||
static void fini(void);
|
||||
|
||||
// Symbols provided by the linker script.
|
||||
extern uint32_t
|
||||
bdata, sdata, // Location of .data section
|
||||
bbss, sbss, // Location of .bss section
|
||||
bgint, egint, // Location of interrupt handler and gint data
|
||||
bgbss, egbss, // Location of interrupt handler and gint data
|
||||
gint_vbr, // VBR address
|
||||
romdata, // ROM address of .data section contents
|
||||
gint_data; // ROM address of .gint section contents
|
||||
extern void
|
||||
(*bctors)(void), (*ectors)(void), // Constructors
|
||||
(*bdtors)(void), (*edtors)(void); // Destructors
|
||||
|
||||
// This variable should be overwritten before being returned, so the default
|
||||
// value doesn't matter much.
|
||||
static int exit_code = EXIT_SUCCESS;
|
||||
static jmp_buf env;
|
||||
|
||||
// Exit handlers.
|
||||
static void (*atexit_handlers[ATEXIT_MAX])(void);
|
||||
static int atexit_index = 0;
|
||||
|
||||
#include <internals/display.h>
|
||||
|
||||
/*
|
||||
start()
|
||||
Program entry point. Loads the data section into the memory and invokes
|
||||
main(). Also prepares the execution environment by initializing all the
|
||||
modules.
|
||||
*/
|
||||
__attribute__((section(".pretext.entry"))) int start(void)
|
||||
{
|
||||
/* Configure the display output or we won't get anywhere - this is also
|
||||
needed for the add-in even if the startup logs are disabled */
|
||||
display_useVRAM(display_getLocalVRAM());
|
||||
text_configure(NULL, color_black);
|
||||
|
||||
#ifdef GINT_STARTUP_LOG
|
||||
/* Start outputting information on the screen so that we can quickly
|
||||
debug anything that would fail */
|
||||
uint32_t rom_size = ((uint32_t)&romdata - 0x00300000) >> 10;
|
||||
uint32_t ram_size = ((uint32_t)&gint_data - (uint32_t)&romdata)
|
||||
+ ((uint32_t)&sbss);
|
||||
uint32_t rram_size = ((uint32_t)&egbss - (uint32_t)&gint_vbr);
|
||||
|
||||
dclear();
|
||||
init_stage("Startup");
|
||||
dupdate();
|
||||
|
||||
print(1, 1, init_version());
|
||||
print(1, 2, "ROM RAM RRAM");
|
||||
print(4, 3, "k c d");
|
||||
print_dec(1, 3, rom_size, 3);
|
||||
print_dec(6, 3, ram_size, 4);
|
||||
print_dec(11, 3, rram_size, 4);
|
||||
print_dec(17, 3, &ectors - &bctors, 2);
|
||||
print_dec(20, 3, &edtors - &bdtors, 2);
|
||||
dupdate();
|
||||
#endif
|
||||
|
||||
/* Determining the processor type */
|
||||
mpu_t mpu = getMPU();
|
||||
|
||||
#ifdef GINT_STARTUP_LOG
|
||||
const char *mpu_names[] = { "SH7337", "SH7355", "SH7305", "SH7724" };
|
||||
if(mpu >= 1 && mpu <= 4) print(16, 2, mpu_names[mpu - 1]);
|
||||
else print_dec(16, 2, mpu, 6);
|
||||
dupdate();
|
||||
#endif
|
||||
|
||||
/* Make sure the MPU is of a valid type */
|
||||
if(!mpu || mpu > 4) init_halt();
|
||||
|
||||
#ifdef GINT_STARTUP_LOG
|
||||
init_stage(" MMU");
|
||||
dupdate();
|
||||
#endif
|
||||
|
||||
/* Try to get the TLB filled by the system by accessing all the pages
|
||||
while the system still answers TLB misses */
|
||||
mmu_pseudoTLBInit();
|
||||
|
||||
#ifdef GINT_STARTUP_LOG
|
||||
/* Read the TLB and count how much memory has been mapped */
|
||||
/* TODO: Debug TLB at startup */
|
||||
print(1, 4, "MMU ROM:???k RAM:???k");
|
||||
|
||||
init_stage("Section");
|
||||
dupdate();
|
||||
#endif
|
||||
|
||||
/* Copying the data section, then clearing the bss section. This
|
||||
doesn't affect the previously used variables, which are stored in
|
||||
another section for this purpose */
|
||||
volatile uint32_t *data = &bdata;
|
||||
volatile uint32_t *data_end = (void *)&bdata + (int)&sdata;
|
||||
volatile uint32_t *src = &romdata;
|
||||
while(data < data_end) *data++ = *src++;
|
||||
volatile uint32_t *bss = &bbss;
|
||||
volatile uint32_t *bss_end = (void *)&bbss + (int)&sbss;
|
||||
while(bss < bss_end) *bss++ = 0;
|
||||
|
||||
#ifdef GINT_STARTUP_LOG
|
||||
init_stage("Handler");
|
||||
dupdate();
|
||||
#endif
|
||||
|
||||
/* Initialize gint and debug all the interrupt priorities */
|
||||
gint_init(mpu);
|
||||
|
||||
#ifdef GINT_STARTUP_LOG
|
||||
if(isSH3())
|
||||
{
|
||||
print(1, 5, "ABCD");
|
||||
print_hex( 6, 5, INTC._7705.iprs.IPRA->word, 4);
|
||||
print_hex(10, 5, INTC._7705.iprs.IPRB->word, 4);
|
||||
print_hex(14, 5, INTC._7705.iprs.IPRC->word, 4);
|
||||
print_hex(18, 5, INTC._7705.iprs.IPRD->word, 4);
|
||||
print(1, 6, "EFGH");
|
||||
print_hex( 6, 6, INTC._7705.iprs.IPRE->word, 4);
|
||||
print_hex(10, 6, INTC._7705.iprs.IPRF->word, 4);
|
||||
print_hex(14, 6, INTC._7705.iprs.IPRG->word, 4);
|
||||
print_hex(18, 6, INTC._7705.iprs.IPRH->word, 4);
|
||||
}
|
||||
else
|
||||
{
|
||||
print(1, 5, "ACFG");
|
||||
print_hex( 6, 5, INTC._7305.iprs->IPRA.word, 4);
|
||||
print_hex(10, 5, INTC._7305.iprs->IPRC.word, 4);
|
||||
print_hex(14, 5, INTC._7305.iprs->IPRF.word, 4);
|
||||
print_hex(18, 5, INTC._7305.iprs->IPRG.word, 4);
|
||||
print(1, 6, "HJKL");
|
||||
print_hex( 6, 6, INTC._7305.iprs->IPRH.word, 4);
|
||||
print_hex(10, 6, INTC._7305.iprs->IPRJ.word, 4);
|
||||
print_hex(14, 6, INTC._7305.iprs->IPRK.word, 4);
|
||||
print_hex(18, 6, INTC._7305.iprs->IPRL.word, 4);
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Checking that at least the required interrupts are enabled */
|
||||
if(isSH3()
|
||||
? (!INTC._7705.iprs.IPRA->word)
|
||||
: (!INTC._7305.iprs->IPRA.word || !INTC._7305.iprs->IPRK.word)
|
||||
) init_halt();
|
||||
|
||||
#ifdef GINT_STARTUP_LOG
|
||||
init_stage(" Clocks");
|
||||
dupdate();
|
||||
#endif
|
||||
|
||||
/* Measuring the clock speed */
|
||||
clock_measure();
|
||||
clock_measure_end();
|
||||
|
||||
#ifdef GINT_STARTUP_LOG
|
||||
clock_config_t clock = clock_config();
|
||||
print(1, 7, "Clock I B P");
|
||||
print_dec( 8, 7, clock.Iphi_f / 1000000, 2);
|
||||
print_dec(12, 7, clock.Bphi_f / 1000000, 2);
|
||||
print_dec(16, 7, clock.Pphi_f / 1000000, 2);
|
||||
|
||||
init_stage("Boot OK");
|
||||
dupdate();
|
||||
|
||||
/* Displaying some interrupt controller config */
|
||||
if(isSH3())
|
||||
{
|
||||
print(1, 8, "INTC:0");
|
||||
print_hex(7, 8, INTC._7705.ICR1->word, 4);
|
||||
}
|
||||
else
|
||||
{
|
||||
print(1, 8, "INTC:");
|
||||
print_hex(6, 8, INTC._7305.ICR0->word, 4);
|
||||
print_hex(10, 8, (INTC._7305.USERIMASK->lword >> 4) & 0xf, 1);
|
||||
}
|
||||
|
||||
#ifndef GINT_NO_SYSCALLS
|
||||
/* Print the OS version */
|
||||
char version[11];
|
||||
__get_os_version(version);
|
||||
version[2] = version[3];
|
||||
version[3] = version[4];
|
||||
version[4] = version[6];
|
||||
version[5] = version[7];
|
||||
version[6] = version[8];
|
||||
version[7] = version[9];
|
||||
version[8] = 0;
|
||||
print(12, 8, "OS");
|
||||
print(14, 8, version);
|
||||
#endif
|
||||
|
||||
dupdate();
|
||||
#endif
|
||||
|
||||
/* Call the global constructors */
|
||||
init();
|
||||
|
||||
#ifdef GINT_STARTUP_LOG
|
||||
/* Keep this visible for a second or so */
|
||||
keyboard_interrupt();
|
||||
if(pollevent().type != event_none) getkey();
|
||||
#endif
|
||||
|
||||
/* Otherwise, call main() and get the show on the road! */
|
||||
int x = setjmp(env);
|
||||
if(!x) exit_code = main();
|
||||
|
||||
while(atexit_index > 0) (*atexit_handlers[--atexit_index])();
|
||||
fini();
|
||||
|
||||
gint_quit();
|
||||
|
||||
return exit_code;
|
||||
}
|
||||
|
||||
/* init() -- call the constructors */
|
||||
static void init(void)
|
||||
{
|
||||
extern void
|
||||
(*bctors)(void),
|
||||
(*ectors)(void);
|
||||
void (**func)(void) = &bctors;
|
||||
|
||||
while(func < &ectors) (*(*func++))();
|
||||
}
|
||||
|
||||
/* fini() -- call the destructors */
|
||||
static void fini(void)
|
||||
{
|
||||
extern void
|
||||
(*bdtors)(void),
|
||||
(*edtors)(void);
|
||||
void (**func)(void) = &bdtors;
|
||||
|
||||
while(func < &edtors) (*(*func++))();
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
abort()
|
||||
Immediately ends the program without invoking the exit handlers.
|
||||
*/
|
||||
void abort(void)
|
||||
{
|
||||
exit_code = EXIT_FAILURE;
|
||||
// Avoiding any exit handler call.
|
||||
atexit_index = 0;
|
||||
longjmp(env, 1);
|
||||
}
|
||||
|
||||
/*
|
||||
exit()
|
||||
Ends the program and returns the given exit code status. Calls exit
|
||||
handlers before returning.
|
||||
Usually exit() would ask the operating system to stop the process but
|
||||
the fx-9860G executes only one program at a time and calls it as a
|
||||
function. Reaching the program end point thus efficiently exits.
|
||||
*/
|
||||
void exit(int status)
|
||||
{
|
||||
exit_code = status;
|
||||
longjmp(env, 1);
|
||||
}
|
||||
|
||||
/*
|
||||
atexit()
|
||||
Registers a function to be called at normal program termination.
|
||||
*/
|
||||
int atexit(void (*function)(void))
|
||||
{
|
||||
if(atexit_index >= ATEXIT_MAX) return 1;
|
||||
|
||||
atexit_handlers[atexit_index++] = function;
|
||||
return 0;
|
||||
}
|
90
src/init/util.c
Normal file
90
src/init/util.c
Normal file
|
@ -0,0 +1,90 @@
|
|||
#include <internals/init.h>
|
||||
#include <string.h>
|
||||
#include <clock.h>
|
||||
#include <gint.h>
|
||||
|
||||
/* qdiv10() -- quickly divide by 10 */
|
||||
struct qdiv qdiv10(uint32_t n)
|
||||
{
|
||||
uint32_t magic10 = 0x1999999a;
|
||||
struct qdiv result;
|
||||
|
||||
__asm__(
|
||||
"dmuls.l %1, %2 \n\t"
|
||||
"sts mach, %0 "
|
||||
: "=r"(result.q)
|
||||
: "r"(n), "r"(magic10)
|
||||
: "macl", "mach"
|
||||
);
|
||||
|
||||
result.r = n - 10 * result.q;
|
||||
return result;
|
||||
}
|
||||
|
||||
/* init_version() -- get a version string */
|
||||
const char *init_version(void)
|
||||
{
|
||||
static char data[14];
|
||||
uint32_t s = (uint32_t)&GINT_VERSION;
|
||||
|
||||
/* Force the string constant to reside in ROM because we haven't
|
||||
initialized the data section yet */
|
||||
memcpy(data, "gint #0.0-000", 14);
|
||||
|
||||
/* Quickly get the three digits of the build number */
|
||||
struct qdiv x = qdiv10(s & 0xffff);
|
||||
struct qdiv y = qdiv10(x.q);
|
||||
|
||||
data[5] = (s & 0xff000000) >> 24;
|
||||
data[6] += ((s & 0x00f00000) >> 20);
|
||||
data[8] += ((s & 0x000f0000) >> 16);
|
||||
data[10] += y.q;
|
||||
data[11] += y.r;
|
||||
data[12] += x.r;
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
/* init_stage() -- change the current init stage */
|
||||
void init_stage(const char *name)
|
||||
{
|
||||
drect(85, 0, 127, 7, color_white);
|
||||
print(15, 1, name);
|
||||
}
|
||||
|
||||
/* init_halt() -- halt the program */
|
||||
void init_halt(void)
|
||||
{
|
||||
while(1) sleep();
|
||||
}
|
||||
|
||||
/* print_dec() -- print a number in base 10 */
|
||||
void print_dec(int x, int y, int n, int digits)
|
||||
{
|
||||
char str[20];
|
||||
str[digits] = 0;
|
||||
|
||||
while(--digits >= 0)
|
||||
{
|
||||
struct qdiv d = qdiv10(n);
|
||||
str[digits] = '0' + d.r;
|
||||
n = d.q;
|
||||
}
|
||||
|
||||
print(x, y, str);
|
||||
}
|
||||
|
||||
/* print_hex() -- print a number in base 16 */
|
||||
void print_hex(int x, int y, uint32_t n, int digits)
|
||||
{
|
||||
char str[20];
|
||||
str[digits] = 0;
|
||||
|
||||
while(--digits >= 0)
|
||||
{
|
||||
str[digits] = (n & 0xf) + '0' + 39 * ((n & 0xf) > 9);
|
||||
n >>= 4;
|
||||
}
|
||||
|
||||
print(x, y, str);
|
||||
}
|
|
@ -16,7 +16,9 @@ int getkey(void)
|
|||
getkey_shift_modifier |
|
||||
getkey_alpha_modifier |
|
||||
getkey_manage_backlight |
|
||||
#ifndef GINT_NO_SYSCALLS
|
||||
getkey_task_switch |
|
||||
#endif
|
||||
getkey_repeat_arrow_keys,
|
||||
|
||||
0
|
||||
|
@ -105,12 +107,14 @@ int getkey_opt(getkey_option_t options, int delay_ms)
|
|||
break;
|
||||
|
||||
case event_key_release:
|
||||
#ifndef GINT_NO_SYSCALLS
|
||||
if((options & getkey_task_switch) && event.key.code == KEY_MENU
|
||||
&& !modifier)
|
||||
{
|
||||
gint_switch();
|
||||
continue;
|
||||
}
|
||||
#endif
|
||||
|
||||
if((int)event.key.code != last_key) break;
|
||||
last_key = KEY_NONE;
|
||||
|
|
|
@ -34,45 +34,6 @@ timer_t *vtimer = NULL;
|
|||
// Interrupt management.
|
||||
//---
|
||||
|
||||
static inline void push_press(int keycode)
|
||||
{
|
||||
uint32_t id = key_id(keycode);
|
||||
|
||||
event_t event = {
|
||||
.type = event_key_press,
|
||||
.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.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.code = keycode,
|
||||
.key.id = id,
|
||||
.key.character = key_char(keycode),
|
||||
};
|
||||
event_push(event);
|
||||
}
|
||||
|
||||
/*
|
||||
keyboard_interrupt()
|
||||
Callback function for keyboard update; called by the timer manager when
|
||||
|
|
|
@ -1,54 +0,0 @@
|
|||
#include <keyboard.h>
|
||||
#include <internals/keyboard.h>
|
||||
#include <events.h>
|
||||
|
||||
/*
|
||||
multigetkey()
|
||||
Listens the keyboard for simultaneous key hits. Uses the same options
|
||||
as getkey_opt().
|
||||
multigetkey() fills the 'keys' array with 'count' key codes, adding
|
||||
KEY_NOKEY if less than 'count' keys are pressed.
|
||||
Be aware that rectangle and column effects can make multigetkey() read
|
||||
unpressed keys as pressed (see documentation for more information).
|
||||
Setting count = 3 is generally safe.
|
||||
The function returns after 'max_cycles' if no key is pressed.
|
||||
*/
|
||||
static void multigetkey_wait(int *cycles)
|
||||
{
|
||||
while(!interrupt_flag) sleep();
|
||||
interrupt_flag = 0;
|
||||
|
||||
if(*cycles > 0) (*cycles)--;
|
||||
}
|
||||
void multigetkey(int *keys, int count, int cycles)
|
||||
{
|
||||
event_t event;
|
||||
int number = 0;
|
||||
|
||||
if(count <= 0) return;
|
||||
if(cycles <= 0) cycles = -1;
|
||||
|
||||
while(cycles != 0)
|
||||
{
|
||||
number = getPressedKeys(keyboard_state, keys, count);
|
||||
|
||||
// We want to update the last key data when multigetkey()
|
||||
// returns a single key, because getkey() could be called a
|
||||
// short time after we return, and send a new event for this
|
||||
// key.
|
||||
if(number == 1)
|
||||
{
|
||||
last_key = keys[0];
|
||||
last_repeats = 0;
|
||||
last_time = 0;
|
||||
}
|
||||
|
||||
if(number) break;
|
||||
multigetkey_wait(&cycles);
|
||||
}
|
||||
|
||||
do event = pollevent();
|
||||
while(event.type != event_none);
|
||||
|
||||
return;
|
||||
}
|
|
@ -3,7 +3,7 @@
|
|||
#pragma GCC diagnostic ignored "-Wunused-parameter"
|
||||
|
||||
#ifndef GINT_NO_SYSCALLS
|
||||
void __free(void *ptr);
|
||||
#include <internals/syscalls.h>
|
||||
#endif
|
||||
|
||||
/*
|
||||
|
@ -12,6 +12,9 @@ void __free(void *ptr);
|
|||
*/
|
||||
void free(void *ptr)
|
||||
{
|
||||
// Just to be sure.
|
||||
if(!ptr) return;
|
||||
|
||||
#ifndef GINT_NO_SYSCALLS
|
||||
__free(ptr);
|
||||
#endif
|
||||
|
|
|
@ -7,8 +7,8 @@
|
|||
*/
|
||||
|
||||
#ifndef GINT_NO_SYSCALLS
|
||||
#include <internals/syscalls.h>
|
||||
|
||||
void *__malloc(size_t size);
|
||||
void *malloc(size_t size)
|
||||
{
|
||||
return __malloc(size);
|
||||
|
|
|
@ -6,8 +6,8 @@
|
|||
*/
|
||||
|
||||
#ifndef GINT_NO_SYSCALLS
|
||||
#include <internals/syscalls.h>
|
||||
|
||||
void *__realloc(void *ptr, size_t size);
|
||||
void *realloc(void *ptr, size_t size)
|
||||
{
|
||||
return __realloc(ptr, size);
|
||||
|
|
|
@ -4,17 +4,10 @@
|
|||
#include <ctype.h>
|
||||
#include <gray.h>
|
||||
|
||||
font_t *font = NULL;
|
||||
color_t operator;
|
||||
|
||||
/*
|
||||
tales_init()
|
||||
Configures tales with the default font (which is part of gint).
|
||||
*/
|
||||
void tales_init(void)
|
||||
{
|
||||
text_configure(NULL, color_black);
|
||||
}
|
||||
/* Put these in gint's uninitialized bss section so that text rendering can be
|
||||
used before the library is fully initialized */
|
||||
__attribute__((section(".gint.bss"))) font_t *font = NULL;
|
||||
__attribute__((section(".gint.bss"))) color_t operator;
|
||||
|
||||
/*
|
||||
getCharacterIndex()
|
||||
|
|
2
version
2
version
|
@ -1 +1 @@
|
|||
beta-0.9-410
|
||||
beta-0.9-566
|
||||
|
|
Loading…
Reference in a new issue