A start value of 196 means 257 - 196 = 61 increments, which would be expected to yield an AUTO_INT_5 rate of 1024 / 61 ~ 16.787 Hz. 16 * 1.0069 ~ 16.11 Hz , so it looks like the timing frequency is a bit off on your calculator, or there's something else going on.
ACK about being able to trigger AUTO_INT_5 at a rate close to 16384 Hz. Maybe I misremembered, or maybe the 89T doesn't behave the same as older models. Anyway, that's not the way to go here.
Could you post at least the first data file for testing purposes ? I'd like to work a bit on the code, to implement my previous suggestions, and more, but I won't go very far without being able to perform proper tests
EDIT 2:
* BTW, in the video, I see hundreds of frames on which multiple top rows are made of only white pixels, or only black pixels. That's less frequent with bottom rows, but it happens as well.
I think that it's worth special-casing these full-white or full-black lines, so as to be able to use just about 6 instructions to write them (moveq #0/#-1,dn; move.l dn,(an)+; move.l dn,(an)+; move.l dn,(an)+; move.l dn,(an)+; move.l dn,(an)+), and thereby save more power by sleeping more until the next frame.
* how many full-white and full-black frames are there in the video ?
EDIT 3: actually, "here are N consecutive full-white/black lines at the top" and "here's a full-white/black frame" are just special cases of "here are N consecutive white/black lines". memset(LCD_MEM, 0x00/0xFF, HEIGHT * LCD_WIDTH) are slower than HEIGHT iterations of a loop based on the aforementioned full line draw, besides. So I'd think that repurposing opcodes 253 and 252 as prefixes for 1 byte containing the number of consecutive white / black lines would be a good thing, all the more it would solve the flashing (potentially seizure-triggering in addition to being time-wasting, boo me) on the top right part of the screen in the code below.
EDIT 1: here's an
entirely untested, and therefore certainly broken, mod of your code, showing part of what I have in mind:
Code: #define USE_TI89
#define SAVE_SCREEN
#include <tigcclib.h>
#define WIDTH 160
#define HEIGHT 100
#define FPS 16
#define TOTAL_STR 16
// A derivative of FastDrawHLine_R from ExtGraph by TICT.
void SpecialFastDrawHLine_R(void* line asm("a0"), unsigned short x1 asm("d0"), unsigned short x2 asm("d1"), short mode asm("d3"));
asm ("
| C prototype:
| Valid values for mode are: A_REVERSE, A_NORMAL, A_REPLACE, A_OR (A_XOR removed).
|
| This routine draws a horizontal line from (x1) to (x2) on the given line address.
.text
.even
0:
.word 0xFFFF,0x7FFF,0x3FFF,0x1FFF,0x0FFF,0x07FF,0x03FF,0x01FF,0x00FF,0x007F,0x003F,0x001F,0x000F,0x0007,0x0003,0x0001
1:
.word 0x8000,0xC000,0xE000,0xF000,0xF800,0xFC00,0xFE00,0xFF00,0xFF80,0xFFC0,0xFFE0,0xFFF0,0xFFF8,0xFFFC,0xFFFE,0xFFFF
.globl SpecialFastDrawHLine_R
SpecialFastDrawHLine_R:
move.l %d4,%a1 | d4 mustn't be destroyed.
| Removed: test and fix for reversed x1 and x2.
| Largely optimized: line address computation.
move.w %d0,%d4
lsr.w #4,%d4
adda.w %d4,%a0
| d4 = 8 * (x1/16 + x1/16) + 16. We add 1 before shifting instead of adding 16
| after shifting (gain: 4 clocks and 2 bytes).
addq.w #1,%d4 | d4 = 8 * (x1/16 + x1/16) + 16.
lsl.w #4,%d4
move.w %d1,%d2 | x2 is stored in d2.
andi.w #0xF,%d0
add.w %d0,%d0
move.w 0b(%pc,%d0.w),%d0 | d0 = mask of first pixels.
andi.w #0xF,%d1
add.w %d1,%d1
move.w 1b(%pc,%d1.w),%d1 | d1 = mask of last pixels.
cmp.w %d4,%d2 | All pixels in the same word ?
blt.s 4f
sub.w %d4,%d2 | d2 = x2 - x.
moveq.l #32,%d4
tst.w %d3
beq.s 0f
| A_NORMAL / A_OR / A_REPLACE.
or.w %d0,(%a0)+
moveq #-1,%d0
sub.w %d4,%d2
blt.s 5f
6:
move.l %d0,(%a0)+
sub.w %d4,%d2
bge.s 6b
5:
cmpi.w #-16,%d2
blt.s 7f
move.w %d0,(%a0)+
7:
or.w %d1,(%a0)
move.l %a1,%d4
rts
| A_REVERSE.
0:
not.w %d0
and.w %d0,(%a0)+
moveq #0,%d0
sub.w %d4,%d2
blt.s 5f
6:
move.l %d0,(%a0)+
sub.w %d4,%d2
bge.s 6b
5:
cmpi.w #-16,%d2
blt.s 8f
move.w %d0,(%a0)+
8:
not.w %d1
and.w %d1,(%a0)
move.l %a1,%d4
rts
4:
and.w %d0,%d1
tst.w %d3
beq.s 8b
or.w %d1,(%a0)
move.l %a1,%d4
rts
");
static inline SYM_ENTRY * openBinVAT(const char *symptrName) { // Quicker file reader than default fopen. Thanks Lionel
return DerefSym(SymFind(symptrName));
}
volatile unsigned short currentFile = 0;
unsigned char * dataPtr;
unsigned char * dataBlockEndPtr;
unsigned char * datas[TOTAL_STR];
unsigned short dataLengths[TOTAL_STR];
DEFINE_INT_HANDLER(DrawFrameInt) {
unsigned char x0 = 0, x1 = 0, currentColor = 0, lastByte = 0; // Initialize vars for writing
unsigned char * line = LCD_MEM;
while (dataPtr < dataBlockEndPtr) { // Draw a frame (essentially copied from for loop, with breaks instead of wait for frame)
unsigned char nextByte = *dataPtr++;
if (line == LCD_MEM + LCD_SIZE) {
// No need to reinitialize variables.
break;
}
unsigned char newColor = !currentColor; // Defined here so it doesnt have to be recalculated.
if (nextByte == 255 || (lastByte < WIDTH && nextByte < lastByte)) { // New horizontal line? Then finish the previous row.
SpecialFastDrawHLine_R(line, x0, WIDTH - 1, newColor);
x0 = 0;
x1 = 0;
line += LCD_WIDTH;
}
if (nextByte < WIDTH) { // This is the normal line draw. Majority
x1 = nextByte;
SpecialFastDrawHLine_R(line, x0, x1, newColor);
currentColor = newColor;
x0 = x1;
} else if (nextByte == 254) { // This is if the frame has not changed. Just wait.
break;
} else if (nextByte == 253) { // Draw a full black screen, and wait
memset(LCD_MEM, 0xFF, HEIGHT * LCD_WIDTH); // That draws also pixels we don't need, but it's still much faster than HEIGHT calls to a generic horizontal line drawing function. Will look like crap on the 92+/V200, though. See EDIT 3.
break;
} else if (nextByte == 252) { // Draw an empty frame, wait
memset(LCD_MEM, 0x00, HEIGHT * LCD_WIDTH); // Ditto.
break;
}
lastByte = nextByte;
}
short row = _rowread(0x0000);
if (dataPtr >= dataBlockEndPtr || row == 8) { // TODO portable row reading.
if (currentFile < sizeof(datas) / sizeof(datas[0])) {
currentFile++;
}
dataPtr = datas[currentFile];
dataBlockEndPtr = dataPtr + dataLengths[currentFile];
} else if (row == 2) { // TODO portable row reading.
if (currentFile > 0) {
currentFile--;
}
dataPtr = datas[currentFile];
dataBlockEndPtr = dataPtr + dataLengths[currentFile];
}
}
// currentFile = -1 is invalid.
/*DEFINE_INT_HANDLER(BreakDrawInt) {
currentFile = -1;
}*/
void _main(void) {
static const char VARNAME_STRS[TOTAL_STR][9] = {
"\0ba\\px01", "\0ba\\px02", "\0ba\\px03", "\0ba\\px04",
"\0ba\\px05", "\0ba\\px06", "\0ba\\px07", "\0ba\\px08",
"\0ba\\px09", "\0ba\\px10", "\0ba\\px11", "\0ba\\px12",
"\0ba\\px13", "\0ba\\px14", "\0ba\\px15", "\0ba\\px16",
};
SYM_ENTRY *symPtrs[TOTAL_STR];
short missingfile = FALSE;
ClrScr(); // Initial setup
for (unsigned short fileI = 0; fileI < TOTAL_STR; fileI++) { // Set up direct pointers to data while locking memory blocks.
SYM_ENTRY * symPtr = openBinVAT(VARNAME_STRS[fileI] + sizeof(VARNAME_STRS[0]) - 1); // Define the data using the vat
if (symPtr == NULL) {
missingfile = TRUE;
DrawStr(0, 0, VARNAME_STRS[fileI], A_NORMAL);
GKeyIn(NULL, 0);
}
unsigned char* data = HLock(symPtr->handle);
data += 2; // Offset from VAT length data
dataLengths[fileI] = *(unsigned short *)data; // Define the length of the data
datas[fileI] = data + 2; // Offset from real length data
symPtrs[fileI] = symPtr;
}
if (missingfile) {
return;
}
// Reinitialize global variables.
dataPtr = datas[0];
dataBlockEndPtr = datas[0] + dataLengths[0];
currentFile = 0;
INT_HANDLER oldInt5 = GetIntVec(AUTO_INT_5); // Save default stuff
INT_HANDLER onInt = GetIntVec(INT_VEC_ON_KEY_PRESS);
unsigned char oldStart = PRG_getStart();
asm volatile("trap #12; move.w #0x0200,%sr"); // Set defaults to new stuff
SetIntVec(AUTO_INT_5, DrawFrameInt);
//SetIntVec(INT_VEC_ON_KEY_PRESS, BreakDrawInt);
PRG_setStart(196); // If start was 196 for all frames, actual playback speed: 16hz * 1.0069 (found by experiment)
PRG_setStart(196);
PRG_setRate(1);
unsigned char frameCounter = 0;
while (currentFile < TOTAL_STR) {
asm volatile("move.b #0b10000,0x600005");
frameCounter++;
if (frameCounter == 73) { // Calculated by: (1/(1.0068 - 1)) / 2 = ~73.5 (note: i dont know why divide by 2 shows up)
PRG_setStart(195);
} else if (frameCounter == 74) {
frameCounter = 0;
PRG_setStart(196);
}
}
GKeyFlush();
asm volatile("trap #12; move.w #0x0000,%sr"); // Restore the program / defaults
SetIntVec(AUTO_INT_5, oldInt5);
SetIntVec(INT_VEC_ON_KEY_PRESS, onInt);
PRG_setStart(oldStart);
for (unsigned short fileI = 0; fileI < TOTAL_STR; fileI++) { // Unlock memory blocks.
HeapUnlock(symPtrs[fileI]->handle);
}
}
Build invocation taking advantage of the toolchain's possibilities and peculiarities (*):
Code: tigcc -std=gnu99 -O3 -Wall -W -Wwrite-strings -fomit-frame-pointer -mregparm=5 -ffunction-sections -fdata-sections -mno-bss -Wa,-l -mpcrel --optimize-code --cut-ranges -v v4.c -o v4
Resulting binary:
Code: Program Statistics:
Program Variable Name: main\v4
Program Variable Size: 1871 Bytes
Absolute Relocs: 0
Natively Emitted Relocs: 0
Relocs Removed by Branch Optimization: 1
Space Saved by Range-Cutting: 4 Bytes
*: I have long stopped using both the IDE and tprbuilder, except for e.g. TICT-Explorer, which I had already converted to TPRs before I hit their limitations and subsequently contributed an improvement to tprbuilder to make TPRs usable for my (simple) use case. Too bad I had failed to understand the complaints about TPRs posted by some other programmers before I hit hard one of the limitations myself...