Register

FONTCHARACTER Reference - compatibility, sets

Discuss issues related to Calculator Hacking/Modding.
Senior Member
User avatar
Posts: 605
Joined: Sat Sep 15, 2012 6:59 am
Location: Krautland ****
Calculators: Casio fx-7400GII, Casio fx-7400GII (SH4), Casio fx-9750GII, Casio fx-9750GII (SH4), Casio fx-9860G, Casio fx-9860G SD, Casio fx-9860G Slim, Casio fx-9860GII SD, Casio fx-9860GII SD Power Graphic 2, Casio Classpad 330 plus, Casio fx-CG20, Casio fx-CG50, Casio Classpad fx-CP400

Re: FONTCHARACTER Reference - compatibility, sets

Postby SimonLothar » Fri Jan 20, 2017 10:43 pm

cakeisalie5 wrote:And if I understand well, the second dword is the flags of the entry. What I don't understand is that in your example, the size of the element is the high nibble of the first byte of the flags, where here, it looks like it is the lowest nibble. Also, the characters looks deformed: for 0x0E, it should be 0xE591 but it is 0xE691, 0x14 should be [0x66,0x31] but it is [0x66,0xD1]...

The string of opcode 0x0E is 0xE691. (one character)
The string of opcode 0x14 is 0x66E5D1. (two characters)

Perhaps the flags are stored in a byte only. I used Dword to minimize the output's line-count.
At present the meaning of the byte's flags is not known to me.
Your observation seems to be right, that the low nibble of the flag-byte represents the count of characters of the opcode string.
I'll be back!

Senior Member
User avatar
Posts: 101
Joined: Sun Mar 27, 2016 10:24 am
Location: France
Calculators: Casio Afx 1.0, Casio fx-9860GII, Casio fx-CG50

Re: FONTCHARACTER Reference - compatibility, sets

Postby cakeisalie5 » Sat Jan 21, 2017 1:52 am

Okay, so I was making a stupid error in my code. This is fixed, I can now read logical things.
Here's my code (works on a PC using GNU/Linux):

Extractor: Show
Code: Select all
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>

/* useless, therefore totally important! :D */
#define be8toh(x) (x)

/* let's do it this way. */
#define pbyte(x)  (((uint8_t*) &os[(x) & 0x0FFFFFFF]))
#define pword(x)  (((uint16_t*)&os[(x) & 0x0FFFFFFF]))
#define pdword(x) (((uint32_t*)&os[(x) & 0x0FFFFFFF]))

#define byte(x)   ( be8toh( *pbyte (x) ))
#define word(x)   (be16toh( *pword (x) ))
#define dword(x)  (be32toh( *pdword(x) ))

/* print the opcode content */
static void print_opcode(const uint8_t *s, int size)
{
   if (!size) printf("<empty>");
   while (size--) {
      if (*s && strchr("\xF9\xF7\x7F\xE5\xE6\xE7", *s)) {
         printf("0x%02X%02X", *s, *(s + 1));
         s++;
      } else
         printf("0x%02X", *s);

      s++;
      if (size) printf(",");
   }
   printf("\n");
}

/* main function */
int main(int ac, char **av)
{
   int ret = 1;
   FILE *f = NULL; char *os = NULL;

   /* open the thing */
   if (ac != 2) return (0);
   f = fopen(av[1], "r");
   if (!f) return (1);

   /* get the OS size */
   fseek(f, 0, SEEK_END);
   long sz = ftell(f);
   fseek(f, 0, SEEK_SET);

   /* get the OS */
   os = malloc(sz);
   if (!os) goto fail;
   fread(os, 1, sz, f);
   fclose(f); f = NULL;

   /* get the table base */
   uint32_t base = 0;
   uint32_t is = dword(dword(0x8001007C) + 4 * 0x7FC);
   printf("Syscall address is 0x%08X\n", is);
   for (; word(is) != 0x000B /* current instruction isn't `rts` */; is += 2) {
      /* check if it's a mov.l opcode */
      if ((word(is) & 0xF000) != 0xD000)
         continue;

      /* get the offset */
      base = dword((is + 4 * (word(is) & 0x00FF) + 4) & 0xFFFFFFFC);

      /* check if it points to OS range */
      if ((base >= 0x80010000) && (base <= 0x80400000))
         break;

      /* reset the base address */
      base = 0;
   }
   if (!base) {
      fprintf(stderr, "We have not found the base!\n");
      goto fail;
   }

   /* we have the base! */
   printf("Base address is 0x%08X\n", base);
   for (int tid = 0; tid < 1 /* 7 */; tid++) {
      for (int minor = 0; minor < 255; minor++) {
         uint32_t daddr = dword(base), flags = dword(base + 4);
         int size = (flags >> 24) & 0x0F;
         base += 8;

         printf("Opcode 0x%02X%02X - %08X - ",
            (int[]){0x00, 0xF9, 0x7F, 0xF7, 0xE5, 0xE6, 0xE7}[tid], minor,
            flags);
         if (daddr >= 0x80010000 && daddr <= 0x80400000)
            print_opcode(pbyte(daddr), size);
         else
            printf("<invalid>\n");
      }
   }

   /* we're good */
   ret = 0;
fail:
   if (f) fclose(f);
   free(os);
   return (ret);
}


EDIT: Using what I've manually decoded, I guessed the flags (first byte's high nibble) were:
- 0x04: appended newline
- 0x08: appended space
Part of the Planète Casio community (FR) - main author of Cahute

Senior Member
User avatar
Posts: 605
Joined: Sat Sep 15, 2012 6:59 am
Location: Krautland ****
Calculators: Casio fx-7400GII, Casio fx-7400GII (SH4), Casio fx-9750GII, Casio fx-9750GII (SH4), Casio fx-9860G, Casio fx-9860G SD, Casio fx-9860G Slim, Casio fx-9860GII SD, Casio fx-9860GII SD Power Graphic 2, Casio Classpad 330 plus, Casio fx-CG20, Casio fx-CG50, Casio Classpad fx-CP400

Re: FONTCHARACTER Reference - compatibility, sets

Postby SimonLothar » Sat Jan 21, 2017 7:56 am

cakeisalie5 wrote:EDIT: Using what I've manually decoded, I guessed the flags (first byte's high nibble) were:
- 0x04: appended newline
- 0x08: appended space
Fine. So that is unravelled, too.
I'll be back!

Senior Member
User avatar
Posts: 101
Joined: Sun Mar 27, 2016 10:24 am
Location: France
Calculators: Casio Afx 1.0, Casio fx-9860GII, Casio fx-CG50

Re: FONTCHARACTER Reference - compatibility, sets

Postby cakeisalie5 » Sat Jan 21, 2017 11:09 am

Yup! I'll be using these to correct and complete the FONTCHARACTER Reference then (just as I thought I had more or less finished *-*). Thanks :D
Part of the Planète Casio community (FR) - main author of Cahute

Senior Member
User avatar
Posts: 101
Joined: Sun Mar 27, 2016 10:24 am
Location: France
Calculators: Casio Afx 1.0, Casio fx-9860GII, Casio fx-CG50

Re: FONTCHARACTER Reference - compatibility, sets

Postby cakeisalie5 » Sat Jan 21, 2017 3:17 pm

Found something odd. In your code, you said:
if the content of the E7-table is not an address in the OS-range, the E7-opcodes do not exist (OS 1.xx).

The fact is, in the fx9860_au/os/1.05.bin, the first entries look valid (but are not: 0xE701 should resolve as [ns]):
Code: Select all
Opcode 0xE700 - 0x801703FF02000000 (0x0) - 0xE640,0x00
Opcode 0xE701 - 0x801703FF02000000 (0x0) - 0xE640,0x00
Opcode 0xE702 - 0x801703FF02000000 (0x0) - 0xE640,0x00
Opcode 0xE703 - 0x801703FF02000000 (0x0) - 0xE640,0x00
Opcode 0xE704 - 0x801703FF02000000 (0x0) - 0xE640,0x00
Opcode 0xE705 - 0x801703FF02000000 (0x0) - 0xE640,0x00
Opcode 0xE706 - 0x0027000000280000 (0x0) - <empty>
Opcode 0xE707 - 0x00290000002A0000 (0x0) - <empty>
Opcode 0xE708 - 0x002B0000002C0000 (0x0) - <empty>
Opcode 0xE709 - 0x002D0000002E0000 (0x0) - <empty>
Part of the Planète Casio community (FR) - main author of Cahute

Senior Member
User avatar
Posts: 605
Joined: Sat Sep 15, 2012 6:59 am
Location: Krautland ****
Calculators: Casio fx-7400GII, Casio fx-7400GII (SH4), Casio fx-9750GII, Casio fx-9750GII (SH4), Casio fx-9860G, Casio fx-9860G SD, Casio fx-9860G Slim, Casio fx-9860GII SD, Casio fx-9860GII SD Power Graphic 2, Casio Classpad 330 plus, Casio fx-CG20, Casio fx-CG50, Casio Classpad fx-CP400

Re: FONTCHARACTER Reference - compatibility, sets

Postby SimonLothar » Sat Jan 21, 2017 5:43 pm

Neither OS 1.05 nor OS 1.05AU has E7-opcodes.

Try

for (int minor = 0; minor <= 255; minor++) {

instead of

for (int minor = 0; minor < 255; minor++) {
I'll be back!

Senior Member
User avatar
Posts: 101
Joined: Sun Mar 27, 2016 10:24 am
Location: France
Calculators: Casio Afx 1.0, Casio fx-9860GII, Casio fx-CG50

Re: FONTCHARACTER Reference - compatibility, sets

Postby cakeisalie5 » Sat Jan 28, 2017 12:32 pm

Done! Just putting it there so I can link to it :p
Code: Select all
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>

/*   The FONTCHARACTER Opcode Table
 *   ------------------------------
 *   The OS has two different tables for the characters: one with the glyphs,
 *   for the simple characters, and one with the opcodes (also called
 *   multi-characters). This program will extract and print the data from
 *   an OS image's opcode table.
 *
 *   This table is not referenced directly in any way, but Simon Lothar found
 *   a trick to find it: browse the 0x7fc syscall (which resolves opcodes as
 *   strings), and checks for a `mov.l` which refers to a valid OS address.
 *   And it works!
 *
 *   Then resolved address refers to a <ID of the major> -> <table of the major>
 *   table. The majors are: 0x00, 0xF9, 0x7F, 0xF7, 0xE5, 0xE6, 0xE7.
 *   0xE7 appeared more or less with OS2; used with an older OS, it will probably
 *   display crap or invalid stuff.
 *
 *   The table of the major is a <character minor> -> <entry>.
 *   Each entry is 8-bytes long. The first 4 bytes represent the absolute adress
 *   (in OS's virtual mapping 0x8XXXXXXX) of the string, and the other 4 bytes
 *   are the other info about the character:
 *
 *   - the high nibble of the first byte are the flags;
 *   - the second nibble of the first byte seem to be the size of the character
 *     (although this looks unused, therefore unreliable);
 *   - the other bytes look like alignment (always zero).
 *
 *   The strings are null-terminated. The flags are:
 *
 *   - 0x4: newline should be appended to the string
 *     (although this looks unused, even if referenced in the 0x7fc syscall;
 *      it's the syscall 0x6a6, "PRGM_IsEndOfLine" that determines if the
 *      character should be followed by a newline or not, and it validates the
 *      0x0C, 0x0D and 0x3A characters only);
 *   - 0x8: space should be appended to the string.
 *
 *   Keep in mind that "unattributed" characters are in fact resolved
 *   as '@' (0x40)! (except the 0x40 character, of course)
 *
 *   Also, I think unknown leading characters in the fixed-width FONTCHARACTER
 *   encoding are resolved as the 0x00 leading character, which means that
 *   0xFFFF is resolved as 0x00FF = 0xFF, which is resolved as 0x00. */
/* ************************************************************************** */
/*  Utilities                                                                 */
/* ************************************************************************** */
/* useless, therefore totally important! :D */
#define be8toh(x) (x)

/* let's do it this way. */
#define pbyte(x)  (((uint8_t*) &os[(x) & 0x0FFFFFFF]))
#define pword(x)  (((uint16_t*)&os[(x) & 0x0FFFFFFF]))
#define pdword(x) (((uint32_t*)&os[(x) & 0x0FFFFFFF]))

#define byte(x)   ( be8toh( *pbyte (x) ))
#define word(x)   (be16toh( *pword (x) ))
#define dword(x)  (be32toh( *pdword(x) ))

/**
 *   print_opcode:
 *   Print an opcode the refc way, using its information.
 *
 *   @arg   s      the pointer to the characters (not-a-string).
 *   @arg   size   the number of characters to read.
 *   @arg   flags   the flags of the multi-character.
 */

static void print_opcode(unsigned int opcode, const uint8_t *s, int flags)
{
   unsigned int c;

   /* get the size of the string */
   unsigned int inisize = 0; int spe = 0;
   const uint8_t *t = s;
   while (*t || spe) {
      if (spe)
         spe = 0;
      else if (strchr("\xF9\xF7\x7F\xE5\xE6\xE7", *t))
         spe = *t;

      t++;
      if (!spe) inisize++;
   }

   unsigned int size = inisize;
   /* print the info */
   printf("0x%04X - ", opcode);
   if (!size) printf("<empty>");
   while (size--) {
      if (*s && strchr("\xF9\xF7\x7F\xE5\xE6\xE7", *s)) {
         c = ((unsigned int)*s << 8) | *(s + 1);
         s++;
      } else
         c = *s;

      s++;
      if (inisize == 1 && c == opcode && !flags)
         printf("<simple>");
      else if (inisize == 1 && (c & 0xFF) == 0x40)
         printf("<unattributed>");
      else printf("0x%0*X", (c > 0xFF) ? 4 : 2, c);
      if (size) printf(",");
   }
   if (flags & 0x08)
      printf(",0x20");

   // if (flags & 0x04) -- see syscall 0x6a6
   if (opcode == 0x3A || opcode == 0x0D || opcode == 0x0C || opcode == 0x00)
      printf(" [is end of line]");

   printf("\n");
}

/**
 *   main:
 *   The main function of the program.
 *
 *   The program should be called this way:
 *      ./a.out <path/to/os.bin>
 *
 *   @arg   ac      the arguments count
 *   @arg   av      the arguments values
 *   @return         the error code (0 if ok)
 */

/* main function */
int main(int ac, char **av)
{
   int ret = 1;
   FILE *f = NULL; char *os = NULL;

   /* open the thing */
   if (ac != 2) return (0);
   f = fopen(av[1], "r");
   if (!f) return (1);

   /* get the OS size */
   fseek(f, 0, SEEK_END);
   long sz = ftell(f);
   fseek(f, 0, SEEK_SET);

   /* get the OS */
   os = malloc(sz);
   if (!os) goto fail;
   fread(os, 1, sz, f);
   fclose(f); f = NULL;

   /* get the table base */
   uint32_t base = 0;
   uint32_t is = dword(dword(0x8001007C) + 4 * 0x7FC);
   printf("Syscall address is 0x%08X\n", is);
   for (; word(is) != 0x000B /* current instruction isn't `rts` */; is += 2) {
      /* check if it's a mov.l opcode */
      if ((word(is) & 0xF000) != 0xD000)
         continue;

      /* get the offset */
      base = dword((is + 4 * (word(is) & 0x00FF) + 4) & 0xFFFFFFFC);

      /* check if it points to OS range */
      if ((base >= 0x80010000) && (base <= 0x80400000))
         break;

      /* reset the base address */
      base = 0;
   }
   if (!base) {
      fprintf(stderr, "We have not found the base!\n");
      goto fail;
   }

   /* we have the base! */
   printf("Base address is 0x%08X\n", base);
   for (int tid = 0; tid < 7; tid++) {
      for (int minor = 0x00; minor <= 0xFF; minor++) {
         uint32_t cbase = base + tid * 0x800 + minor * 8;

         int major = (int[]){0x00, 0xF9, 0x7F, 0xF7, 0xE5, 0xE6, 0xE7}[tid];
         uint32_t daddr = dword(cbase), snd = dword(cbase + 4);
         /* `size` used to be calculated here, but it was unreliable */
         int flags = (snd >> 28);

         /* check if valid character, and not '@' */
         if (daddr < 0x80010000 || daddr > 0x80400000) continue;
         print_opcode((major << 8) | minor, pbyte(daddr), flags);
      }
   }

   /* we're good */
   ret = 0;
fail:
   if (f) fclose(f);
   free(os);
   return (ret);
}


EDIT: Updated with new information.
Last edited by cakeisalie5 on Mon Feb 20, 2017 3:38 pm, edited 2 times in total.
Part of the Planète Casio community (FR) - main author of Cahute

Senior Member
User avatar
Posts: 101
Joined: Sun Mar 27, 2016 10:24 am
Location: France
Calculators: Casio Afx 1.0, Casio fx-9860GII, Casio fx-CG50

Re: FONTCHARACTER Reference - compatibility, sets

Postby cakeisalie5 » Sun Jan 29, 2017 12:52 am

Huh... that's awkward. I've tried disassembling the 0x7FC (as a training) and, while understanding where your method comes from, my result was this (disasm):

0x7FC: Show
Code: Select all
/**
 *   OpcodeToStr:
 *   Opcode to multi-byte string.
 *
 *   @arg   opcode      the opcode.
 *   @arg   string      the destination buffer.
 *   @return            the size?
 */

int OpcodeToStr(unsigned short opcode, unsigned char *string)
{
   char buf[32];
   char **addr;
   int ret;

   /* get the table address, and the minor. the tables order are:
    * 0x00, 0xF9, 0x7F, 0xF7, 0xE5, 0xE6, 0xE7 */
   if (opcode <= 0xFF)
      addr = (char**)0x801e5974;
   else if (opcode >= 0x7F00 && opcode <= 0x7FFF) {
      addr = (char**)0x801e6974;
      opcode -= 0x7F;
   } else if (opcode >= 0xF700 && opcode <= 0xF7FF) {
      addr = (char**)0x801e7174;
      opcode -= 0xF7;
   } else if (opcode >= 0xF900 && opcode <= 0xF9FF) {
      addr = (char**)0x801e6174;
      opcode -= 0xF9;
   } else if (opcode >= 0xE500 && opcode <= 0xE5FF) {
      addr = (char**)0x801e7974;
      opcode -= 0xE5;
   } else if (opcode >= 0xE600 && opcode <= 0xE6FF) {
      addr = (char**)0x801e8174;
      opcode -= 0xE6;
   } else if (opcode >= 0xE700 && opcode <= 0xE7FF) {
      addr = (char**)0x801e8974;
      opcode -= 0xE7;
   } else
      addr = NULL;

   if (addr) {
      addr += (opcode << 1);

      memcpy(buf, *addr, 32);
      ret = MB_ByteCount(buf);
      MB_strcpy(string, buf);

      unsigned char byte = *(uint8_t*)(buf + 1);
      if (byte & 0x80) {
         string[ret++] = 0x20;
         string[ret] = 0;
      }
      if (byte & 0x40) {
         /* ... what? */
         string[ret] = 0;
      }
   }

   return (ret);
}

First thing I saw was that the byte was read after the string was copied, so the length of the string looks unused (which means if they are stripped, this won't have any effect... unless some other syscalls use them?).

Second thing is that this syscall doesn't add any newline on the 0x40 flag, and it doesn't look like there is a newline-only character (0x0A is used by something else). Do you know if there is any syscall related to that? The SDK manual about the character set deals with control characters in the 0xF9 table, but I don't know if these are used for newlines... Oo
Last edited by cakeisalie5 on Mon Jan 30, 2017 8:34 pm, edited 1 time in total.
Part of the Planète Casio community (FR) - main author of Cahute

Senior Member
User avatar
Posts: 605
Joined: Sat Sep 15, 2012 6:59 am
Location: Krautland ****
Calculators: Casio fx-7400GII, Casio fx-7400GII (SH4), Casio fx-9750GII, Casio fx-9750GII (SH4), Casio fx-9860G, Casio fx-9860G SD, Casio fx-9860G Slim, Casio fx-9860GII SD, Casio fx-9860GII SD Power Graphic 2, Casio Classpad 330 plus, Casio fx-CG20, Casio fx-CG50, Casio Classpad fx-CP400

Re: FONTCHARACTER Reference - compatibility, sets

Postby SimonLothar » Sun Jan 29, 2017 9:26 am

cakeisalie5 wrote:First thing I saw was that the byte was read after the string was copied, so the length of the string looks unused (which means if they are stripped, this won't have any effect... unless some other syscalls use them?).
I did not find any reference to the length-nibble. The length of an OpCodeString seems to be determined by syscall sys0534_MB_ByteCount only.
cakeisalie5 wrote:Second thing is that this syscall doesn't add any newline on the 0x40 flag, and it doesn't look like there is a newline-only character (0x0A is used by something else). Do you know if there is any syscall related to that? The SDK manual about the character set deals with control characters in the 0xF9 table, but I don't know if these are used for newlines... Oo
It looks as if the necessity of a newline is determined by syscall
0x06A6: int PRGM_IsEndOfLine( char*opcode );
If *opcode is 0x3A(:), 0x0D, 0x0C or 0x00, the function returns 1.
Else it returns 0.
Inside of syscall 0x07fc the 0x40 flag is used without any detectable effect. A zero byte is replaced by a zero byte, if I am not totally mistaken.
I'll be back!

Senior Member
User avatar
Posts: 101
Joined: Sun Mar 27, 2016 10:24 am
Location: France
Calculators: Casio Afx 1.0, Casio fx-9860GII, Casio fx-CG50

Re: FONTCHARACTER Reference - compatibility, sets

Postby cakeisalie5 » Sun Jan 29, 2017 1:30 pm

SimonLothar wrote:The length of an OpCodeString seems to be determined by syscall sys0534_MB_ByteCount only.

Well, it looks like MB_strcpy doesn't use the result of MB_ByteCount. So anyway, yeah, that looks null-terminated.

Also:
SimonLothar wrote:Try

for (int minor = 0; minor <= 255; minor++) {

instead of

for (int minor = 0; minor < 255; minor++) {

Well actually, the syscall I have here (OS 2.05, fx-9860GII-2) uses a cmp/gt, so it is < 0xLLFF (where 0xLL is the leading character). :P (see my disasm on my last post ^^)

And thanks for the 0x6A6 syscall!
I've updated my program with the new elements :)
Part of the Planète Casio community (FR) - main author of Cahute

PreviousNext

Return to Calculator Hacking/Modding Discussions

Who is online

Users browsing this forum: No registered users and 33 guests