Special Feature

A T-Kernel Primer

- Creating User Programs and Customizing -


The T-Engine Forum made public on January 2004 the "T-Kernel" real-time operating system that serves as the so-called nucleus of the T-Engine Project, which makes as its greatest objective the distribution of embedded middleware [1]. Because the method of obtaining and the method of building T-Kernel have been explained in "Releasing T-Kernel Source Code" in Vol. 86 of this magazine, on this occasion we will give an explanation centering on the method of creating user programs on top of T-Kernel and the methods of customizing the kernel (the addition and deletion of system calls).

The Methods for Obtaining and Building T-Kernel

First, we will explain both the method of obtaining T-Kernel and the method of building it.

The T-Kernel source code can be obtained from the T-Engine Forum's Web page (http://www.t-engine.org/). When you select "Subscription to T-Kernel," consent to the license, and fill in the required items, an e-mail that shows the download page will be sent to you. When you access the page mentioned in that e-mail, you can download the T-Kernel source code.

Let's try actually building and running the kernel from the downloaded source code. In running as is the downloaded T-Kernel, a T-Engine development kit is needed. Here, we will decide to use the GNU base development environment [2] attached to the development kit and build T-Kernel for use with SH7727 after decompressing the source code directly below the home directory.

If you are successful in decompressing, a directory called tkernel_source is created. We register this in the environment variable BD. Outside of this, we set for future keeping the environment variables of the type shown in Table 1.

Table 1 Environment Variables Necessary in Constructing T-Kernel

Environment Variable

Set Value in This Article

Supplement

BD

~/tkernel_source
Top of T-Kernel source code

GNUs

/usr
GNU-related tools

GNU_BD

/usr/local/te/tool/Linux-i686
Base directory of GNU-related tools used in cross development

GNUsh

${GNU_BD}/sh-unknown-tkernel
SH7727 compiler, etc.

GCC_EXEC_PREFIX

${GNU_BD}/lib/gcc-lib
gcc-related directory

We compile T-Kernel in the order of the libraries, and then the main body. We build the libraries by executing make in the ~/tkernel_source/lib/build/std_sh7727 directory, and we build the main body by executing make in the ~/tkernel_source/tkernel/sysmain/build/std_sh7727 directory.

When make finishes, a file called kernel-rom.mot is created above the current directory. This is the T-Kernel object file.

Next we create a RomInfo object. When we execute make above the ~/tkernel_source/config/build/std_sh7727 directory, rominfo.mot, which is the object file of RomInfo, is created.

Finally, we transmit the two objects we created, rominfo.mot and kernel-rom.mot, in this order to the Flash ROM in T-Engine. In writing into the Flash ROM, we normally use the FlashLoad (FLLO) command of T-Engine. Because there are cases where things are different, please look at the development kit's manual for details.

The Method of Creating user Programs on Top of T-Kernel

Basically, we create user programs in the ~/tkernel_source/kernel/usermain/ directory.

When T-Kernel finishes the initialization of the kernel, it calls up the usermain() function that is in usermain.c of the same directory. In a state in which you have downloaded and expanded T-Kernel, this usermain() function is defined in the manner of a list. The system outputs the message "Push any key to shutdown the T-Kernel" in the debug message, waits for something from an input on top of the debug console, and if there is an input, it terminates T-Kernel.

Well then, let's try building a puzzle game on top of T-Kernel. We will decide to create it with a configuration of the type in Fig. 1.

Figure 1. Configuration of the puzzle game

The initial task is the usermain() function that T-Kernel starts up. As in the comments in List 1, stopping at just starting up the initial task of the user program is recommended for the processing carried out with the usermain() function. For this reason, the initial task waits for the system to start up the main task and for its termination. The main task starts up the cyclic handler used for calculating time and the key/touch panel monitoring task that exists in T-Engine. The cyclic handler starts up every 100 milliseconds, and it rewrites the screen every second. The key/touch panel monitoring task is a task that waits for an input from the buttons (hereafter called keys) and touch panel installed on T-Engine and relays that to the main task. We use a message buffer for the purpose of communicating events from the key/touch panel monitoring task to the main task.

List 1 The main function usermain() of the user program (portion extracted)
/*
 * Entry routine for the user application.
 * At this point, Initialize and start the user application.
 *
 * Entry routine is called from the initial task for Kernel,
 * so system call for stopping the task should not be issued
 * from the contents of entry routine. 
 * We recommend that: 
 * (1)'usermain()' only generates the user initial task.
 * (2)initialize and start the user application by the user
 * initial task.
 */

EXPORT INT usermain( void )
{

    tm_putstring("Push any key to shutdown the T-Kernel.\n");
    tm_getchar(-1);


    return 0;

}

The initial task runs at the very low priority level of priority level 138 (the highest priority level is 1, and the lowest priority level is 140). If you start up a task with a higher priority level than this priority level, the initial task stands by as is in executable status. After this, when the initial task changes to execution status, the system escapes the usermain() function, and T-Kernel just as it is switches into termination processing. For that reason, it is necessary to make it so that the initial task does not change to execution status outside of when you are making T-Kernel terminate.

List 2 The usermain() function of the puzzle game
/* Initial task */
IMPORT INT usermain( void );
/* User initial task main processing */
IMPORT INT main_task( INT stcd, VP exinf );


/* Main task definition */
EXPORT const T_CTSK mtask =  {
       NULL,                   /* exinf */
       TA_HLNG|TA_RNG0,        /* tskatr */
       (FP)main_task,          /* task */
       3,                      /* itskpri */
       8*1024,                 /* stksz */
       0,                      /* sstksz */
       NULL,                   /* stkptr */
       NULL,                   /* uatb */
       0,                      /* lsid */
       0                       /* resid */

};


/* itskid - initial task
 * mtskid - main task, main process task
 * ktskid - key, touch panel monitoring task */
EXPORT ID itskid. mtskid, ktskid;


/* Initial task */
EXPORT INT usermainj( void )
{
  /* Obtain self task ID for future keeping */
  itskid = tk_get_tid();

  /* Create main task and start it up */
  mtskid = tk_cre_tsk((T_CTSK*)&mtask);
  tk_sta_tsk(mtskid, 0);

  /* Put self to sleep after starting up the main task
   * Wait for being awakened when the main task terminates */
  tk_slp_tsk(TMO_FEVR);

  /* Termination */
  return -1;
}

List 2 is the usermain() function of the puzzle game. Here, after starting up the main task of the user program, we put the initial task into wait status using the tk_slp_tsk() system call. This is a measure for the purpose of preventing the initial task from changing to execution status in which the main task has moved into wait status by means of a semaphore wait, etc., while the initial task remains in executable status. When the user program terminates, the system moves to normal T-Kernel termination processing.

List 3 Main task part of the puzzle game
/* Key, touch panel monitoring definitions */
EXPORT const T_CTSK ktask = {
       NULL,                /* exinf */
       TA_HLNG|TA_RNG0,     /* tskatr */
       (FP)&kbpd_task,      /* task */
       4,                   /* itskpri */
       8*1024,              /* stksz */
       0,                   /* sstksz */
       NULL,                /* stkptr */
       NULL,                /* uatb */
       0,                   /* lsid */
       0                    /* resid */
};

/* itskid - initial task
 * mtskid - main task, main process task
 * ktskid - key, touch panel monitoring task */
EXPORT ID itskid, mtskid, ktskid;

/* Cyclic handler */
EXPORT ID cycid;
EXPORT T_CCYC ccyc = {
   NULL,            /* exinf */
   TA_HLNG,         /* cycatr */
   (FP)&cyclic_hdr, /* cychdr */
   100,             /* cyctim */
   0                /* cycphs */
};

/* Message buffer */
EXPORT ID mbfid;
EXPORT T_CMBF cmbf = {
   NULL,     /* exinf */
   TA_TPRI,  /* mkfatr */
   32,       /* bufsz */
   4         /* maxmsz */
};


/* Main task */
EXPORT INT main_task( INT stcd, VP exinf )
{
   /* --------------------------------------------------------------------- */
   /* Various initialization processes */
  
   /* Initialize keyboard, LCD */
   init_kbpd();
   init_screen();

   /* Create input monitoring task */
   ktskid = tk_cre_tsk((T_CTSK*)&ktask);
   tk_sta_tsk(ktskid, 0);
   
   /* Create timer handler */
   cycid = tk_cre_cyc(&ccyc);
   
   /* Create message buffer */
   mbfid = tk_cre_mbf(&cmbf);
   /* --------------------------------------------------------------------- */
   /* Main game loop */
   while(1)
   {
      /* Initialize */
      init_game();
      /* Main game process */
      if(game_proc()==0) break;
      /* In a case where it's been cleared, raise the level and restart */
      lev++;
   }
  
   /* --------------------------------------------------------------------- */
   /* Termination */
   
   /* Delete resources */
   tk_del_cyc(cycid);
   tk_del_mbf(mbfid);
   tk_ter_tsk(ktskid);
   tk_del_tsk(ktskid);

   /* Wake up the initial task and terminate self */
   tk_rel_wai(itskid);
   tk_exd_tsk();

   /* Does not reach here */
   return 0;
}

List 3 is the main task part. Here, we define the cyclic handler and the key/touch panel monitoring task, and we start up the touch panel monitoring task. We carry out the startup and suspension of the cyclic handler with the game_proc() function, which is the main body of the game.

Figure 2. Puzzle game screen

This puzzle game was implemented using a part of the sample code attached to the T-Engine development kits. When we compile and execute this, a screen like the one in Fig. 2 appears. There are red and blue panels on the screen, and when you click a panel, the color of your panel and neighboring panels reverse. When you make them all the same color, it will be clear. The light blue square is the cursor. using the SW1 key you move this, and you can also select the panel to reverse by pressing the SW2 key.

List 4 Makefile.usermain file (extraction)
# source files
SRC += usermain.c

The Construction Method in a Case Where You Divide the User Program into Multiple Files

Furthermore, in a case where the user program has become large and we will divide it into multiple files, we edit they file called ~/tkernel_source/kernel/usermain/Makefile.usermain. In this file, there is a variable called SRC that shows the source code of the user program that we will compile. In the file we downloaded, things are made up in the manner of List 4. In a case where we will add src1.c and src2.c to this, we rewrite and compile the SRC variable definition of the Makefile.usermain file in the manner of List 5.

List 5 Source file added definitions to Makefile.usermain file
# source files
SRC += usermain.c srcl.c src2.c

Customizing T-Kernel

The license for T-Kernel, T-License (refer to "T-License" on page 21 of this magazine for details), allows you to change and utilize the T-Kernel source code as long as you do not redistribute it. When you change the T-Kernel source code and rebuild the kernel with the same method as when we built T-Kernel for the first time, you can utilized a changed kernel. Here, we will explain the method of adding and deleting system calls.

An Analysis of the Method for Calling Up T-Kernel System Calls

Before we explain the method of adding and deleting system calls, let's take a look at the method of calling up system calls in T-Kernel. In T-Kernel, a system call is called up by means of an interrupt, and that method differs depending on the target system. Here, we will give an explanation on calling up system calls on the SH7727.

T-Kernel system calls are provided in function call up form in the manner of tk_cre_tsk(), tk_wai_sem(), etc. As for these functions, the libsvc library provides them, carries out interrupts inside the library, and executes the main body of the system calls. The main body of a system call is a function with "_" attached to the head in the manner of _tk_cre_tsk(), etc.

In the directory ~/tkernel_source/lib/libsvc/src/sysdepend/std_sh7727/, one file each for each system call is arranged. The file name is something in which the extension ".S" is attached to the system call name in the manner of tk_cre_tsk.S.

List 6 tk_cre_tsl.S assembler code (portion extracted)
    .globl  Csym(tk_cre_tsk)
    .type   Csym(tk_cre_tsk), @function

Csym(tk_cre_tsk):
    mov.1   fno, r0
    trapa   #TRAP_SVC
    rts
    nop

    .balign      4
fno:    .long    TFN_CRE_TSK

In concrete terms, when we take a look at tk_cre_tsk.S, it is made up in the manner of List 6. In List 6, we have set a macro defined constant called TFN_CRE_TSK in register r0, and we have turned on an interrupt by means of the trapa instruction. This trapa is an instruction that generates an exception, and by means of this, the call_entry function is called up (List 7).

List 7 Part that calls up the call_entry() function by means of the trapa interrupt
(extracted from ~/tkernel_source/kernel/sysdepend/cpu/sh7727/cpu_init.c)
/* Register exception handler used on OS */

define_inthdr(TRAP_SVC,      call_entry);
define_inthdr(TRAP_RETINT,   _tk_ret_int);
define_inthdr(TRAP_DISPATCH, dispatch_entry);
define_inthdr(TRAP_LOADSR    load_SR);

This call_entry() function is described with an assembler in ~/tkernel_source/kernel/sysdepend/cpu/sh7727/cpu_support.S. With this function, besides carrying out register evacuation and the like for interrupts, we call up functions that use and deal with values specified in register r0 at the time of trapa execution.

The TFN_CRE_TSK constant specified in List 6 is the function code of a T-Kernel system call, and it is defined in ~/tkernel_source/include/sys/svc/tkfncd.h (List 8).

List 8 T-Kernel function code definition
(extracted from ~tkernel_source/include/sys/svc/tkfncd.h)

/*
 *       T-Kernel function code
 */


#define TFN_CRE_TSK        0x80010100
#define TFN_DEL_TSK        0x80020100
#define TFN_STA_TSK        0x80030200
#define TFN_EXT_TSK        0x80040000
#define TFN_EXD_TSK        0x80050000
#define TFN_TER_TSK        0x80060100
#define TFN_DIS_DSP        0x80070000
#define TFN_ENA_DSP        0x80080000
#define TFN_CHG_PRI        0x80090200

This value is defined in the manner of Table 2.

Table 2 System Call Function Code Definitions

Byte Position

Value

Highest level byte
Always 0x80

Second byte
1, 2, 3, . . . are attached to the function definition order

Third byte
Number of function arguments

Lowest byte
Always 0x00

Also, together with the macro definition in List 8, a macro in the manner of List 9 is defined in ~/tkernel_source/include/sys/svc/tksvctbl.h.

List 9 System call entry definitions
(extracted from ~/tkernel_source/include/sys/svc/tksvctbl.h)
#define _SVC_ENTRY(name)   .int   Csym(__##name)

#define N_TFN    109

         _SVC_ENTRY(tk_cre_tsk)
         _SVC_ENTRY(tk_del_tsk)
         _SVC_ENTRY(tk_sta_tsk)
         _SVC_ENTRY(tk_ext_tsk)
         _SVC_ENTRY(tk_exd_tsk)
         _SVC_ENTRY(tk_ter_tsk)
         _SVC_ENTRY(tk_dis_dsp)
         _SVC_ENTRY(tk_ena_dsp)
         _SVC_ENTRY(tk_chg_pri)

N_TFN in List 9 is the maximum number of system calls. Because _SVC_ENTRY is a macro to which we have attached an underscore "_" to the head, when the macro is expanded, the function names of the main bodies of the system calls, such as _tk_cre_tsk(), _tk_del_tsk(), and _tk_sta_tsk(), get lined up.

This line up order matches the order of the values of the second byte inside each of the constant macros that are defined in List 8.

The call_entry() function carries out the process in which we call the function of a location that corresponds to the second byte of the numerical value at the time we do trapa. By means of this, the calling up of the system call function described in C language is finally carried out.

The Method of Adding and Deleting T-Kernel System Calls

Let's try deleting from T-Kernel the tk_ref_por system call that references a rendezvous. From the analysis of the method for calling up T-Kernel system calls that we previously saw, we understand that the following processes are necessary in order to delete system call tk_aaa_bbb from T-Kernel.

(1)
Delete macro constant TFN_AAA_BBB from ~/tkernel_source/include/sys/svc/tkfncd.h, and afterward revise the numerical values of the second byte of the defined macro constants so that they fall into order.

(2)
Delete _SVC_ENTRY (tk_aaa_bbb) from ~/tkernel_source/include/sys/svc/tksvctbl.h, and decrease the value of the N_TFN macro constant by one.

(3)
Delete the tk_aaa_bbb.S file for the purpose of issuing the trapa instruction from the libsvc subordinates.

(4)
Delete the tk_aaa_bbb() prototype declaration from the ~/tkernel_source/include/tk/syscall.h.

(5)
Actually delete the tk_aaa_bbb() function from among the kernel source code.

Among these processes, the first three are not very dependent on the function contents to be added or deleted. For that reason, the script ~/tkernel_source/etc/mktksvc that automatically carries out the first three is attached to the T-Kernel source code. This script automatically generates an assembler file that issues the macro constant and trapa instruction from the system call list inside ~/tkernel_source/include/tk/syscall.h (List 10).

List 10 System call list definition
(extracted from ~/tkernel_source/include/tk/syscall.h)
/* ----------------------------------------------------------------------
*/
/* 
 * Definition for interface library automatic generation (mktksvc)
 */

/*** DEFINE_TKSVC ***/

/* [BEGIN SYSCALLS */
IMPORT ID tk_cre_tsk( T_CTSK *pk_ctsk );
IMPORT ER tk_del_tsk( ID tskid );
IMPORT ER tk_sta_tsk( ID tskid, INT stacd );
. . . (omitted) . . . 
IMPORT ER tk_rpl_rdv( RNO rdvno, VP msg, INT rmsgsz );
IMPORT ER tk_ref_por( ID porid, T_RPOR *pk_rpor );
IMPORT ER tk_def_int( UINT dintno, T_DINT *pk_int );
. . . (omitted) . . . 
IMPORT ER tk_del_res( ID resid );
IMPORT ER tk_get_res( ID resid, ID ssid, VP *p_resblk );
IMPORT ER tk_set_pow( UINT powmode );
/* [END SYSCALLS] */

The script mktksvc regards everything between /* [BEGIN SYSCALLS] */ and /* [END SYSCALLS] */ as the prototype definitions of system calls.

Using this script, let's try deleting the the system call tk_ref_por. First, we delete the tk_ref_por prototype from the ~/tkernel_source/include/tk/syscall.h (List 11).

List 11 Deleting tk_ref_por from the system call list definition
/* [BEGIN SYSCALLS */
IMPORT ID tk_cre_tsk( T_CTSK *pk_ctsk );
IMPORT ER tk_del_tsk( ID tskid );
IMPORT ER tk_sta_tsk( ID tskid, INT stacd );
. . . (omitted) . . . 
IMPORT ER tk_rpl_rdv( RNO rdvno, VP msg, INT rmsgsz );
IMPORT ER tk_def_int( UINT dintno, T_DINT *pk_int );
. . . (omitted) . . . 
IMPORT ER tk_del_res( ID resid );
IMPORT ER tk_get_res( ID resid, ID ssid, VP *p_resblk );
IMPORT ER tk_set_pow( UINT powmode );
/* [END SYSCALLS] */

In this state, we move to the ~/tkernel_source/lib/libsvc/build/std_sh7727/ libsvc build directory, where we can create a source file with make source. With make clean_source, we can delete the previous source.

As a result of make, several header files and assembler files that issue trapa instructions are created in the same numbers as the system calls. After this, we execute make install. This make install compiles the created assembler files, creates libsvc.a, and copies it into the appropriate place. However, the created header files are not automatically copied into the appropriate place. Because the created header files are placed in ~/tkernel_source/lib/libsvc/src/sysdepend/include/, please either copy all of the contents of this directory into ~/tkernel_source/include/sys/svc/, or paste a symbolic link.

Also, the system call tk_ref_por's main body _tk_ref_por() is defined in ~/tkernel_source/kernel/tkernel/src/rendezvous.c. If you delete this and rebuild the kernel, a T-Kernel without the system call tk_ref_por is created.

In a case where you delete a system call, if there is a system call that utilizes the system call that was deleted, the link will fail. It is necessary to confirm beforehand whether there is not a system call that uses the system call that you intend to delete.

In a case where you add a system call also [3], after editing of ~/tkernel_source/include/tk/syscall.h and rebuilding libsvc in the same manner, we add a system call function definition and rebuild the kernel.

____________________

[1] T-Kernel was updated to version 1.01.00 in July 2004.

[2] Here, we are using the T-Engine/SH7727 Development Kit (provided by Personal Media Corporation).

[3] As for function additions, the method of operation is not to directly add system calls, but rather to add them using extended SVC.

CD-ROM Files from TRONWARE Vol. 89

Makefile.usermain
#
# ----------------------------------------------------------------------
#     T-Kernel
#
#     Copyright (C) 2004 by Ken Sakamura. All rights reserved.
#     T-Kernel is distributed under the T-License.
# ----------------------------------------------------------------------
#
#     Version:   1.01.00
#     Released by T-Engine Forum(http://www.t-engine.org) at 2004/6/28.
#
# ----------------------------------------------------------------------
#
#
#	Makefile for gmake
#	usermain (included from sysmain)
#
# source files
#SRC += usermain.c
SRC += puzzle.c
# source file path
VPATH += ../../../usermain
HEADER += ../../../usermain
# Manager and Driver objects
I_OBJ +=
# additional libraries
LDUSRLIBS += -lstr

puzzle.c
/* puzzle.c
 *   Sample Program for T-Kernel
 *
 * Copyright (C) 2004 by T-Engine Forum and Personal Media Cooperation */

#include <basic.h>
#include <tk/tkernel.h>
#include <tm/tmonitor.h>
#include "screen.h"

/* --------------------------------------------------------------------- */

/* Routine that carries out actual initialization
 * As in the above instructions, initialization is carried out with a separate task
 * With usermain, only create that task */
IMPORT INT usermain( void );                /* Initial task */
IMPORT INT main_task( INT stcd, VP exinf ); /* User initial task main process */
IMPORT void cyclic_hdr( VP exinf);          /* Cyclic handler */
IMPORT INT kbpd_task( INT stcd, VP exinf ); /* Input monitoring task */

/* Supplemental routine during the game */
IMPORT void init_game();
IMPORT W turn_panel( W bl[][], W x, W y );
IMPORT W game_proc( void );
IMPORT void draw_status( void );
IMPORT void refresh_display( void );

IMPORT void draw_panels( void );
IMPORT void draw_panel( W col, W x, W y );
IMPORT void draw_cur( W x, W y );
IMPORT void draw_text( char *str, int x, int y, int c );
IMPORT void draw_large_text( char *str, int x, int y, int d, int e, int c );
IMPORT void draw_vline( W col, W x, W y, W h );
IMPORT void draw_hline( W col, W x, W y, W w );
IMPORT void draw_fill( W x, W y, W w, W h, W col );

IMPORT W rand( void );
IMPORT ER lcdwait( W par );
IMPORT void init_screen( void );
IMPORT W read_h8( W reg, W len );
IMPORT W write_h8( W reg, W len, W dat );
IMPORT void init_kbpd( void );
IMPORT W get_pd_state( W* x, W* y );

/* --------------------------------------------------------------------- */

/* Main task definition */
EXPORT const T_CTSK mtask = {
       NULL,                /* exinf */
       TA_HLNG|TA_RNG0,     /* tskatr */
       (FP)&main_task,      /* task */
       3,                   /* itskpri */
       8*1024,              /* stksz */
       0,                   /* sstksz */
       NULL,                /* stkptr */
       NULL,                /* uatb */
       0,                   /* lsid */
       0                    /* resid */
};

/* Keyboard, touch panel monitoring definitions */
EXPORT const T_CTSK ktask = {
       NULL,                /* exinf */
       TA_HLNG|TA_RNG0,     /* tskatr */
       (FP)&kbpd_task,      /* task */
       4,                   /* itskpri */
       8*1024,              /* stksz */
       0,                   /* sstksz */
       NULL,                /* stkptr */
       NULL,                /* uatb */
       0,                   /* lsid */
       0                    /* resid */
};

/* itskid - initial task
 * mtskid - main task, main process task
 * ktskid - key, touch panel monitoring task */
EXPORT ID itskid, mtskid, ktskid;

/* Cyclic handler */
EXPORT ID cycid;
EXPORT T_CCYC ccyc = {
   NULL,            /* exinf */
   TA_HLNG,         /* cycatr */
   (FP)&cyclic_hdr, /* cychdr */
   100,             /* cyctim */
   0                /* cycphs */
};

/* Message buffer */
EXPORT ID mbfid;
EXPORT T_CMBF cmbf = {
   NULL,     /* exinf */
   TA_TPRI,  /* mkfatr */
   32,       /* bufsz */
   4         /* maxmsz */
};

/* --------------------------------------------------------------------- */
/* Data necessary in game */

/* Game region */
#define FIELD_WIDTH  240
#define FIELD_HEIGHT 240
/* Panel numbers */
#define PANEL_COLUMN   6
#define PANEL_ROW      6
/* Initial level */
#define FIRST_LEVEL    3
/* Number of blocks that cannot be reversed */
#define HARD_PANEL     7

W panel[PANEL_ROW][PANEL_COLUMN];
W panel_b[PANEL_ROW][PANEL_COLUMN];
W tim, step;
W lev = FIRST_LEVEL;
W curx = PANEL_COLUMN/2, cury = PANEL_ROW/2;

/* Macro for fixing VRAM location */
#define VRAMADDR(x,y) (VRAM_ADDR_BAK+((y)*DISP_WIDTH+(x))*2)

/* User initial task */
EXPORT INT usermain( void )
{
   /* Obtain self task ID for future keeping */
   itskid = tk_get_tid();

   /* Create main task and start it up */
   mtskid = tk_cre_tsk((T_CTSK*)&mtask);
   tk_sta_tsk(mtskid, 0);

   /* Put self to sleep after starting up the main task
    * Wait for being awakened when the main task terminates */
   tk_slp_tsk(TMO_FEVR);

   /* Termination */
   return -1;
}

/* Main task */
EXPORT INT main_task( INT stcd, VP exinf )
{
   /* --------------------------------------------------------------------- */
   /* Various initialization processes */
  
   /* Initialize keyboard, LCD */
   init_kbpd();
   init_screen();

   /* Create input monitoring task */
   ktskid = tk_cre_tsk((T_CTSK*)&ktask);
   tk_sta_tsk(ktskid, 0);
   
   /* Create timer handler */
   cycid = tk_cre_cyc(&ccyc);
   
   /* Create message buffer */
   mbfid = tk_cre_mbf(&cmbf);

   /* --------------------------------------------------------------------- */
   /* Main game loop */
   while(1)
   {
      /* Initialize */
      init_game();
      /* Main game process */
      if(game_proc()==0) break;
      /* In a case where it's been cleared, raise the level and restart */
      lev++;
   }
  
   /* --------------------------------------------------------------------- */
   /* Termination */
   
   /* Delete resources */
   tk_del_cyc(cycid);
   tk_del_mbf(mbfid);
   tk_ter_tsk(ktskid);
   tk_del_tsk(ktskid);

   /* Wake up the initial task and terminate self */
   tk_rel_wai(itskid);
   tk_exd_tsk();

   /* Does not reach here */
   return 0;
}
/* Game main body */
EXPORT W game_proc( void )
{
   W i, x, y;
   W mbuf;
   /* Start up the cyclic handler that counts the time */
   tk_sta_cyc(cycid);

   while(1)
   {
      /* Wait for a message */
      /* Here, a four-byte message will be sent in from the input monitoring task */
      tk_rcv_mbf(mbfid, &mbuf, TMO_FEVR);
    
      if (mbuf&0x80000000)
      {
         /* That on which the highest bit is standing is a key operation */

         /* Up, down, left, right */
         if (mbuf&1) curx = (curx+1) % PANEL_COLUMN;
         if (mbuf&2) curx = (curx+(PANEL_COLUMN-1)) % PANEL_COLUMN;
         if (mbuf&4) cury = (cury+(PANEL_ROW-1)) % PANEL_ROW;
         if (mbuf&8) cury = (cury+1) % PANEL_ROW;

         /* retry */
         if (mbuf&16)
         {
            /* If it hasn't been overturned even once, reconfigure the face
             * When that's not the case, return to the initial state */
            if (step==0)
            {
               lev--;
               return 1;
            }
            else
            {
               for (y=0; y<PANEL_ROW; y++)
                  for (x=0;x<PANEL_COLUMN;x++)
                     panel[x][y]=panel_b[x][y];
               step = tim = 0;
            }
         }

         /* turn */
         if (mbuf&0x0400) step += turn_panel(panel,curx,cury);

         /* exit */
         if (mbuf&0x0100) return 0;
      }
      else
      {
         /* Obtain the panel of the coordinates that were touched */
         x = (mbuf>>16) & 0xffff;
         y = mbuf & 0xffff;
         step += turn_panel(
             panel,
             x/(FIELD_WIDTH/PANEL_COLUMN),
             y/(FIELD_HEIGHT/PANEL_ROW));
      }

      /* Has it cleared? */
      i=0;
      for (y=0; y<PANEL_ROW; y++)
         for (x=0; x<PANEL_COLUMN; x++)
            if (panel[x][y]<=1) i += panel[x][y];

      /* Clear */
      if (i==0 || i==PANEL_ROW*PANEL_COLUMN-HARD_PANEL)
      {
         /* Stop the time count for the clear message display */
         tk_stp_cyc(cycid);
			
         /* Clear screen display */
         draw_panels();
         draw_status();
			
         draw_fill(40, 40, FIELD_WIDTH-80, FIELD_HEIGHT-80, 0x7FFF);
         draw_large_text("C", 56+20*0, 60+4*0, 2, 2, 0x7C00);
         draw_large_text("L", 56+20*1, 60+4*1, 2, 2, 0x03E0);
         draw_large_text("E", 56+20*2, 60+4*2, 2, 2, 0x001F);
         draw_large_text("A", 56+20*3, 60+4*3, 2, 2, 0x7C00);
         draw_large_text("R", 56+20*4, 60+4*4, 2, 2, 0x03E0);
         draw_large_text("!", 56+20*5, 60+4*5, 2, 2, 0x001F);
         draw_large_text("!", 56+20*6, 60+4*6, 2, 2, 0x7C00);
         draw_text("try next level!", 60, 140, 0);
         draw_text("- push any key -", 56, 165, 0);
      
         refresh_display();
			
         /* If some message comes from the input monitoring task, escape the loop */
         tk_rcv_mbf(mbfid,&mbuf,TMO_FEVR);
         break;
      }
    
      draw_panels();
      draw_cur(curx, cury);
      draw_status();
      refresh_display();
   }

   /* Main loop terminates; on to next level */
   return 1;
}

/* Cyclic handler */
EXPORT void cyclic_hdr( VP exinf )
{
   tim++;
	
   /* Refresh the screen display every second (=10 count) */
   if (tim%10==0)
   {
      draw_status();
      refresh_display();
   }
  
   return;
}

/* Button, touch panel monitoring task */
EXPORT INT kbpd_task( INT stcd, VP exinf )
{
   H prek,k,i;
   W nk;
   W x, y, prebut, but;
	
   prek = k = 0;
   x = y = prebut = but = 0;
  
  
   while(1)
   {
      /* ------------------------------------------------------------------- */
      /* Obtain the key status */
      i = read_h8(KEYSR);

      if (i&2)
      {
         /* Some key or other has been pressed */
         k = read_h8(KBITPR);
         write_h8(KEYSR,i&(~2));
         
         /* Is there a bit that has changed from zero to one? */
         nk = k&(~prek);

         if (nk)
         { /* For key operation, make the highest bit 1 */
            nk |= 0x80000000;
            tk_snd_mbf(mbfid,&nk,4,TMO_FEVR);
            prek = k;
         }
      }
      else
      {
         write_h8(KEYSR,i&(~4));
         prek = 0;
      }
		
      
      /* ------------------------------------------------------------------- */
      /* Touch panel */
      but = get_pd_state(&x,&y);
      if ((prebut==0) && (but==1))
      {
         //x coordinate, upper two bytes; y coordinate, lower two bytes
         nk = ((x&0xFFFF)<<16) | (y&0xFFFF);
      
         //Make the highest bit of the touch panel zero
         nk &= 0x7FFFFFFF;
         tk_snd_mbf(mbfid,&nk,4,TMO_FEVR);
      }
      prebut = but;
    
      tk_dly_tsk(20);
   }
  
   tk_ext_tsk();
   return 0;
}

/* Initialization of game information */
EXPORT void init_game()
{
   W i,j;

   /* Variable initialization */
   for (i=0; i<PANEL_ROW; i++)
      for (j=0; j<PANEL_COLUMN; j++)
         panel[i][j] = 0;
   tim = 0;
   step = 0;

   /* Assigning of blocks that cannot be reversed */
   for (i=0; i<HARD_PANEL; i++)
   {
      j=rand()%(PANEL_ROW*PANEL_COLUMN);
      if (panel[j%PANEL_COLUMN][j/PANEL_COLUMN] != 2)
         panel[j%PANEL_COLUMN][j/PANEL_COLUMN]=2;
      else
         i--;
   }
  
   /* Randomly reverse lev spots */
   for (i=0; i<lev; i++)
   {
      j=rand()%(PANEL_ROW*PANEL_COLUMN);
      if (panel[j%PANEL_COLUMN][j/PANEL_COLUMN]!=2)
         turn_panel(panel,j%PANEL_COLUMN,j/PANEL_COLUMN);
      else
         i--;
   }

   /* Save for future keeping */
   for (i=0; i<PANEL_ROW; i++)
      for (j=0; j<PANEL_COLUMN; j++)
         panel_b[i][j] = panel[i][j];

   /* Draw and refresh the screen */
   draw_panels();
   draw_status();
   refresh_display();
   
   return;
}

/* Reversal of blocks */
EXPORT W turn_panel( W bl[PANEL_COLUMN][PANEL_ROW], W x, W y )
{
   /* Check the regions once */
   if (x<0 || x>=PANEL_COLUMN || y<0 || y>=PANEL_ROW) return 0;
  
   /* Reverse the thing itself */
  
   if (bl[x][y] <= 1)
      bl[x][y] = 1-bl[x][y];
   else
      return 0;
   /* Left */
   if (x >= 1)
      if (bl[x-1][y] <= 1)
         bl[x-1][y] = 1-bl[x-1][y];
   /* Right */
   if (x < PANEL_COLUMN-1)
      if (bl[x+1][y] <= 1)
         bl[x+1][y] = 1-bl[x+1][y];
   /* Top */
   if (y >= 1)
      if (bl[x][y-1] <= 1)
         bl[x][y-1] = 1-bl[x][y-1];
   /* Bottom */
   if (y<PANEL_ROW-1)
      if (bl[x][y+1] <= 1)
         bl[x][y+1] = 1-bl[x][y+1];
  
   return 1;
}

EXPORT void draw_panels( void )
{
   W i,j;

   /* Draw the present panels and the cursor */
  
   for (i=0; i<PANEL_ROW; i++)
      for (j=0; j<PANEL_COLUMN; j++)
         draw_panel(panel[i][j], i, j);
   draw_cur(curx, cury);
}

EXPORT void draw_panel( W col, W x, W y)
{
   W w,h;
   W tx,ty;
   /* Respective{middle, top left, bottom}colors */
   LOCAL H colors[3][3]={
      {0x7C00,0x7E10,0x4000},
      {0x001F,0x0010,0x4210},
      {0x2108,0x1084,0x0421}
   };
  
  
   /* -------------- Panel drawing routine ---------------- */

   /* Drawing size */
   w = FIELD_WIDTH/PANEL_COLUMN;
   h = FIELD_HEIGHT/PANEL_ROW;
   x *= w;
   y *= h;

   /* Upper and left bright parts */
   draw_fill(x,y,w,4,colors[col][1]);
   draw_fill(x,y,4,h,colors[col][1]);

   /* Lower and right dark spots */
   for (ty=y+h-4; ty<y+h; ty++)
      draw_hline(x+(y+h-ty), ty, w-(y+h-ty), colors[col][2]);
   for (tx=x+w-4; tx<x+w; tx++)
      draw_vline(tx,y+(x+w-tx), h-(x+w-tx), colors[col][2]);

   //Things outside of those
   draw_fill(x+4, y+4, w-8, h-8, colors[col][0]);
}

EXPORT void draw_cur( W x, W y )
{
   W w, h, wb, hb;
   W col = 0x03FF;

   /* Draw the cursor */
  
   /* Drawing size */
   w = FIELD_WIDTH/PANEL_COLUMN;
   h = FIELD_HEIGHT/PANEL_ROW;
   x *= w;
   y *= h;

   wb = w/8;
   hb = h/8;

   /* Upper left */
   draw_fill(x, y, wb*3, hb, col);
   draw_fill(x, y+hb, wb, hb*2, col);

   /* Upper right */
   draw_fill(x+w-wb*3-1, y, wb*3, hb, col);
   draw_fill(x+w-wb-1, y+hb, wb, hb*2, col);
  
   /* Lower left */
   draw_fill(x, y+h-hb-1, wb*3, hb, col);
   draw_fill(x, y+h-hb*3-1, wb, hb*2, col);

   /* Lower right */
   draw_fill(x+w-wb*3-1, y+h-hb-1, wb*3, hb, col);
   draw_fill(x+w-wb-1, y+h-hb*3-1, wb, hb*2, col);
}

/* Convert num into character string str of n places
 * A simple version of the so-called sprintf() */
static inline void num2str( char *str, int num, int n )
{
   char *pnt = str;
   int i;
   int length = n;

   if (n <= 0)
   {
      int tnum = num;
      length = 1;
      while ((tnum /= 10)) length++;
   }
   pnt = str+length;
   if (num < 0)
   {
      *str = '-';
      pnt++;
      num = -num;
   }
   *pnt = '\0';
   for (i=0; i<length; i++)
   {
      *(--pnt) = '0' + num%10;
      num /= 10;
   }
}

EXPORT void draw_status( void )
{
   char str[256];
   W x ,y, i, j, cnt[3];
   x = 0;
   y = FIELD_HEIGHT;
   /* Initialize background with white */
   draw_fill(x, y, DISP_WIDTH, DISP_HEIGHT-y, 0x7FFF);

   /* ---------- Drawing of the present state ------------- */

   /* Counting of the number of red and blue */
   cnt[0] = cnt[1] = 0;
   for (i=0; i<PANEL_COLUMN; i++)
      for (j=0;j<PANEL_ROW;j++)
         cnt[panel[i][j]]++;
   draw_text("PANEL   :", x, y, 0);

   num2str(str, cnt[0], 2);
   draw_text(str, x+8*6, y, 0x7C00);

   num2str(str, cnt[1], 2);
   draw_text(str, x+8*10, y, 0x001F);
	  
   x=8*15;
   draw_text("TIME - ", x, y, 0);
   num2str(str, tim/10, 0);
   draw_text(str, x+8*7, y, 0);
   x=0;
   y+=20;

   draw_text("STEP - ", x, y, 0);
   num2str(str, step, 0);
   draw_text(str, x+8*7, y, 0);
   x=8*15;

   draw_text("LEVEL - ", x, y, 0);
   num2str(str, lev, 0);
   draw_text(str, x+8*8, y, 0);

   x=0;
   y+=40;

   if (step) draw_text("SW2:TURN   SW1:RETRY   SW3:END",x,y,0);
   else      draw_text("SW2:TURN   SW1:REGEN   SW3:END",x,y,0);
  
   return;
}


/* --------------------------------------------------------------------- */

/* Drawing vertical and horizontal lines */
EXPORT void draw_vline( W x, W y, W h, W col )
{
   W ty;
   for (ty=y; ty<y+h; ty++)
      out_h(VRAMADDR(x,ty),col);
}

EXPORT void draw_hline( W x, W y, W w, W col)
{
   W tx;
   for (tx=x; tx<x+w; tx++)
      out_h(VRAMADDR(tx,y),col);
}

/* Drawing of rectangles */
EXPORT void draw_fill( W x, W y, W w, W h, W col)
{
   W tx,ty;
   for (ty=y; ty<y+h; ty++)
      for (tx=x; tx<x+w; tx++)
         out_h(VRAMADDR(tx,ty),col);
}

EXPORT void refresh_display( void )
{
   W src, dst;
   W s;

   dst = VRAM_ADDR;
   src = VRAM_ADDR_BAK;
   /* Copy memory to the LCD VRAM */
   for (s=DISP_HEIGHT*DISP_WIDTH*2/4; s>0; s--)
   {
      out_w(dst,in_w(src));
      dst+=4;
      src+=4;
   }
   //Copy once again, since there are cases in which it doesn't go well
   for (s=32*DISP_WIDTH*2/4; s>0; s--)
   {
      out_w(dst,in_w(src));
      dst+=4;
      src+=4;
   }
   return;
}

/* --------------------------------------------------------------------- */
/* Character string drawing related (from stpwtc sample) */

/*
 * Bit map image of a character(8x16 pixels)
 */
/*
For example, "&"
0x00    00000000    
0x38    00111000      ***
0x44    01000100     *   *
0x44    01000100     *   *
0x44    01000100     *   *
0x44    01000100     *   *
0x28    00101000      * *
0x10   00010000      * 
0x2a    00101010      * * *
0x4a    01001010     *  * *
0x44    01000100     *   *
0x84    10000100    *    *
0x84    10000100    *    *
0x8a    10001010    *   * *
0x72    01110010     ***   *
0x00    00000000    
*/
LOCAL UB img[94][16] = {
 {0x00,0x10,0x38,0x38,0x38,0x38,0x38,0x10,0x10,0x10,0x10,0x00,0x10,0x38,0x10,0x00} /* ! */
,{0x00,0x28,0x28,0x50,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00} /* " */
,{0x00,0x00,0x28,0x28,0x2e,0x38,0xe8,0x28,0x2e,0x38,0xe8,0x28,0x28,0x28,0x00,0x00} /* # */
,{0x10,0x10,0x38,0x54,0x92,0x92,0x50,0x30,0x18,0x14,0x92,0x92,0x54,0x38,0x10,0x10} /* $ */
,{0x00,0x42,0xa2,0xa4,0xa4,0xa8,0x48,0x10,0x10,0x24,0x2a,0x4a,0x4a,0x8a,0x84,0x00} /* % */
,{0x00,0x38,0x44,0x44,0x44,0x44,0x28,0x10,0x2a,0x4a,0x44,0x84,0x84,0x8a,0x72,0x00} /* & */
,{0x00,0x20,0x20,0x40,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00} /* ' */
,{0x02,0x04,0x08,0x08,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x08,0x08,0x04,0x02} /* ( */
,{0x80,0x40,0x20,0x20,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x20,0x20,0x40,0x80} /* ) */
,{0x00,0x00,0x00,0x10,0x10,0x92,0x54,0x38,0x54,0x92,0x10,0x10,0x00,0x00,0x00,0x00} /* * */
,{0x00,0x00,0x00,0x10,0x10,0x10,0x10,0x7c,0x10,0x10,0x10,0x10,0x00,0x00,0x00,0x00} /* + */
,{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x60,0x60,0x20,0x20,0x40} /* , */
,{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x7c,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00} /* - */
,{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x60,0x60,0x00,0x00} /* . */
,{0x00,0x02,0x02,0x04,0x04,0x08,0x08,0x10,0x10,0x20,0x20,0x40,0x40,0x80,0x80,0x00} /* / */
,{0x00,0x38,0x44,0x44,0x82,0x82,0x82,0x82,0x82,0x82,0x82,0x82,0x44,0x44,0x38,0x00} /* 0 */
,{0x00,0x10,0x30,0x50,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x7c,0x00} /* 1 */
,{0x00,0x38,0x44,0x82,0x82,0x02,0x04,0x04,0x08,0x10,0x10,0x20,0x42,0x82,0xfe,0x00} /* 2 */
,{0x00,0x38,0x44,0x82,0x02,0x02,0x04,0x38,0x04,0x02,0x02,0x02,0x82,0x44,0x38,0x00} /* 3 */
,{0x00,0x0c,0x0c,0x14,0x14,0x24,0x24,0x44,0x44,0x84,0x84,0xfe,0x04,0x04,0x04,0x00} /* 4 */
,{0x00,0xfc,0x80,0x80,0x80,0x80,0xb8,0xc4,0x02,0x02,0x02,0x02,0x02,0x84,0x78,0x00} /* 5 */
,{0x00,0x3c,0x42,0x80,0x80,0x80,0xb8,0xc4,0x82,0x82,0x82,0x82,0x82,0x44,0x38,0x00} /* 6 */
,{0x00,0xfe,0x82,0x82,0x84,0x04,0x04,0x08,0x08,0x08,0x08,0x10,0x10,0x10,0x10,0x00} /* 7 */
,{0x00,0x38,0x44,0x82,0x82,0x82,0x44,0x38,0x44,0x82,0x82,0x82,0x82,0x44,0x38,0x00} /* 8 */
,{0x00,0x38,0x44,0x82,0x82,0x82,0x82,0x82,0x46,0x3a,0x02,0x02,0x02,0x84,0x78,0x00} /* 9 */
,{0x00,0x00,0x00,0x00,0x00,0x30,0x30,0x00,0x00,0x00,0x00,0x30,0x30,0x00,0x00,0x00} /* : */
,{0x00,0x00,0x00,0x00,0x00,0x30,0x30,0x00,0x00,0x00,0x00,0x30,0x30,0x10,0x10,0x20} /* ; */
,{0x00,0x02,0x04,0x08,0x10,0x20,0x40,0x80,0x80,0x40,0x20,0x10,0x08,0x04,0x02,0x00} /* < */
,{0x00,0x00,0x00,0x00,0x00,0x00,0xfe,0x00,0x00,0xfe,0x00,0x00,0x00,0x00,0x00,0x00} /* = */
,{0x00,0x80,0x40,0x20,0x10,0x08,0x04,0x02,0x02,0x04,0x08,0x10,0x20,0x40,0x80,0x00} /* > */
,{0x00,0x38,0x44,0x82,0x82,0x02,0x04,0x08,0x10,0x10,0x10,0x00,0x00,0x10,0x10,0x00} /* ? */
,{0x00,0x38,0x44,0x82,0x9a,0xaa,0xaa,0xaa,0xaa,0xaa,0xac,0x90,0x80,0x42,0x3c,0x00} /* @ */
,{0x00,0x10,0x10,0x28,0x28,0x28,0x44,0x44,0x44,0x7c,0x82,0x82,0x82,0x82,0x82,0x00} /* A */
,{0x00,0xf8,0x84,0x82,0x82,0x82,0x84,0xf8,0x84,0x82,0x82,0x82,0x82,0x84,0xf8,0x00} /* B */
,{0x00,0x38,0x44,0x82,0x82,0x80,0x80,0x80,0x80,0x80,0x80,0x82,0x82,0x44,0x38,0x00} /* C */
,{0x00,0xf8,0x84,0x82,0x82,0x82,0x82,0x82,0x82,0x82,0x82,0x82,0x82,0x84,0xf8,0x00} /* D */
,{0x00,0xfe,0x80,0x80,0x80,0x80,0x80,0xfc,0x80,0x80,0x80,0x80,0x80,0x80,0xfe,0x00} /* E */
,{0x00,0xfe,0x80,0x80,0x80,0x80,0x80,0xfc,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x00} /* F */
,{0x00,0x38,0x44,0x82,0x82,0x80,0x80,0x80,0x8e,0x82,0x82,0x82,0x82,0x44,0x38,0x00} /* G */
,{0x00,0x82,0x82,0x82,0x82,0x82,0x82,0xfe,0x82,0x82,0x82,0x82,0x82,0x82,0x82,0x00} /* H */
,{0x00,0x7c,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x7c,0x00} /* I */
,{0x00,0x02,0x02,0x02,0x02,0x02,0x02,0x02,0x02,0x02,0x02,0x02,0x82,0x84,0x78,0x00} /* J */
,{0x00,0x82,0x82,0x84,0x88,0x90,0xa0,0xd0,0x90,0x88,0x88,0x84,0x84,0x82,0x82,0x00} /* K */
,{0x00,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0xfe,0x00} /* L */
,{0x00,0x82,0x82,0xc6,0xc6,0xaa,0xaa,0xaa,0x92,0x92,0x92,0x82,0x82,0x82,0x82,0x00} /* M */
,{0x00,0x82,0x82,0xc2,0xc2,0xa2,0xa2,0x92,0x92,0x8a,0x8a,0x86,0x86,0x82,0x82,0x00} /* N */
,{0x00,0x38,0x44,0x82,0x82,0x82,0x82,0x82,0x82,0x82,0x82,0x82,0x82,0x44,0x38,0x00} /* O */
,{0x00,0xf8,0x84,0x82,0x82,0x82,0x82,0x84,0xf8,0x80,0x80,0x80,0x80,0x80,0x80,0x00} /* P */
,{0x00,0x38,0x44,0x82,0x82,0x82,0x82,0x82,0x82,0x82,0x82,0xba,0xc6,0x44,0x3a,0x00} /* Q */
,{0x00,0xf8,0x84,0x82,0x82,0x82,0x84,0xf8,0x88,0x88,0x84,0x84,0x84,0x82,0x82,0x00} /* R */
,{0x00,0x38,0x44,0x82,0x82,0x80,0x40,0x30,0x0c,0x02,0x02,0x82,0x82,0x44,0x38,0x00} /* S */
,{0x00,0xfe,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x00} /* T */
,{0x00,0x82,0x82,0x82,0x82,0x82,0x82,0x82,0x82,0x82,0x82,0x82,0x82,0x44,0x38,0x00} /* U */
,{0x00,0x82,0x82,0x82,0x82,0x82,0x44,0x44,0x44,0x44,0x28,0x28,0x28,0x10,0x10,0x00} /* V */
,{0x00,0x82,0x82,0x92,0x92,0x92,0x92,0xaa,0xaa,0xaa,0xaa,0x44,0x44,0x44,0x44,0x00} /* W */
,{0x00,0x82,0x82,0x44,0x44,0x28,0x28,0x10,0x10,0x28,0x28,0x44,0x44,0x82,0x82,0x00} /* X */
,{0x00,0x82,0x82,0x82,0x44,0x44,0x44,0x28,0x28,0x10,0x10,0x10,0x10,0x10,0x10,0x00} /* Y */
,{0x00,0xfe,0x82,0x84,0x04,0x08,0x08,0x10,0x10,0x20,0x20,0x40,0x42,0x82,0xfe,0x00} /* Z */
,{0x00,0x1e,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x1e,0x00} /* [ */
,{0x00,0x82,0x82,0x44,0x44,0x28,0x28,0xfe,0x10,0x10,0xfe,0x10,0x10,0x10,0x10,0x00} /* \ */
,{0x00,0x78,0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x78,0x00} /* ] */
,{0x18,0x24,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00} /* ^ */
,{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xfe} /* _ */
,{0x30,0x08,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00} /* ` */
,{0x00,0x00,0x00,0x00,0x00,0x00,0x78,0x84,0x04,0x1c,0x64,0x84,0x84,0x8c,0x72,0x00} /* a */
,{0x00,0x80,0x80,0x80,0x80,0x80,0xb8,0xc4,0x82,0x82,0x82,0x82,0x82,0xc4,0xb8,0x00} /* b */
,{0x00,0x00,0x00,0x00,0x00,0x00,0x3c,0x42,0x82,0x80,0x80,0x80,0x80,0x42,0x3c,0x00} /* c */
,{0x00,0x02,0x02,0x02,0x02,0x02,0x3a,0x46,0x82,0x82,0x82,0x82,0x82,0x46,0x3a,0x00} /* d */
,{0x00,0x00,0x00,0x00,0x00,0x00,0x38,0x44,0x82,0xfe,0x80,0x80,0x80,0x42,0x3c,0x00} /* e */
,{0x00,0x1c,0x22,0x20,0x20,0x20,0xfc,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x00} /* f */
,{0x00,0x00,0x00,0x00,0x00,0x00,0x7a,0x84,0x84,0x78,0x40,0x80,0x7c,0x82,0x82,0x7c} /* g */
,{0x00,0x80,0x80,0x80,0x80,0x80,0xb8,0xc4,0x82,0x82,0x82,0x82,0x82,0x82,0x82,0x00} /* h */
,{0x00,0x10,0x10,0x00,0x00,0x00,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x00} /* i */
,{0x00,0x04,0x04,0x00,0x00,0x00,0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x84,0x78} /* j */
,{0x00,0x80,0x80,0x80,0x80,0x80,0x84,0x88,0x90,0xa0,0xe0,0x90,0x88,0x84,0x82,0x00} /* k */
,{0x00,0x30,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x38,0x00} /* l */
,{0x00,0x00,0x00,0x00,0x00,0x00,0x6c,0x92,0x92,0x92,0x92,0x92,0x92,0x92,0x92,0x00} /* m */
,{0x00,0x00,0x00,0x00,0x00,0x00,0xb8,0xc4,0x82,0x82,0x82,0x82,0x82,0x82,0x82,0x00} /* n */
,{0x00,0x00,0x00,0x00,0x00,0x00,0x38,0x44,0x82,0x82,0x82,0x82,0x82,0x44,0x38,0x00} /* o */
,{0x00,0x00,0x00,0x00,0x00,0x00,0xb8,0xc4,0x82,0x82,0x82,0x82,0xc4,0xb8,0x80,0x80} /* p */
,{0x00,0x00,0x00,0x00,0x00,0x00,0x3a,0x46,0x82,0x82,0x82,0x82,0x46,0x3a,0x02,0x02} /* q */
,{0x00,0x00,0x00,0x00,0x00,0x00,0x98,0xa4,0xc0,0x80,0x80,0x80,0x80,0x80,0x80,0x00} /* r */
,{0x00,0x00,0x00,0x00,0x00,0x00,0x7c,0x82,0x82,0x60,0x1c,0x02,0x82,0x82,0x7c,0x00} /* s */
,{0x00,0x00,0x20,0x20,0x20,0x20,0xfc,0x20,0x20,0x20,0x20,0x20,0x20,0x22,0x1c,0x00} /* t */
,{0x00,0x00,0x00,0x00,0x00,0x00,0x82,0x82,0x82,0x82,0x82,0x82,0x82,0x46,0x3a,0x00} /* u */
,{0x00,0x00,0x00,0x00,0x00,0x00,0x82,0x82,0x82,0x44,0x44,0x28,0x28,0x10,0x10,0x00} /* v */
,{0x00,0x00,0x00,0x00,0x00,0x00,0x82,0x92,0x92,0x92,0xaa,0xaa,0xaa,0x44,0x44,0x00} /* w */
,{0x00,0x00,0x00,0x00,0x00,0x00,0x82,0x82,0x44,0x28,0x10,0x28,0x44,0x82,0x82,0x00} /* x */
,{0x00,0x00,0x00,0x00,0x00,0x00,0x82,0x82,0x44,0x44,0x28,0x28,0x10,0x10,0x20,0xc0} /* y */
,{0x00,0x00,0x00,0x00,0x00,0x00,0xfe,0x02,0x04,0x08,0x10,0x20,0x40,0x80,0xfe,0x00} /* z */
,{0x00,0x04,0x08,0x08,0x08,0x08,0x08,0x10,0x08,0x08,0x08,0x08,0x08,0x08,0x04,0x00} /* { */
,{0x00,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x00} /* | */
,{0x00,0x40,0x20,0x20,0x20,0x20,0x20,0x10,0x20,0x20,0x20,0x20,0x20,0x20,0x40,0x00} /* } */
,{0xfe,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00} /* ~ */
};

/*
 * Drawing of the character string
 * Draw character string str from left corner (x,y) with color c
 */
EXPORT void draw_text( char *str, int x, int y, int c )
{
   int k, h, b, m;
   for (; *str != '\0'; str++) {
      k = *str - '!';
      if (k < 0 || k >= 94) {
         x += 8; continue;
      }
      for (h = 0; h < 16; h++) {
         b = img[k][h];
         for (m = 7; m >= 0; m--) {
            if (b & 1) out_h(VRAMADDR(x + m, y + h), c);
            b >>= 1;
         }
      }
      x += 8;
   }
}

/*
 * Drawing of an enlargement of the character string
 * Draw character s from left corner (x,y) with color c, d times width, e times height
 */
EXPORT void draw_large_text( char *str, int x, int y, int d, int e, int c )
{
   int k, h, b, m,lx,ly;
   for (; *str != '\0'; str++) {
      k = *str - '!';
      if (k < 0 || k >= 94){
         x+=8*d;
         continue;
      }
      for (h = 0; h < 16; h++) {
         b = img[k][h];
         for (m = 7; m >= 0; m--) {
            if (b & 1) {
               for (ly=y+h*e ; ly<y+(h+1)*e; ly++) {
                  for (lx=x+m*d ; lx<x+(m+1)*d; lx++) {
                     out_h(VRAMADDR(lx,ly), c);
                  }
               }
            }
            b >>= 1;
         }
      }
      x+=8*d;
   }
}

/* --------------------------------------------------------------------- */
/* Random processing */
EXPORT W rand( void )
{
   static W first=1,x=0;
   W h,m,s;
  
  
   /* First time initialize */
   if (first==1)
   {
      first=0;
      /* Fittingly make a random number seed value using the SH7727 RT clock function */
      h=read_h8(HRCNT ,1);
      m=read_h8(MINCNT,1);
      s=read_h8(SECCNT,1);
    
      x=(h+3)*(m+5)*(s+7);
   }

   /* Linear combination method(x=(x*A+C)%D) */
   x=x*214013+253101;
   return (W)((x>>16)&32767);
}

/* --------------------------------------------------------------------- */
/* Screen related (from SH7727 screen driver) */

LOCAL ER lcdwait( W par )
{
   W i;
   /* Wait until it comes about that the LCD can be used */
   for (i=0; i<5000; i++)
   {
      if ((ReadLCDC(LDPMMR) & LDPMMR_LPS) == par) return E_OK;
      tk_dly_tsk(10);
   };
  
   return E_BUSY;
}

EXPORT void init_screen( void )
{
   W err;
   out_b(STBCR3, in_b(STBCR3) & ~STBCR3_MSTP11);
   /* Display off */
   WriteLCDC(LDPMMR, LCDparm.ldpmmr);
   WriteLCDC(LDCNTR, 0);
   err = lcdwait(0);

   WriteLCDC(LDINTR,  0);

   WriteLCDC(LDICKR,  LCDparm.ldickr);
   WriteLCDC(LDMTR,   LCDparm.ldmtr);
   WriteLCDC(LDSMR,   LDSMR_AU_16);
   WriteLCDC(LDDFR,   LDDFR_DSPCOLOR_15C);
   WriteLCDC(LDLAOR,  DISP_WIDTH*2);
  
   WriteLCDC(LDPALCR, 0);
   WriteLCDC(LDHCNR,  LCDparm.ldhcnr);
   WriteLCDC(LDHSYNR, LCDparm.ldhsynr);
   WriteLCDC(LDVDLNR, LCDparm.ldvdlnr);
   WriteLCDC(LDVTLNR, LCDparm.ldvtlnr);
   WriteLCDC(LDVSYNR, LCDparm.ldvsynr);
   WriteLCDC(LDACLNR, LCDparm.ldaclnr);
   WriteLCDC(LDPSPR,  LCDparm.ldpspr);

   WriteLCDCW(LDSARU, VRAM_ADDR);
   WriteLCDCW(LDSARL, 0);
  
   /* Display on */
   WriteLCDC(LDCNTR, LDCNTR_DON);
   lcdwait(LDPMMR_LPS);
   return;
}

/* --------------------------------------------------------------------- */
/* Keys */

/* Reading and writing of H8 controller register */

EXPORT W read_h8( W reg, W len )
{
   W err;
   err = tm_extsvc(0x80,(len==2 ? reg+0x1000 : reg),0,0);
   return err;
}

EXPORT W write_h8( W reg, W len, W dat )
{
   W err;
   err = tm_extsvc(0x81,(len==2 ? reg+0x1000 : reg),dat,0);
   return err;
}

EXPORT void init_kbpd( void )
{
   W i;
  
   /* Make key operations possible */
   i = read_h8(KEYCR);
   i |= 1;
   write_h8(KEYCR,i);

   /* Make it possible to utilize the touch panel */
   write_h8(TPSCR, 1<<3);
   write_h8(TPCR, (TPCR_PEN_ONRE | TPxR_PEN_OFF | TPxR_PEN_ON | TPCR_TP_STR));
   return ;
}

/* Initialization of the touch panel (from KB/PD driver source) */

#define	ACC_DELTA (8)	/* Used to prevent pen shaking */
EXPORT W get_pd_state( W *x, W *y )
{
   static W p_x, p_y, p_but=-1, adx, ady;/* Pen status on previous occasion */
   W sts, xpos, ypos;

   sts=read_h8(TPSR);
   xpos=read_h8(XPAR);
   ypos=read_h8(YPAR);
   write_h8(TPSR, 0);
  
   if (sts < 0 || xpos < 0 || ypos < 0) return -1;

   if (sts & TPxR_PEN_OFF) {
      if (p_but == 0) goto fin;
      xpos = p_x;
      ypos = p_y;
   } else if (sts & TPxR_PEN_ON) {
      if (p_but != 0) {
         /* Preventing shaking of the pointer */
         adx += xpos - p_x;
         ady += ypos - p_y;
         if (adx > -ACC_DELTA && adx < ACC_DELTA &&
             ady > -ACC_DELTA && ady < ACC_DELTA) goto fin;
      }
   }
   else{
      return 0;
   }
  
   adx = ady = 0;
   p_but = (sts & TPxR_PEN_ON) ? 1 : 0;
   p_x = xpos;
   p_y = ypos;
  
   /* Normalizing X/Y coordinates */
   xpos = (xpos - TabPar.x_bias) * PDIN_XMAX / TabPar.x_span;
   xpos = (xpos < 0) ? 0 : ((xpos > PDIN_XMAX) ? PDIN_XMAX : xpos);
  
   ypos = (ypos - TabPar.y_bias) * PDIN_YMAX / TabPar.y_span;
   ypos = (ypos < 0) ? 0 : ((ypos > PDIN_YMAX) ? PDIN_YMAX : ypos);
  
   *x = xpos*DISP_WIDTH/PDIN_XMAX;
   *y = ypos*DISP_HEIGHT/PDIN_YMAX;
  
  fin:
   return p_but;
  
}

screen.h
/* screen.h
 *   Header file of Sample Program (puzzle) for T-Kernel
 *
 * Copyright (C) 2004 by T-Engine Forum and Personal Media Cooperation */

/* Display */

#define DISP_WIDTH 240
#define DISP_HEIGHT 320

#define VRAM_SIZE (DISP_WIDTH*DISP_HEIGHT*2)

#define VRAM_ADDR 0x0C800000
#define VRAM_ADDR_BAK 0x0CA00000

/* LCD sample of the development environment below
 * obtained from (${BD}/te/driver/screen/src/sh7727/sh3lcdc.c) */

#define	SH3LCDC_PALETTE	0xa4000800	/* Palette register */
#define	SH3LCDC_BASE	0xa4000c00	/* LCDC */

#define	STBCR3		0xa4000230	/* Standby controller register */
#define	STBCR3_MSTP11	0x02

#define	LDICKR		0x00
#define	LDMTR		0x02
#define	LDDFR		0x04
#define	LDSMR		0x06
#define	LDSARU		0x08	/* 32bit */
#define	LDSARL		0x0c	/* 32bit */
#define	LDLAOR		0x10
#define	LDPALCR		0x12
#define	LDHCNR		0x14
#define	LDHSYNR		0x16
#define	LDVDLNR		0x18
#define	LDVTLNR		0x1a
#define	LDVSYNR		0x1c
#define	LDACLNR		0x1e
#define	LDINTR		0x20
#define	LDPMMR		0x24
#define	LDPSPR		0x26
#define	LDCNTR		0x28

/* LDICKR */
#define	LDICKR_ICKSEL_CKIO	0x0000	/* Clock source: CKIO */
#define	LDICKR_ICKSEL_PCLK	0x1000	/* Clock source: Pclk */
#define	LDICKR_ICKSEL_LCLK	0x2000	/* Clock source: Lclk */
#define	LDICKR_DCDR_1_1		0x0001	/* 1/1 */
#define	LDICKR_DCDR_1_2		0x0002	/* 1/2 */
#define	LDICKR_DCDR_1_4		0x0004	/* 1/4 */
#define	LDICKR_DCDR_1_8		0x0008	/* 1/8 */
#define	LDICKR_DCDR_1_16	0x0010	/* 1/16 */

/* LDMTR */
#define	LDMTR_FLMPOL		0x8000	/* 1: FLM pulse is low active */
#define	LDMTR_CL1POL		0x4000	/* 1: CL1 pulse is low active */
#define	LDMTR_DISPPOL		0x2000	/* 1: DISP is low active */
#define	LDMTR_DPOL              0x1000  /* 1: LCDD is low active */
#define	LDMTR_MCNT              0x0400  /* 1: No MCNT signal */
#define	LDMTR_CL1CNT		0x0200	/* 1: No CL1 signal */
#define	LDMTR_CL2CNT		0x0100	/* 1: No CL2 signal */
#define	LDMTR_MIFTYP_STN4M	0x0000	/* STN monochrome 4bit bus */
#define	LDMTR_MIFTYP_STN8M	0x0001	/* STN monochrome 8bit bus */
#define	LDMTR_MIFTYP_STN4C	0x0008	/* STN color 4bit bus */
#define	LDMTR_MIFTYP_STN8C	0x0009	/* STN color 8bit bus */
#define	LDMTR_MIFTYP_STN12C	0x000a	/* STN color 12bit bus */
#define	LDMTR_MIFTYP_STN16C	0x000b	/* STN color 16bit bus */
#define	LDMTR_MIFTYP_DSTN8M	0x0011	/* DSTN monochrome 8bit bus */
#define	LDMTR_MIFTYP_DSTN16M    0x0013	/* DSTN monochrome 16bit bus */
#define	LDMTR_MIFTYP_DSTN8C	0x0019	/* DSTN color 8bit bus */
#define	LDMTR_MIFTYP_DSTN12C    0x001a	/* DSTN color 12bit bus */
#define	LDMTR_MIFTYP_DSTN16C    0x001b	/* DSTN color 16bit bus */
#define	LDMTR_MIFTYP_TFT16C	0x002b	/* TFT color 16bit bus */

/* LDDFR */
#define	LDDFR_PABD		0x0100	/* Byte data is little endian */
#define	LDDFR_DSPCOLOR_1M	0x0000	/* monochrome 1bpp */
#define	LDDFR_DSPCOLOR_2M	0x0001	/* monochrome 2bpp */ 
#define	LDDFR_DSPCOLOR_4M	0x0002	/* monochrome 4bpp */
#define	LDDFR_DSPCOLOR_6M	0x0004	/* monochrome 6bpp */
#define	LDDFR_DSPCOLOR_4C	0x000a	/* color 4bpp */
#define	LDDFR_DSPCOLOR_8C	0x000c	/* color 8bpp */
#define	LDDFR_DSPCOLOR_15C	0x001d	/* color 15bpp (5-5-5) */
#define	LDDFR_DSPCOLOR_16C	0x002d	/* color 16bpp (5-6-5) */

/* LDSMR */
#define	LDSMR_ROT		0x2000	/* 1: rotate enable */
#define	LDSMR_AU_4		0x0000	/* 4burst */
#define	LDSMR_AU_8		0x0100	/* 8burst */
#define	LDSMR_AU_16		0x0200	/* 16burst */
#define	LDSMR_AU_32		0x0300	/* 32burst */

/* LDPALCR */
#define	LDPALCR_PALS		0x0010	/* 1: CPU uses palette (read only) */
#define	LDPALCR_PALEN		0x0001	/* 1: CPU uses palette */

/* LDINTR */
#define	LDINTR_VINTSEL		0x1000	/* 1: Interrupt at LCD Vsync */
#define	LDINTR_VINTE		0x0100	/* 1: LCDC Vsync interrupt enable */
#define	LDINTR_VINTS		0x0001	/* 1: Vsync interrupt status */

/* LDPMMR */
#define	LDPMMR_VCPE		0x0040	/* 1: VCPWC control enable */
#define	LDPMMR_VEPE		0x0020	/* 1: VEPWC control enable */
#define	LDPMMR_DONE		0x0010	/* 1: DON control enable */
#define	LDPMMR_LPS		0x0003	/* 3: LCD power is ON (read only) */

/* LDCNTR */
#define	LDCNTR_DON		0x0011	/* 1: LCDC enable, display on */

/* Output to palette */
#define	WritePAL(adr, dat)	out_w(SH3LCDC_PALETTE + (adr) * 4, (dat))

/* Input/output to LCDC */
#define	WriteLCDC(adr, dat)	out_h(SH3LCDC_BASE + (adr), (dat))
#define	WriteLCDCW(adr, dat)	out_w(SH3LCDC_BASE + (adr), (dat))
#define	ReadLCDC(adr)		in_h(SH3LCDC_BASE + (adr))

typedef struct {
    UH	ldickr;
    UH	ldmtr;
    UH	ldhcnr;
    UH	ldhsynr;
    UH	ldvdlnr;
    UH	ldvtlnr;
    UH	ldvsynr;
    UH	ldpmmr;
    UH	ldaclnr;
    UH	ldpspr;
} LCDdefs;

LOCAL const LCDdefs LCDparm = {
    0x0108,	// LDICKR
    0xc02b,	// LDMTR	TFT 16bit data Bus
    0x1d23,	// LDHCNR	
    0x0020,	// LDHSYNR
    0x013f,	// LDVDLNR
    0x014b,	// LDVTLNR
    0x1140,	// LDVSYNR
    0xff70,	// LDPMMR
    0x000c,	// LDACLNR
    0x0500,	// LDPSPR
};

/*
 * Keyboard/pointing device controller register definition
 */
/* Touch panel */
#define	TPCR		0x20, 1	/* Touch panel controller register */
#define	TPSR		0x21, 1	/* Touch panel status register */
#define	TPSCR		0x22, 1	/* Sampling controller register */
#define	XPAR		0x24, 2	/* X location A/D register */
#define	YPAR		0x26, 2	/* Y location A/D register */
#define DXDR		0x44, 2	/* DX dot register */
#define	DYDR		0x46, 2	/* DY dot register */

#define	TPCR_PEN_ONRE	0x08
#define	TPxR_PEN_OFF	0x04
#define	TPxR_PEN_ON	0x02
#define	TPCR_TP_STR	0x01

/* Key operations register */
#define KEYCR 0x0060,1
#define KBITPR 0x0064,2
#define KEYSR 0x0062,1

/* Status (common to RTC, touch panel, keyboard, power supply controller) */
#define	RTKISR		0x90, 1

#define	RTKISR_IRRIF	0x10
#define	RTKISR_POWERIF	0x08
#define	RTKISR_KEYIF	0x04
#define	RTKISR_TPIF	0x02
#define	RTKISR_RTCIF	0x01

/* Various types of interrupt settings used */
#define	KB_INTMASK	(KEYxR_PONSW | KEYxR_KEY_OFF | KEYxR_KEY_ON)
#define	PD_INTMASK	(TPxR_PEN_ON | TPxR_PEN_OFF)
#define	RTKISR_INTMASK	(RTKISR_TPIF | RTKISR_KEYIF)

/*
 * Tablet parameters (coordinate normalization parameters, and so on)
 */
typedef	struct {
	W	x_bias;
	W	x_span;
	W	y_bias;
	W	y_span;
	W	nodsp;
	W	rate;
} TABPAR;

LOCAL TABPAR TabPar = {0xec0, -3432, 0xea0, -3360, 0, 80};

#define	PDIN_XMAX	4096
#define	PDIN_YMAX	3072

/* Real-time clock function */

#define SECCNT 2
#define MINCNT 3
#define HRCNT  4


The above article on T-Engine appeared on pages 23-29 in Vol. 89 of TRONWARE . It was translated and loaded onto this Web page with the permission of Personal Media Corporation.

Copyright © 2004 Personal Media Corporation

Copyright © 2004 Sakamura Laboratory, University Museum, University of Tokyo