TextIOC Version 1.0 Release


Today I released the first version of TextIOC as part of the LLVM branch of the C toolchain. The library will not officially be available until the next toolchain release, but in the meantime, it is available for download at its Github repository.

Here are several screenshots taken from some of the toolchain examples:


Timed Input



Click here to view the demo's source code.

This example demonstrates the textio_TimedInput function. The time displayed is in seconds. Each IDS allocates 3 bytes for a timer, so each timer has a theoretical maximum capacity of 16,777,216 seconds, or about 4,660 hours. Plenty of leeway for any project that you might have. Wink

To implement this timer, TextIOC uses the third calculator timer, leaveing the first two timers free for progam use.


Program/Appvar Name Input



Click here to view the demo's source code.

This screenshot shows how the textio_SetPrgmNameFlag and the textio_SetThetaCharacter functions work. Each IDS has a byte allocated for special attributes. These attributes include whether or not an IDS is timed, is locked, set for program/appvar input, or if its data buffer is full. When the prgm_name flag is set, textio_Input will not accept a numeral as the first input character and locks the key that moves the cursor left, executing the backspace instead. The textio_SetThetaCharacter function determines which codepoint in the current font will act as the theta character. In the example above, I set the theta character value to 255. Thus, instead of the TI-OS value for theta (0x5b or 91; "[" in ASCII), the input function inserts 255 in the program/appvar name. TextIOC also offers two functions, textio_ConvertProgramAppvarName_TIOS and textio_ConvertProgramAppvarName_TextIOC, to convert the name into a TI-OS compatible format from a TextIOC compatible format and vice versa.


Advanced Input



Click here to view the demo's source code.

This demonstration shows how the IDS_Lock flag works as well as how to switch between different input fields.


TextIOC provides four keymaps of its own: uppercase letters, lowercase letters, numerals, and a special program/appvar name keymap. However, it also supports custom keymaps using the special string format given with the os_GetCSC function in the tice.h documentation.

The library also supports cursor customization, offering such functions as textio_SetCursorDimensions, textio_SetCursorColor, and textio_SetCursorBlinkRate.


As always, any feedback is greatly appreciated! Thanks again to everyone who helped make this project possible! Very Happy
@Adriweb:

After looking at some of the other autotext.json files in the toolchain examples, I believe I have figured out what they do: they load their associated demo into CEmu and test it. The first three sections: "transfer_files:", "target:", and "sequence:" also seem to make sense. What I don't understand is what kind of "sequence:" I am supposed to use for the TextIOC demos and how the "description:" blocks work.

I fixed part of the Travis build by modifying the call to GetCSC, but I think it is still failing because I don't have the autotest files in place.

Thank you for your help! Smile

EDIT: My apologies for double posting.
Well actually it fails because of another ti.xxxx call as you can see in the travis log, but I see you fixed it already Razz

Regarding the Autotest, well, you can copy/paste an existing json file from another project, but yeah you seem to have it already figured out for some parts. The description parts, well, you can actually just modify the expected CRCs values by getting them from CEmu Smile There is an autotester window/doc that allows you to get them.
So basically go step by step in CEmu, with your example programs, then for each step worthy of testing, get the crc/hash in CEmu and write that in the autotest file after the sequence of action (keypresses generally) to reach that step.
Another way of getting the CRCs is to just write in the JSON fake/wrong values at the expected steps and wait for the autotest log to give you the difference between actual and expected values, then put in the json file the actual values. But if you do that, make sure to (re)play the autotest within CEmu to make sure that it actually runs succesfully and everything is displayed and done as you want it.

An official documentation of the format is available here: https://github.com/CE-Programming/CEmu/blob/master/tests/autotester/json_file_documentation.txt
That said, the source code is the best documentation, as always Very Happy

__

Regardless, this is impressive work, your PR is big, with documented code, examples, and soon tests, great job!

That said, it would have been nice to have a few brainstorming sessions with the usual toolchain/libs contributors in order to think more about the architecture side of things, as Mateo already has comments about it Razz
Thanks, Adriweb! Very Happy

I have added the autotests and modified the makefiles for each TextIOC example, using the format I found in the other toolchain examples, including the -Oz flag for LLVM. Travis now builds perfectly, but AppVeyor does not for some reason.

Adriweb wrote:
That said, it would have been nice to have a few brainstorming sessions with the usual toolchain/libs contributors in order to think more about the architecture side of things, as Mateo already has comments about it Razz

That sounds good. Can you recommend a time when all of the contributors could meet? Will this conversation take place on SAX, or IRC?
We're generally all there on the #ez80-dev IRC channel, since many years ago Razz
(and #cemu-dev for CEmu specific stuff, but that's less the concern here)

And yep, I saw you added the autotests, that's good Smile

Regarding AppVeyor failing, yes that's normal, it wasn't updated to work with LLVM yet. And it might never be if we switch to GitHub actions anyway.
I know we briefly talked, but ideally there should be a structure that contains pointers to functions that implement the different mechanisms to instrument the library. A pointer to a character drawing function, a pointer to get a character's width, etc. Then the user would pass this structure to a textio function which would read the structure and modify it's internal calls to use the ones provided by the user.
Additionally, you can have a define in your header file that provides a struct initializer that contains a set of default routines that can be used. This way you don't have any direct dependencies, but the user is able to choose the routines and can still use some reasonable set of defaults with little code.
I did some work on integrating GraphX support for TextIOC this afternoon and have gotten a semi-working input function that uses the GraphX font.

To make the library compatible with the GraphX text functions, I added this structure and function to the library's header file:

Code:
typedef struct {
   void (*set_cursor_position)(int, int);
   int (*get_cursor_x)(void);
   int (*get_cursor_y)(void);
   void (*draw_char)(char);
   unsigned int (*get_char_width)(char);
   unsigned int (*get_string_width_l)(char *, size_t);
} textio_library_routines_t;

/* Function for setting up the library to use external text functions. */
void textio_SetLibraryRoutines(textio_library_routines_t *ptr);


I have had a difficult time with structures since learning C, so this might not be the best way of implementing multi-function compatibility.

In the C file, I create a function that passes the GraphX text functions to the library:

Code:
void setup_textio(void) {

   textio_library_routines_t *ptr = malloc(sizeof(textio_library_routines_t));

   ptr->set_cursor_position = &gfx_SetTextXY;
   ptr->get_cursor_x = &gfx_GetTextX;
   ptr->get_cursor_y = &gfx_GetTextY;
   ptr->draw_char = &gfx_PrintChar;
   ptr->get_char_width = &gfx_GetCharWidth;
   ptr->get_string_width_l = &textio_gfx_GetStringWidthL;

   textio_SetLibraryRoutines(ptr);
   free(ptr);
   return;
}


The textio_gfx_GetStringWidthL is an internal library function that I created as a surrogate for FontLib's function of the same name. Wink

I created several three-byte pointer slots in the library's internal data to hold pointers to the external text functions and removed all of the direct calls to the FontLib functions and replaced them with calls to general util.* functions which handle the pointers to the external functions. For example, instead of calling fontlib_DrawGlyph, the library calls util.DrawChar which pushes the argument to the stack, gets the external function pointer from the library's internal data, and jumps to that location.

I found during the testing that I will need a variable to tell TextIOC from which library (GraphX or FontLibC) the text functions are coming from because some of the functions do not have the same parameters or return value types, which means different registers are used.

I also found that I will need to separate the cursor's y-position from that of its parent IDS so that the programmer can choose how much of the cursor should project above the top of the font.

Do you see anything in this approach that could be improved?
Version 2.0 Release

I am happy to announce the release of the second version of TextIOC. This version supports both FontLibC and GraphX fonts for both text input and output using two new functions, textio_SetSourceLibrary and textio_SetSourceLibraryRoutines. These functions supply TextIOC with pointers to certain basic text functions such as setting and getting the text coordinates, printing characters, and so on.

The following example shows how to specify the source library that TextIOC will use.

Code:

// To setup GraphX as the source library, call this function before using any TextIOC function.

void setup_gfx_textio(void) {

   /* Allocate the pointer structure. */
   textio_library_routines_t *ptr = malloc(sizeof(textio_library_routines_t));

   /* Tell TextIOC that it will be using GraphX. */
   textio_SetSourceLibrary(TEXTIO_SET_GRAPHX_AS_SRC_LIB);

   /* Set the struct's pointers to the requisite GraphX functions. */
   ptr->set_cursor_position = &gfx_SetTextXY;
   ptr->get_cursor_x = &gfx_GetTextX;
   ptr->get_cursor_y = &gfx_GetTextY;
   ptr->draw_char = &gfx_PrintChar;
   ptr->get_char_width = &gfx_GetCharWidth;

   /* Pass the struct pointers to TextIOC. */
   textio_SetLibraryRoutines(ptr);
   
   /* Free the structure memory. */
   free(ptr);
   
   return;
}

// To setup FontLib as the source library, call this function before calling any TextIOC functions.

void setup_fontlib_textio(void) {

   /* Allocate the pointer structure. */
   textio_library_routines_t *ptr = malloc(sizeof(textio_library_routines_t));

   /* Tell TextIOC that it will be using FontLibC. */
   textio_SetSourceLibrary(TEXTIO_SET_FONTLIBC_AS_SRC_LIB);

   /* Set the struct pointers to the necessary FontLib functions. */
   ptr->set_cursor_position = &fontlib_SetCursorPosition;
   ptr->get_cursor_x = &fontlib_GetCursorX;
   ptr->get_cursor_y = &fontlib_GetCursorY;
   ptr->draw_char = &fontlib_DrawGlyph;
   ptr->get_char_width = &fontlib_GetGlyphWidth;

   /* Pass the struct pointers to TextIOC. */
   textio_SetLibraryRoutines(ptr);
   
   /* Free the structure memory. */
   free(ptr);
   
   return;
}


And that is all you need to setup TextIOC!

As part of the abstraction process, I made a text window for textio_PrintText that operates separately from the FontLib text window. In doing so I had to recreate some of DrDnar's functions for the TextIOC text window, so this window has its own newline character, line spacing, and so on. Separating the two windows, however, also means that you can have both a FontLib and a TextIOC text window operating at the same time on the same screen, instead of only be able to use one.

Late in the abstracting, I found a problem with how the library handled drawing the theta character. With a custom FontLib font, you could create a theta glyph at some inconsequential offset, like 0 or 255, tell TextIOC was where it was located, and it would draw the character. With GraphX, however, it would be difficult to directly edit the library font itself, and then, when TextIOC was finished, it would have to edit the library font again to erase the character. To get around this problem, I added textio_SetDrawThetaCharFunction, which takes a pointer to a programmer-defined function that draws the theta character.

I failed to mention in the first release post that you can also specify custom keybindings for the input function that activate the backspace function, the clear function, and the cursor movement functions.

You can see the updated code examples at the Github repository.
Nice work Smile There shouldn't be a textio_SetSourceLibrary function though Wink

Ideally there should be a default #define that sets up the structure for you if you are using graphx or fontlib, which you can then pass to textio_SetLibraryRoutines. Something like this:


Code:
void setup_fontlib_textio(void) {

   /* Set the structure pointer. */
   textio_library_routines_t ptr = TEXTIO_FONTLIB_ROUTINES;

   /* Pass the struct pointers to TextIOC. */
   textio_SetLibraryRoutines(&ptr);
   
   return;
}


The user can modify the pointers as they see fit:


Code:
void setup_fontlib_textio(void) {

   /* Set the structure pointer. */
   textio_library_routines_t ptr =
   {
     custom_func0,
     custom_func1,
     custom_func2,
     custom_func3,
     custom_func4
   };

   /* Pass the struct pointers to TextIOC. */
   textio_SetLibraryRoutines(&ptr);
   
   return;
}


You'll probably have to abstract things a bit more in your library to not depend on the particular argument locations. Anywho.
Earlier today on the SAX chat, I discussed the need for further abstraction and code efficiency for TextIOC with Mateo and jacobly.

As my limited C, linker, and compiler knowledge did not help me understand the whole of their advice, here is a list of what I think they were trying to make me understand.

  • The library as a whole needs to have more abstraction. The textio_SetSourceLibrary should be removed as it makes the library dependant on FontLibC (I am not including GraphX here because it is already a dependency for cursor drawing).
  • There should be a better way to give the external function pointers to TextIOC. jacobly suggested using wrappers for GraphX and FontLibC to accomplish this.
  • TextIOC should be designed to support backwards compatibility in the event that new features are added.

Please note this list is my interpretation of what jacobly and Mateo said.

Going on this conversation and Mateo's previous post, I created some experimental code that smooths out the differences between the FontLibC API and the GraphX API, ex TextIOC (In other words, TextIOC does not need any assembly source code to sort out the differences. All of the smoothing is done outside of the library in a seperate C file.). This code is not attached to TextIOC, but it does compile.

First is a C file containing wrappers for FontLib and GraphX.

Code:
#include "fonts/fonts.h"
#include "main.h"
#include <stdint.h>
#include <graphx.h>
#include <fontlibc.h>
#include <tice.h>
#include <debug.h>

#define TEXTIO_FONTLIB_ROUTINES { \
            2, \
            textio_fontlib_SetCursorPosition, \
            textio_fontlib_GetCursorX, \
            textio_fontlib_GetCursorY, \
            textio_fontlib_GetGlyphWidth, \
            textio_fontlib_DrawGlyph \
            };

#define TEXTIO_GRAPHX_ROUTINES { \
            2, \
            textio_gfx_SetTextXY, \
            textio_gfx_GetTextX, \
            textio_gfx_GetTextY, \
            textio_gfx_GetCharWidth, \
            textio_gfx_PrintChar\
            };

/* Function typecasts for FontLibC */
void textio_fontlib_SetCursorPosition(uint24_t xPos, uint24_t yPos) {
   fontlib_SetCursorPosition((unsigned int)xPos, (uint8_t)yPos);
   return;
}

uint24_t textio_fontlib_GetCursorX(void) {
   return (uint24_t)fontlib_GetCursorX();
}

uint24_t textio_fontlib_GetCursorY(void) {
   return (uint24_t)fontlib_GetCursorY();
}

uint24_t textio_fontlib_GetGlyphWidth(char codepoint) {
   return (uint24_t)fontlib_GetGlyphWidth(codepoint);
}

void textio_fontlib_DrawGlyph(char codepoint) {
   fontlib_DrawGlyph((uint8_t)codepoint);
   return;
}


/* Function typecasts for GraphX */
void textio_gfx_SetTextXY(uint24_t xPos, uint24_t yPos) {
   gfx_SetTextXY((int)xPos, (int)yPos);
   return;
}

uint24_t textio_gfx_GetTextX(void) {
   return (uint24_t)gfx_GetTextX();
}

uint24_t textio_gfx_GetTextY(void) {
   return (uint24_t)gfx_GetTextY();
}

uint24_t textio_gfx_GetCharWidth(char codepoint) {
   return (uint24_t)gfx_GetCharWidth((const char)codepoint);
}

void textio_gfx_PrintChar(char codepoint) {
   gfx_PrintChar((const char)codepoint);
   return;
}

void main(void) {
   
   textio_library_routines_t ptr = TEXTIO_FONTLIB_ROUTINES;
   // textio_SetLibraryRoutines(&ptr);
   exit(0);
}


Second is the C file's associated header file.

Code:
#ifndef MAIN_H
#define MAIN_H


typedef struct {
   uint8_t version;
   void *set_text_position;
   void *get_text_x;
   void *get_text_y;
   void *draw_char;
   void *get_char_width;
} textio_library_routines_t;


#endif


This model addresses three, key problems.

First, it provides backwards compatibility for older library versions. When textio_SetLibraryRoutines gets the structure pointer, the first field it stores is the library version number. Using the following version of textio_SetLibraryRoutines, this forces the function to store only those function pointers that the particular library version needs.

Code:
;-------------------------------------------------------------
textio_SetLibraryRoutines:
; Arguments:
;   arg0 = pointer to structure
; Returns:
;   None
; Destroys:
;   HL and IY

   ld   hl,arg0
   add  hl,sp
   ld   iy,(hl)
   ld   a,(iy)
   ld   (_Version),a
   ld   hl,(iy + 3)
   ld   (_SetTextPosition),hl
   ld   hl,(iy + 6)
   ld   (_GetTextX),hl
   ld   hl,(iy + 9)
   ld   (_GetTextY),hl
   ld   hl,(iy + 12)
   ld   (_DrawChar),hl
   ld   hl,(iy + 15)
   ld   (_GetCharWidth),hl
   ld   a,2         ; End of version 2 function pointers
   cp   a,(_Version)
   ret  z
; Load function pointers for version 3
   ld   a,3
   cp   a,(_Version)
   ret  z
; Load function pointers for version 4
; ...


One problem that I thought about for backwards compatibility is the following scenario: What if version 3 of the library provides a new feature in a function present in version 2 that requires an additional external function that version 2 does not supply?. My solution was for the function to check the library version beforehand to determine if it should run the feature or not. This alternative would, however, slow down the function somewhat. Suggestions welcome!

Secondly, it provides for changes in either the FontLib or GraphX APIs. For instance, if the return value for gfx_GetCharWidth was changed from int to uint8_t, all that would have to be changed is a cast in the C file rather than modifying TextIOC's source code, as it would have to be done in the current release.

Third, the programmer can customize the functions at will. As long as they return the value or perform the operation TextIOC expects, the programmer can use any custom function he wishes.

As mentioned earlier, this code provides TextIOC with a uniform API from the two external libraries. Both character-width functions return an uint24_t, both print-character functions return void, and so on. This uniformity makes textio_SetSourceLibrary unnecessary.

@Mateo and jacobly: Would this experiment sufficiently abstract TextIOC and satisfy the underlying linker/compiler requirements?

I would also like to thank Mateo for pointing out the structure initialization trick above. I was unaware that you could do that! Smile
That looks much better to me. You can even make default wrapper functions and put them in your header file, which can be used for either graphx or fontlib in order to make the user's life easier without having to see a bunch of code. Just be sure to declare the functions as static to prevent name conflict; the compiler will optimize them away if they aren't used in a file anyway.

I don't think there is much issue with speed; a single check won't slow things down that much. As for other libraries changing; I hope that they do not but it shouldn't affect anything because of the wrappers.

Anyway; great work Smile I'll let you know if I think of any other things.
Thanks a lot! Smile

My goal tomorrow will be to setup the default, static defines like you suggested, merge the wrapper with TextIOC, and modify the examples to work with it. I am looking forward to how much this will reduce the size of each example program. Very Happy

If nothing else comes up, I will also update the PR with fresh examples and autotests in the next day or two.

-------------------------------------------

EDIT: I do not know how to tell the linker where the functions are defined. My first attempt was to use a prototype declaration and '#define' pair in textioc.h, but that didn't work.

Code:
static void textio_fontlib_SetCursorPosition(uint24_t xPos, uint24_t yPos);
#define textio_fontlib_SetCursorPosition(xPos, yPos) \
fontlib_SetCursorPosition((unsigned int)xPos, (uint8_t)yPos)


My only other solution was to create a header and source file pair separate from textioc.h, but that didn't seem to be the way Mateo suggested doing it. There must be some C trick which which I am unfamiliar to put the function definitions in the header file. Could someone enlighten me, please?
You put the function wrappers themself in the header file. No prototype.
The "problem" given in this post is not a problem and has been resolved. See the post below.

Putting the function wrappers in the header file makes the program able to be compiled and linked with 'make', but not 'make debug'. This is the error:

Code:
$ make debug
"[compiling C]   src/main.c"
C:\CEdev\programs\textioc\dev\examples\fontlib\wrap-text\src\main.c
"[linking] bin/DEMO.bin"
C:\CEdev\include\fasmg-ez80\ld.fasmg [758] macro ? [634] macro handle [5] macro parse [300] obj\main.src [1188]:
        .BEGFUNC 'main',10,'_main'
macro ? [2] macro BEGFUNC [8] macro uleb128 [4]:
        assert val >= 0
Processed: assert val >= 0
Error: assertion failed.
make: *** [bin/DEMO.bin] Error 2


This is the example program I am updating:

Code:
#include "fonts/fonts.h"

#include <textioc.h>
#include <graphx.h>
#include <fontlibc.h>
#include <debug.h>

void setup_fontlib_textio(void);

void main(void) {
   
   char text[] = {"This line starts with a tab, as many paragraphs usually do in the Western world. This paragraph, made up of several lines of testing text, spans about ten lines. Its objective is to reveal any lurking problems in the textio_PrintText() function which is resposible for the TextIOC's text wrapping."};

   /* Start the graphics */
   gfx_Begin();
   
   /* Setup the custom font. */
   fontlib_SetFont(test_font, 0);
   
   /* Setup the source library. */
   setup_fontlib_textio();
   textio_SetFontHeight(fontlib_GetCurrentFontHeight());
   textio_SetLineSpacing(0, 0);
   
   /* Set the text window dimensions*/
   textio_SetTextWindow(0, 0, 140, 240);
   
   /* Set print format to left-margin flush */
   textio_SetPrintFormat(TEXTIOC_PRINT_LEFT_MARGIN_FLUSH);
   
   /* Set the number of spaces that make up the tab */
   textio_SetTabSize(5);
   
   /* Print the text in the TextIOC text window. */
   textio_PrintText(text, 0);
   
   /* Print the same text in a FontLib text window. */
   fontlib_SetWindow(160, 0, 150, 240);
   fontlib_SetNewlineOptions(FONTLIB_ENABLE_AUTO_WRAP);
   fontlib_HomeUp();
   fontlib_DrawString(text);
   
   /* Outline the window so we can see it */
   gfx_SetColor(224);
   gfx_Rectangle_NoClip(0, 0, 140, 240);
   gfx_Rectangle_NoClip(160, 0, 150, 240);
   
   /* Wait for keypress */
   while (!os_GetCSC());
   
   /* Close the graphics */
   gfx_End();
   exit(0);
}

void setup_fontlib_textio(void) {

   /* Set the structure pointers to the FontLib functions. */
   textio_library_routines_t ptr = TEXTIO_FONTLIB_ROUTINES;

   dbg_Debugger();

   /* Pass the struct pointers to TextIOC. */
   textio_SetLibraryRoutines(&ptr);
   
   return;
}


And this is what the wrapper part of textioc.h looks like:

Code:
/**
 * This structure holds the pointers to the source library's text functions.
 *
 * @see textio_SetLibraryRoutines
*/
typedef struct {
   /**
    * Library version
   */
   uint8_t library_version;
   /**
    * Pointer to either gfx_SetTextXY or fontlib_SetCursorPosition
   */
   void *set_text_position;
   
   /**
    * Pointer to either gfx_GetTextX or fontlib_GetCursorX
   */
   void *get_text_x;
   
   /**
    * Pointer to either gfx_GetTextY or fontlib_GetCursorY
   */
   void *get_text_y;
   
   /**
    * Pointer to gfx_PrintChar or fontlib_DrawGlyph
   */
   void *draw_char;
   
   /**
    * Pointer to gfx_GetCharWidth or fontlib_GetGlyphWidth
   */
   void *get_char_width;
} textio_library_routines_t;

/**
 * Function typecasts for FontLibC
*/
static void textio_fontlib_SetCursorPosition(uint24_t xPos, uint24_t yPos) {
   fontlib_SetCursorPosition((unsigned int)xPos, (uint8_t)yPos);
   return;
}

static uint24_t textio_fontlib_GetCursorX(void) {
   return (uint24_t)fontlib_GetCursorX();
}

static uint24_t textio_fontlib_GetCursorY(void) {
   return (uint24_t)fontlib_GetCursorY();
}

static uint24_t textio_fontlib_GetGlyphWidth(char codepoint) {
   return (uint24_t)fontlib_GetGlyphWidth(codepoint);
}

static void textio_fontlib_DrawGlyph(char codepoint) {
   fontlib_DrawGlyph((uint8_t)codepoint);
   return;
}

/**
 * Function typecasts for GraphX
 */
static void textio_gfx_SetTextXY(uint24_t xPos, uint24_t yPos) {
   gfx_SetTextXY((int)xPos, (int)yPos);
   return;
}

static uint24_t textio_gfx_GetTextX(void) {
   return (uint24_t)gfx_GetTextX();
}

static uint24_t textio_gfx_GetTextY(void) {
   return (uint24_t)gfx_GetTextY();
}

static uint24_t textio_gfx_GetCharWidth(char codepoint) {
   return (uint24_t)gfx_GetCharWidth((const char)codepoint);
}

static void textio_gfx_PrintChar(char codepoint) {
   gfx_PrintChar((const char)codepoint);
   return;
}

/**
 * Default external function pointers for FontLibC
*/
#define TEXTIO_FONTLIB_ROUTINES { \
            2, \
            textio_fontlib_SetCursorPosition, \
            textio_fontlib_GetCursorX, \
            textio_fontlib_GetCursorY, \
            textio_fontlib_GetGlyphWidth, \
            textio_fontlib_DrawGlyph \
            };

/**
 * Default external function pointers for GraphX
*/
#define TEXTIO_GRAPHX_ROUTINES { \
            2, \
            textio_gfx_SetTextXY, \
            textio_gfx_GetTextX, \
            textio_gfx_GetTextY, \
            textio_gfx_GetCharWidth, \
            textio_gfx_PrintChar \
            };


Building the example with 'make' and using the debugger in textio_SetLibraryRoutines, I found that the pointers in the structure are located in the mysterious region of $AAxxxx.

------------------------------------------------------------------------------

EDIT: After setting up the LLVM-based toolchain with tremendous help from jacobly and Adriweb, I rebuilt the example and found why the structure pointers were off. For some odd reason the most-significant byte of the pointer is stored last rather than first, making the resultant very, very wrong. Here is an example stacktrace:

Code:
Address    Value      Pointer
------     ------     --------
D1A810     D1AC8A     
D1A813     D1A81C     
D1A816     D1A82C     
D1A819     D1A81C     
D1A81C     AC8F02     Should be d1ac8f | textio_fontlib_SetCursorPosition | (d1 -> 02 for some odd reason)
D1A81F     ACA5D1     Should be d1aca5 | textio_fontlib_GetCursorX
D1A822     ACA9D1     Should be d1aca9 | textio_fontlib_GetCursorY
D1A825     ACB2D1     Should be d1acb2 | textio_fontlib_GetGlyphWidth
...        ...


------------------------------------------------------------------------------

EDIT TWO: I did some more experimenting and found why the stack values are off. First, on close examination, the 02 that I thought should be a d1 turns out to be the uint8_t that holds the library version number. I tried using an uint24_t instead of the uint8_t to hold the library version number since it is same size as a stack value, and the substitution fixed the function pointer problem. The example, however, does not behave as it should. I am working on pinpointing the problem.

Is there any explanation for this odd structure behavior?
After a discussion with commandz and jacobly, I learned that my code for textio_SetLibraryRoutines was faulty and that the stack behavior above was normal. The library behaved normally after I modified textio_SetLibraryRoutines and fixed a lurking bug in the newline utility function. TextIOC and all of the examples now build and function as expected on the LLVM-based toolchain. Smile

Now that I have the toolchain installed and the library functioning again, I can start getting fresh autotests for the pull request.
After some discussion with Mateo on SAX, I have done a little work redesigning TextIOC. The C-like psuedocode below shows what the new version may look like. Please note that I have renamed the Input Data Structure (IDS) as the Input Control Structure (ICS).

Code:
/* This is the ICS structure, defined in textioc.h */
typedef struct {
   uint24_t buffer_size;
   char *first_visible_char_ptr;
   char *curr_char_ptr;
   uint24_t visible_buffer_width;
   uint24_t xPos;
   uint8_t yPos;
   uint24_t cursor_x;
   uint8_t cursor_y;
} textio_ics_t textio_ics;


allocate_memory_for_ics_keymaps_and_buffers();
create_custom_ics();

/* This loads an ICS's data into the library's internal data. */
textio_LoadICS(ics_ptr);

/* This tells the library where the current keymap is located. */
textio_LoadKeymap(keymap_one);

for (;;) {

   draw_buffer_contents();

   /* If the programmer wants timed input, he can use the hardware
      timers the normal way and put them in this loop. */
   do {
      kb_Scan();
      draw_cursor(xPos, yPos);
   } while (!kb_AnyKey());

   /* Scan the key registers and store the value to key. */

   if (key != control_key && key != submission_key) {
      character = textio_GetChar(keymap, key)
      textio_InsertChar(buffer, character);
   };

   if (key == Delete) {
      textio_DeleteChar(buffer);
   };

   if (key == Clear) {
      textio_Clear(buffer);
   };

   if (key == Left_Arrow) {
      textio_MoveCursorLeft(buffer);
   };

   if (key == Right_Arrow) {
      textio_MoveCursorRight(buffer);
   };

   if (key == Alpha) {
      /* Switch the keymap pointer. */
      textio_LoadKeymap(keymap_ptr);
   };
}

free_memory_for_ics_keymaps_and_buffers();


Basically, I have torn apart the old textio_Input() function and exposed all of the underlying functions to the programmer so that he can build his own input function however he wishes. I have also removed the automatic allocation of memory for the ICSes and the keymaps, leaving it up to the programmer. This allows the programmer to reuse already allocated memory for the ICSes, keymaps, and data buffers.

I have also made the programmer responsible for drawing/erasing the cursor and drawing/erasing the visible part of the data buffer. This allows the programmer to handle special characters, such as theta, and special screen modes, such as drawing text on the homescreen. The toolchain examples for TextIOC will include functions that will handle text and cursor drawing. This will give a starting point that other programmers can then build on to make their own unique functions.

If anyone has any thoughts or suggestions, I would be more than happy to hear them! Smile
TextIOC Input Goals
  • Supports both 8bpp and 16bpp
  • Supports both character insertion (e.g. typical text editor insertion) and overwriting (standard TI-OS behaviour)
  • Supports input scrolling (e.g. input boxes) and fixed input
  • Supports multi-line input (e.g. text editor)
  • Supports standard TI-OS Input function behaviour
  • Supports custom keymaps and keybindings
  • Handles the theta character
  • EDIT: Supports timed input


Input Functions

textio_GetChar - Gets a character from the user and echoes it. Uses getchar().
textio_DrawCursor - Draws a cursor either custom or TI-OS, depending on the library paired with TextIOC
  
Register to Join the Conversation
Have your own thoughts to add to this or any other topic? Want to ask a question, offer a suggestion, share your own programs and projects, upload a file to the file archives, get help with calculator and computer programming, or simply chat with like-minded coders and tech and calculator enthusiasts via the site-wide AJAX SAX widget? Registration for a free Cemetech account only takes a minute.

» Go to Registration page
Page 2 of 2
» All times are UTC - 5 Hours
 
You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot vote in polls in this forum

 

Advertisement