Jump to content
insomnia

GCC for the TI

Recommended Posts

I'm still having problems of making my own programs run.

.

#include <system.h>
#include <conio.h>
#include <string.h>  // from libc99, for later

#define printf  cprintf
#define getchar cgetc

int main(int argc, char * argv[])
{
  set_text();
  charsetlc();
  textcolor(COLOR_WHITE);
  bgcolor(COLOR_DKBLUE);
  cursor(1);

  printf("HELLO WORLD\n");
  printf("HELLO GCC\n");

  halt();
}

.

I'm compiling with:

.

arcturus ~/ti99/sdd99/demo/ > make test 
/home/ralph/ti99/gcc/bin/tms9900-gcc -I/home/ralph/ti99/gcc/include/libti99 -I/h
ome/ralph/ti99/gcc/libc99/include -c test.c -std=c99 -O2 -s -fno-builtin -o test
.o
/home/ralph/ti99/gcc/bin/tms9900-ld test.o /home/ralph/ti99/gcc/libti99/crt0_ea5
.o --section-start .text=a000 --section-start .data=2080 -M -L/home/ralph/ti99/g
cc/lib -lc -lti99 -o test.elf > ea5.map
/home/ralph/ti99/gcc/bin/elf2ea5 test.elf test.bin

.

But when I run this test.bin (an EA5 file), I only get a yellow screen, and a crash. The testlib program in libti99 works, though.

 

Am I missing a crucial step here? Also, why is this very simple program 4K in size? :-o

Share this post


Link to post
Share on other sites

<snip>

Also, why is this very simple program 4K in size? :-o

 

The simple program has included some pretty sophisticated code.

printf is actually a small interpreter that understands not just quoted text but also all the escape codes and numeric formatting for different data types.

printf is not too big but it typically calls a lot of other routines but here is a printf example:

/* Copyright (C) 1991-2018 Free Software Foundation, Inc.
   This file is part of the GNU C Library.
   The GNU C Library is free software; you can redistribute it and/or
   modify it under the terms of the GNU Lesser General Public
   License as published by the Free Software Foundation; either
   version 2.1 of the License, or (at your option) any later version.
   The GNU C Library is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   Lesser General Public License for more details.
   You should have received a copy of the GNU Lesser General Public
   License along with the GNU C Library; if not, see
   <http://www.gnu.org/licenses/>.  */
#include <libioP.h>
#include <stdarg.h>
#include <stdio.h>
#undef printf
/* Write formatted output to stdout from the format string FORMAT.  */
/* VARARGS1 */
int
__printf (const char *format, ...)
{
  va_list arg;
  int done;
  va_start (arg, format);
  done = vfprintf (stdout, format, arg);
  va_end (arg);
  return done;
}
#undef _IO_printf
ldbl_strong_alias (__printf, printf);
/* This is for libg++.  */
ldbl_strong_alias (__printf, _IO_printf);

I don't know the TI GCC code but to give you an example of what it can become see this monster: :)

 

https://code.woboq.org/userspace/glibc/stdio-common/vfprintf.c.html

Edited by TheBF

Share this post


Link to post
Share on other sites

Am I missing a crucial step here? Also, why is this very simple program 4K in size? :-o

 

When you start getting curious about what is taking up space in your program, that's when you start to learn about the MAP file. ;)

 

I'm assuming that will be "ea5.map" in your build there. It describes the layout of all the memory the linker assigned -- all the program, all the data, and all the variables are laid out in there. Takes a bit to learn to read it, but it's worth it.

 

(Edit: but the biggest reason is likely pulling in conio, particularly cprintf as noted. While I wrote my own conio rather than just porting a standard one (as I had to in order to be PD), it's still doing a fair bit of work and cprintf will include a lot of the other support functions. If you drop conio and use putstring() instead (from vdp.h), it will be a lot smaller. It will be smaller still if you can do without carriage returns and scrolling and just use writestring() (also from vdp.h).

 

That said, if you DO want the features of conio, there's no harm in it, and it won't grow much further. ;)

Edited by Tursi
  • Like 1

Share this post


Link to post
Share on other sites

 

I am curious. How big is the final binary? (I don't have GCC for TI-99)

Pretty small two files one 9k and one 6k ea5 load

 

Sent from my LG-H872 using Tapatalk

  • Like 1

Share this post


Link to post
Share on other sites

Is there a possible compiler bug, here?

 

I have a piece of code, that works across a bunch of other compilers, some of them gcc, some others... It is code that processes incoming character set data, and shrinks the character set on the fly using image processing techniques (essentially re-plotting pixels against sets of tables, split across two different algorithms that are selected dependent on pixel density)...

 

...however on the TI, I am getting corrupted character set data, for some reason, on both TIPI and RS232 targets, so it's not mangled input (from eager translation) causing the issue...

 

Here is the original code, the relevant function is terminal_char_load() ...

/**
 * PLATOTerm64 - A PLATO Terminal for the Commodore 64
 * Based on Steve Peltz's PAD
 *
 * Author: Thomas Cherryhomes <thom.cherryhomes at gmail dot com>
 *
 * terminal.c - Terminal state functions
 */

/* Some functions are intentionally stubbed. */
#pragma warn(unused-param, off)

#include <stdbool.h>
#include <string.h>
#include "terminal.h"
#include "screen.h"
#include "protocol.h"

/**
 * ASCII Features to return in Features
 */
#define ASC_ZFGT        0x01
#define ASC_ZPCKEYS     0x02
#define ASC_ZKERMIT     0x04
#define ASC_ZWINDOW     0x08

/**
 * protocol.c externals
 */
extern CharMem CurMem;
extern padBool TTY;
extern padBool ModeBold;
extern padBool Rotate;
extern padBool Reverse;
extern DispMode CurMode;
extern padBool FlowControl;

/**
 * screen.c externals
 */
extern unsigned char CharWide;
extern unsigned char CharHigh;
extern padPt TTYLoc;

extern unsigned char already_started;

#define FONTPTR(a) (((a << 1) + a) << 1)

// Temporary PLATO character data, 8x16 matrix
static unsigned char char_data[]={0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
                                  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00};

static unsigned char BTAB[]={0x80,0x40,0x20,0x10,0x08,0x04,0x02,0x01}; // flip one bit on (OR)
static unsigned char BTAB_5[]={0x08,0x10,0x10,0x20,0x20,0x40,0x80,0x80}; // flip one bit on for the 5x6 matrix (OR)

static unsigned char TAB_0_5[]={0x05,0x05,0x05,0x04,0x04,0x04,0x03,0x03,0x02,0x02,0x01,0x01,0x01,0x00,0x00,0x00};
static unsigned char TAB_0_5i[]={0x00,0x00,0x00,0x01,0x01,0x01,0x02,0x02,0x03,0x03,0x04,0x04,0x04,0x05,0x05,0x05};

static unsigned char TAB_0_4[]={0x00,0x00,0x01,0x02,0x02,0x03,0x03,0x04}; // return 0..4 given index 0 to 7

static unsigned char PIX_THRESH[]={0x03,0x02,0x03,0x03,0x02, // Pixel threshold table.
                                   0x03,0x02,0x03,0x03,0x02,
                                   0x02,0x01,0x02,0x02,0x01,
                                   0x02,0x01,0x02,0x02,0x01,
                                   0x03,0x02,0x03,0x03,0x02,
                                   0x03,0x02,0x03,0x03,0x02};

static unsigned char PIX_WEIGHTS[]={0x00,0x00,0x00,0x00,0x00, // Pixel weights
                                    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};

static unsigned char TAB_0_25[]={0,5,10,15,20,25}; // Given index 0 of 5, return multiple of 5.

static unsigned char pix_cnt;     // total # of pixels
static unsigned char curr_word;   // current word
static unsigned char u,v;       // loop counters

extern unsigned char fontm23[768];

/**
 * terminal_init()
 * Initialize terminal state
 */
void terminal_init(void)
{
  terminal_set_tty();
}

/**
 * terminal_initial_position()
 * Set terminal initial position after splash screen.
 */
void terminal_initial_position(void)
{
  TTYLoc.x=0;
  TTYLoc.y=100; // Right under splashscreen.
}

/**
 * terminal_set_tty(void) - Switch to TTY mode
 */
void terminal_set_tty(void)
{
  if (already_started)
    screen_clear();
  TTY=true;
  ModeBold=padF;
  Rotate=padF;
  Reverse=padF;
  CurMem=M0;
  /* CurMode=ModeRewrite; */
  CurMode=ModeWrite;    /* For speed reasons. */
  CharWide=8;
  CharHigh=16;
  TTYLoc.x = 0;        // leftmost coordinate on screen
  TTYLoc.y = 495;      // Top of screen - one character height
}

/**
 * terminal_set_plato(void) - Switch to PLATO mode
 */
void terminal_set_plato(void)
{
  TTY=false;
  screen_clear();
}

/**
 * terminal_get_features(void) - Inquire about terminal ASCII features
 */
unsigned char terminal_get_features(void)
{
  return ASC_ZFGT; /* This terminal can do Fine Grained Touch (FGT) */
}

/**
 * terminal_get_type(void) - Return the appropriate terminal type
 */
unsigned char terminal_get_type(void)
{
  return 12; /* ASCII terminal type */
}

/**
 * terminal_get_subtype(void) - Return the appropriate terminal subtype
 */
unsigned char terminal_get_subtype(void)
{
  return 1; /* ASCII terminal subtype IST-III */
}

/**
 * terminal_get_load_file(void) - Return the appropriate terminal loadfile (should just be 0)
 */
unsigned char terminal_get_load_file(void)
{
  return 0; /* This terminal does not load its resident from the PLATO system. */
}

/**
 * terminal_get_configuration(void) - Return the terminal configuration
 */
unsigned char terminal_get_configuration(void)
{
  return 0x40; /* Touch panel is present. */
}

/**
 * terminal_get_char_address(void) - Return the base address of the character set.
 */
unsigned short terminal_get_char_address(void)
{
  return 0x3000; /* What the? Shouldn't this be 0x3800? */
}

/**
 * terminal_mem_read - Read a byte of program memory.
 * not needed for our terminal, but must
 * be decoded.
 */
padByte terminal_mem_read(padWord addr)
{
  return (0xFF);
}

/**
 * terminal_mem_load - Write a byte to non-character memory.
 * not needed for our terminal, but must be decoded.
 */
void terminal_mem_load(padWord addr, padWord value)
{
  /* Not Implemented */
}

/**
 * Mode5, 6, and 7 are basically stubbed.
 */
void terminal_mode_5(padWord value)
{
}

void terminal_mode_6(padWord value)
{
}

void terminal_mode_7(padWord value)
{
}

/**
 * terminal_ext_allow - External Input allowed. Not implemented.
 */
void terminal_ext_allow(padBool allow)
{
  /* Not Implemented */
}

/**
 * terminal_set_ext_in - Set which device to get input from.
 * Not implemented
 */
void terminal_set_ext_in(padWord device)
{
}

/**
 * terminal_set_ext_out - Set which device to send external data to.
 * Not implemented
 */
void terminal_set_ext_out(padWord device)
{
}

/**
 * terminal_ext_in - get an external input from selected device.
 * Not implemented.
 */
padByte terminal_ext_in(void)
{
  return 0;
}

/**
 * terminal_ext_out - Send an external output to selected device
 * Not implemented.
 */
void terminal_ext_out(padByte value)
{
}

void memset(char* buf, char val, short len)
{
  for (int i=0;i<len;++i)
    buf[i]=val;
}

/**
 * terminal_char_load - Store a character into the user definable
 * character set.
 */
void terminal_char_load(padWord charnum, charData theChar)
{
  // Clear char data.
  memset(char_data,0,sizeof(char_data));
  memset(PIX_WEIGHTS,0,sizeof(PIX_WEIGHTS));
  memset(&fontm23[FONTPTR(charnum)],0,6);
  pix_cnt=0;

  // Transpose character data.
  for (curr_word=0;curr_word<8;curr_word++)
    {
      for (u=16; u-->0; )
        {
          if (theChar[curr_word] & 1<<u)
            {
              pix_cnt++;
              PIX_WEIGHTS[TAB_0_25[TAB_0_5[u]]+TAB_0_4[curr_word]]++;
              char_data[u^0x0F&0x0F]|=BTAB[curr_word];
            }
        }
    }

  // Determine algorithm to use for number of pixels.
  // Algorithm A is used when roughly half of the # of pixels are set.
  // Algorithm B is used either when the image is densely or sparsely populated (based on pix_cnt).
  if ((54 <= pix_cnt) && (pix_cnt < 85))
    {
      // Algorithm A - approx Half of pixels are set
      for (u=6; u-->0; )
        {
          for (v=5; v-->0; )
            {
              if (PIX_WEIGHTS[TAB_0_25[u]+v] >= PIX_THRESH[TAB_0_25[u]+v])
                fontm23[FONTPTR(charnum)+u]|=BTAB[v];
            }
        }
    }
  else if ((pix_cnt < 54) || (pix_cnt >= 85))
    {
      // Algorithm B - Sparsely or heavily populated bitmaps
      for (u=16; u-->0; )
        {
          if (pix_cnt >= 85)
            char_data[u]^=0xFF;

          for (v=8; v-->0; )
            {
              if (char_data[u] & (1<<v))
                {
                  fontm23[FONTPTR(charnum)+TAB_0_5i[u]]|=BTAB_5[v];
                }
            }
        }
      if (pix_cnt >= 85)
        {
          for (u=6; u-->0; )
            {
              fontm23[FONTPTR(charnum)+u]^=0xFF;
              fontm23[FONTPTR(charnum)+u]&=0xF8;
            }
        }
    }
}

Which gets translated into the following 9900 assembler:

        pseg
        even

        def     terminal_initial_position
terminal_initial_position
        li   r1, TTYLoc
        clr  *r1+
        li   r2, >64
        mov  r2, *r1
        b    *r11
        .size   terminal_initial_position, .-terminal_initial_position
        even

        def     terminal_get_features
terminal_get_features
        li   r1, >100
        b    *r11
        .size   terminal_get_features, .-terminal_get_features
        even

        def     terminal_get_type
terminal_get_type
        li   r1, >C00
        b    *r11
        .size   terminal_get_type, .-terminal_get_type
        even

        def     terminal_get_subtype
terminal_get_subtype
        li   r1, >100
        b    *r11
        .size   terminal_get_subtype, .-terminal_get_subtype
        even

        def     terminal_get_load_file
terminal_get_load_file
        clr  r1
        b    *r11
        .size   terminal_get_load_file, .-terminal_get_load_file
        even

        def     terminal_get_configuration
terminal_get_configuration
        li   r1, >4000
        b    *r11
        .size   terminal_get_configuration, .-terminal_get_configuration
        even

        def     terminal_get_char_address
terminal_get_char_address
        li   r1, >3000
        b    *r11
        .size   terminal_get_char_address, .-terminal_get_char_address
        even

        def     terminal_mem_read
terminal_mem_read
        seto r1
        b    *r11
        .size   terminal_mem_read, .-terminal_mem_read
        even

        def     terminal_mem_load
terminal_mem_load
        b    *r11
        .size   terminal_mem_load, .-terminal_mem_load
        even

        def     terminal_mode_5
terminal_mode_5
        b    *r11
        .size   terminal_mode_5, .-terminal_mode_5
        even

        def     terminal_mode_6
terminal_mode_6
        b    *r11
        .size   terminal_mode_6, .-terminal_mode_6
        even

        def     terminal_mode_7
terminal_mode_7
        b    *r11
        .size   terminal_mode_7, .-terminal_mode_7
        even

        def     terminal_ext_allow
terminal_ext_allow
        b    *r11
        .size   terminal_ext_allow, .-terminal_ext_allow
        even

        def     terminal_set_ext_in
terminal_set_ext_in
        b    *r11
        .size   terminal_set_ext_in, .-terminal_set_ext_in
        even

        def     terminal_set_ext_out
terminal_set_ext_out
        b    *r11
        .size   terminal_set_ext_out, .-terminal_set_ext_out
        even

        def     terminal_ext_in
terminal_ext_in
        clr  r1
        b    *r11
        .size   terminal_ext_in, .-terminal_ext_in
        even

        def     terminal_ext_out
terminal_ext_out
        b    *r11
        .size   terminal_ext_out, .-terminal_ext_out
        even

        def     memset
memset
        mov  r3, r3
        jlt  L38
        jeq  L38
        clr  r4
L37
        mov  r1, r5
        a    r4, r5
        movb r2, *r5
        inc  r4
        c    r4, r3
        jne  L37
L38
        b    *r11
        .size   memset, .-memset
        even

        def     terminal_char_load
terminal_char_load
        dect r10
        mov  r9, *r10
        li   r3, char_data
L42
        clr  r4
        movb r4, *r3+
        ci   r3, char_data+16
        jne  L42
        li   r3, PIX_WEIGHTS
L43
        clr  r5
        movb r5, *r3+
        ci   r3, PIX_WEIGHTS+30
        jne  L43
        mov  r1, r12
        a    r1, r12
        a    r1, r12
        a    r12, r12
        mov  r12, r4
        ai   r4, fontm23
        clr  r1
L44
        mov  r4, r3
        a    r1, r3
        clr  r7
        movb r7, *r3
        inc  r1
        ci   r1, >6
        jne  L44
        clr  r6
        movb r7, r5
        seto r4
        li   r8, >F00
L48
        li   r3, >1000
L72
        ai   r3, >FF00
        cb   r3, r4
        jeq  L75
L46
        movb r3, r0
        srl  r0, 8
        mov  *r2, r1
        abs  r0
        jeq  $+4
        sra  r1, 0
        andi r1, >1
        abs  r1
        jeq  L72
        ai   r5, >100
        mov  r0, r7
        movb @TAB_0_5(r7), r1
        srl  r1, 8
        movb @TAB_0_25(r1), r1
        srl  r1, 8
        movb @TAB_0_4(r6), r7
        srl  r7, 8
        a    r7, r1
        li   r9, >100
        ab   r9, @PIX_WEIGHTS(r1)
        movb r3, r1
        xor  r8, r1
        srl  r1, 8
        socb @BTAB(r6), @char_data(r1)
        movb r5, r7
        ai   r3, >FF00
        cb   r3, r4
        jne  L46
L75
        inc  r6
        inct r2
        ci   r6, >8
        jeq  L47
        movb r7, r5
        jmp  L48
L47
        movb r3, @u
        movb r7, @pix_cnt
        li   r1, >800
        movb r1, @curr_word
        movb r5, r2
        ai   r2, >CA00
        ci   r2, >1EFF
        jh  L49
        li   r3, >400
        li   r2, >500
        seto r5
        li   r6, >FE00
        jmp  L53
L76
        movb r3, r4
        ai   r4, >FF00
        cb   r4, r6
        jeq  L52
        movb r3, r2
        movb r4, r3
L53
        li   r1, >500
        srl  r2, 8
        mov  r2, r4
        ai   r4, TAB_0_25
        a    r12, r2
        ai   r2, fontm23
L73
        ai   r1, >FF00
        cb   r1, r5
        jeq  L76
        movb r1, r8
        srl  r8, 8
        movb *r4, r7
        srl  r7, 8
        a    r8, r7
        cb   @PIX_WEIGHTS(r7), @PIX_THRESH(r7)
        jl  L73
        socb @BTAB(r8), *r2
        jmp  L73
L52
        movb r1, @u
        movb r1, @v
L62
        mov  *r10+, r9
        b    *r11
        b    @L77
L49
        li   r1, >F00
        li   r8, >5400
        seto r6
        jmp  L58
L79
        ai   r1, >FF00
        cb   r1, r2
        jeq  L78
L58
        cb   r5, r8
        jle  L54
        movb r1, r2
        srl  r2, 8
        inv  @char_data(r2)
L54
        li   r2, >800
        movb r1, r3
        srl  r3, 8
        mov  r3, r7
        ai   r7, char_data
        ai   r3, TAB_0_5i
L74
        ai   r2, >FF00
        cb   r2, r6
        jeq  L79
        movb r2, r0
        srl  r0, 8
        movb *r7, r4
        srl  r4, 8
        abs  r0
        jeq  $+4
        sra  r4, 0
        andi r4, >1
        abs  r4
        jeq  L74
        movb *r3, r4
        srl  r4, 8
        a    r12, r4
        mov  r0, r9
        socb @BTAB_5(r9), @fontm23(r4)
        jmp  L74
L78
        movb r1, @u
        movb r1, @v
        ci   r5, >54FF
        jle  L62
        li   r2, >500
        movb r2, r1
        seto r4
        jmp  L61
L80
        movb r1, r2
L61
        srl  r1, 8
        a    r12, r1
        ai   r1, fontm23
        movb *r1, r3
        inv  r3
        andi r3, >F800
        movb r3, *r1
        movb r2, r1
        ai   r1, >FF00
        cb   r1, r4
        jne  L80
        movb r1, @u
        mov  *r10+, r9
        b    *r11
L77
        .size   terminal_char_load, .-terminal_char_load
        even

        def     terminal_set_plato
terminal_set_plato
        clr  @TTY
        b    @screen_clear
        .size   terminal_set_plato, .-terminal_set_plato
        even

        def     terminal_set_tty
terminal_set_tty
        dect r10
        mov  r11, *r10
        movb @already_started, @already_started
        jeq  L84
        bl   @screen_clear
L84
        li   r1, >1
        mov  r1, @TTY
        clr  @ModeBold
        clr  @Rotate
        clr  @Reverse
        clr  @CurMem
        clr  @CurMode
        li   r1, >810
        movb r1, @CharWide
        swpb r1
        movb r1, @CharHigh
        clr  @TTYLoc
        li   r1, >1EF
        mov  r1, @TTYLoc+2
        mov  *r10+, r11
        b    *r11
        .size   terminal_set_tty, .-terminal_set_tty
        even

        def     terminal_init
terminal_init
        b    @terminal_set_tty
        .size   terminal_init, .-terminal_init
        cseg

        even
char_data
        bss 16

        even
PIX_WEIGHTS
        bss 30
        pseg
        .type   TAB_0_5, @object
        .size   TAB_0_5, 16
TAB_0_5
        byte    5
        byte    5
        byte    5
        byte    4
        byte    4
        byte    4
        byte    3
        byte    3
        byte    2
        byte    2
        byte    1
        byte    1
        byte    1
        byte    0
        byte    0
        byte    0
        .type   TAB_0_25, @object
        .size   TAB_0_25, 6
TAB_0_25
        byte    0
        byte    5
        byte    10
        byte    15
        byte    20
        byte    25
        .type   TAB_0_4, @object
        .size   TAB_0_4, 8
TAB_0_4
        byte    0
        byte    0
        byte    1
        byte    2
        byte    2
        byte    3
        byte    3
        byte    4
        .type   BTAB, @object
        .size   BTAB, 8
BTAB
        byte    -128
        byte    64
        byte    32
        byte    16
        byte    8
        byte    4
        byte    2
        byte    1
        .type   PIX_THRESH, @object
        .size   PIX_THRESH, 30
PIX_THRESH
        byte    3
        byte    2
        byte    3
        byte    3
        byte    2
        byte    3
        byte    2
        byte    3
        byte    3
        byte    2
        byte    2
        byte    1
        byte    2
        byte    2
        byte    1
        byte    2
        byte    1
        byte    2
        byte    2
        byte    1
        byte    3
        byte    2
        byte    3
        byte    3
        byte    2
        byte    3
        byte    2
        byte    3
        byte    3
        byte    2
        .type   TAB_0_5i, @object
        .size   TAB_0_5i, 16
TAB_0_5i
        byte    0
        byte    0
        byte    0
        byte    1
        byte    1
        byte    1
        byte    2
        byte    2
        byte    3
        byte    3
        byte    4
        byte    4
        byte    4
        byte    5
        byte    5
        byte    5
        .type   BTAB_5, @object
        .size   BTAB_5, 8
BTAB_5
        byte    8
        byte    16
        byte    16
        byte    32
        byte    32
        byte    64
        byte    -128
        byte    -128
        cseg

        even
pix_cnt
        bss 1

        even
curr_word
        bss 1

        even
u
        bss 1

        even
v
        bss 1

        ref     TTYLoc

        ref     CharHigh

        ref     CharWide

        ref     CurMode

        ref     CurMem

        ref     Reverse

        ref     Rotate

        ref     ModeBold

        ref     TTY

        ref     screen_clear

        ref     already_started

        ref     fontm23


...anything hinky?

 

-Thom

Share this post


Link to post
Share on other sites

I'm definitely seeing bugs when doing bitwise manipulations with variables that are unsigned chars, I suspect there is some data type promotion to int happening, as changing my padRGB elements for red green and blue from unsigned char to int suddenly made my color mapping code work properly...

 

...character set loading is still messed up...

/**
 * terminal_char_load - Store a character into the user definable
 * character set.
 */
void terminal_char_load(padWord charnum, charData theChar)
{
  // Clear char data.
  memset(char_data,0,sizeof(char_data));
  memset(PIX_WEIGHTS,0,sizeof(PIX_WEIGHTS));
  memset(&fontm23[FONTPTR(charnum)],0,6);
  pix_cnt=0;
 
  // Transpose character data.
  for (curr_word=0;curr_word<8;curr_word++)
    {
      for (u=16; u-->0; )
        {
          if (theChar[curr_word] & 1<<u)
            {
              pix_cnt++;
              PIX_WEIGHTS[TAB_0_25[TAB_0_5[u]]+TAB_0_4[curr_word]]++;
              char_data[u^0x0F&0x0F]|=BTAB[curr_word];
            }
        }
    }
 
  // Determine algorithm to use for number of pixels.
  // Algorithm A is used when roughly half of the # of pixels are set.
  // Algorithm B is used either when the image is densely or sparsely populated (based on pix_cnt).
  if ((54 <= pix_cnt) && (pix_cnt < 85))
    {
      // Algorithm A - approx Half of pixels are set
      for (u=6; u-->0; )
        {
          for (v=5; v-->0; )
            {
              if (PIX_WEIGHTS[TAB_0_25[u]+v] >= PIX_THRESH[TAB_0_25[u]+v])
                fontm23[FONTPTR(charnum)+u]|=BTAB[v];
            }
        }
    }
  else if ((pix_cnt < 54) || (pix_cnt >= 85))
    {
      // Algorithm B - Sparsely or heavily populated bitmaps
      for (u=16; u-->0; )
        {
          if (pix_cnt >= 85)
            char_data[u]^=0xFF;
 
          for (v=8; v-->0; )
            {
              if (char_data[u] & (1<<v))
                {
                  fontm23[FONTPTR(charnum)+TAB_0_5i[u]]|=BTAB_5[v];
                }
            }
        }
      if (pix_cnt >= 85)
        {
          for (u=6; u-->0; )
            {
              fontm23[FONTPTR(charnum)+u]^=0xFF;
              fontm23[FONTPTR(charnum)+u]&=0xF0;
            }
        }
    }
}

If I comment out the calls which XOR char_data[], then character set loading MOSTLY works, (but sparsely populated bitmaps do not get correctly transformed, because the algorithm removes too many pixels).

 

I'm really frustrated here, because i'm wondering if I need to give myself a crash course in compiler design to fix this...

 

-Thom

Share this post


Link to post
Share on other sites

(Edit: This issue has since been fixed in patch 1.19)

 

So, I'm doing fun stuff with bank switching cartridge development...

I'm using libti99 ( and my own code reproduces the same issue )

the strlen function, and many others, test for 0 by using the movb *Rx,*Rx idiom... This creates the crazy situation where I cannot naturally get the strlen for a string in ROM. As the test for 0 writes to the rom address which triggers the bankswitching behavior, and then everything goes nuts.

I set a breakpoint in classic99: >(6010-7FFF) and sure enough one of these movb to rom happens, my bank gets switched, and I'm in off the rails.

I have a couple work-arounds : in this instance, I can copy the string from ROM to RAM first.. or I can go rewrite my copy of libti99's strlen in asm to avoid the compiler's choices.

Basically, use of the movb idiom for zero-tests is dangerous given conventional hardware.

Anyone know of an elegant workaround? compiler flag?

I'm two patches behind (so I'll proceed to patch now), but the change-log for 1.17 doesn't indicate anything regarding this, and I didn't see an entry for 1.18.

[email protected]

  • Like 4

Share this post


Link to post
Share on other sites

Still happens in patch 1.18. No reason to have thought it wouldn't.

 

Interestingly this only occurs 3 places in all the libti99 code: twice in strlen, and once in bmputs

 

It happens a bunch in the testlib for libti99... might not be that expensive to abandon the idiom... I assume this is used because it's only a single word instruction.

 

[email protected]

Share this post


Link to post
Share on other sites

The cleanest work-around I've found is to declare:

 

 

volatile char zero = 0;

 

 

and then test for that instead of literal zero.

 

[email protected]

 

That's cool. Does it force the compiler to emit a "compare immediate" instruction instead of MOV *Rx,*Rx ?

Share this post


Link to post
Share on other sites

ARGGG!!! The 'quote' button on Atariage doesn't work, and posting doesn't work 80% of the time...

 

----

 

Comparing against a variable causes a 'C', instead of the movb... It can't be a CI because the value isn't known at the time of assembly... That's why I started with volatile, to make sure the optimizer doesn't make assumptions about the value.

 

[email protected]

Share this post


Link to post
Share on other sites

Comparing against a variable causes a 'C', instead of the movb... It can't be a CI because the value isn't known at the time of assembly... That's why I started with volatile, to make sure the optimizer doesn't make assumptions about the value.

 

[email protected]

 

 

Ah yes. That makes sense.

 

So would it also work if you used a #define myzero = 0; ( This might emit a CI instruction? Maybe?)

 

I don't use C so I am just curious about how you have to think when coding in C.

Share this post


Link to post
Share on other sites

No, a #define won't work as it is a text substitution before compilation. It would leave a literal 0 in the code whenever used, and the optimizer would not be influenced in the direction I want.

 

all the #___ stuff is actually part of a pre-processor language 'The C Pre Processor' (CPP) not to be confused with c++.. Like running a bunch of awk / sed scripts on your code before compiling it.

Share this post


Link to post
Share on other sites

I saw there was a lot of impact to the problem of using a "movb" instruction to test for zero, so I rushed out a patch to fix this. Jedimatt42 has already written a good description of the problem, so I won't repeat him here. I also realized that there was no changelog entry for 1.18, so I fixed that too. I also changed the installer code to remove the need for a "tree" command, and we now automatically build libgcc.

 

Tschack909, I tried to help you out, but I don't have enough information to help you out. I looked at the assembly, and I couldn't find anything that looked like a problem.

 

So here's the changes for the latest patch:

Use the "ci" instruction when testing for 16-bit zero values

Use the combination of "jeq" and "cb" to test for 8-bit zero values

 

The "ci" test is pretty self explanatory so there's not really much to say about that.

 

However, I wanted to talk a little more about the 8-bit test since I thought it was kinda neat.

 

Assume we have a value in r1 we want to test for zero. This is the code that the compiler will now emit:

>1300       jeq 0
>9801 fffe  cb @$-1, r1

The "jeq" instruction is effectively a no-op. Since it's more likely for some random instruction to result in a non-zero value, we save two clocks by not taking the jump. In either case, we will proceed to the "cb" instruction.

 

The "cb" instruction is using the zero of the "jeq" instruction as one of its arguments, and we test against the value in r1 as expected.

 

So by using this combo, we have a bytewise test for zero that's only two bytes and 8 clocks longer than a "ci" instruction. Neat!

 

At any rate, thanks for finding a bug, and let me know if you find any problems.

  • Like 7

Share this post


Link to post
Share on other sites

I'm not sure I understand the solution chosen, probably because I misunderstand the context.

 

When testing an int variable for zero, why not use something like "mov *Rx,r0" - assuming that R0 is a scratch register? Or "mov *Rx, @0" - assuming location 0 is ROM? For a char variable "movb *Rx,r0" / "movb *Rx,@0" should work, no?

 

In the alternative, I don't quite see the need for generating the dummy jump on each occasion. Would it not make more sense to define a zero word in the start up code (e.g. named "czero") and generate a "cb @czero,R1" instead?

 

As said, I probably misunderstand the context.

Share this post


Link to post
Share on other sites

I'm not sure I understand the solution chosen, probably because I misunderstand the context.

 

When testing an int variable for zero, why not use something like "mov *Rx,r0" - assuming that R0 is a scratch register? Or "mov *Rx, @0" - assuming location 0 is ROM? For a char variable "movb *Rx,r0" / "movb *Rx,@0" should work, no?

 

In the alternative, I don't quite see the need for generating the dummy jump on each occasion. Would it not make more sense to define a zero word in the start up code (e.g. named "czero") and generate a "cb @czero,R1" instead?

 

As said, I probably misunderstand the context.

 

Your turn of phrase sounds like you might use Forth or are familiar with it. ("define a zero word")

 

I asked a similar question and the reply is #492

 

"No, a #define won't work as it is a text substitution before compilation. It would leave a literal 0 in the code whenever used, and the optimizer would not be influenced in the direction I want."

 

I read into that, that C does not make it easy to create a constant with a value of zero that GCC will use in an optimal manner in this case.

 

I think genius of the solution is that it creates a byte with the value "zero" for free that is one byte of the JMP instruction.

The NOOP instruction machine code is >1000. With that you can see where the "zero" is hiding.

 

You could put a DATA statement before the compare, with zero in it but then you need to "JMP" over the DATA statement to get to the CB. :-)

 

So "borrowing" the zero byte hidden inside the JMP instruction, gives you what you need for free.

 

The experts can weigh in on the accuracy of my explanation.

Edited by TheBF

Share this post


Link to post
Share on other sites

He wasn't talking about using a #define so much as just ensuring the startup code includes a zero value that can be used. I actually had a similar patch back in the very early days where I used two bytes of scratchpad just to have a zero to compare against - but this required the runtime to set it up (and nothing else to corrupt it). It wouldn't hurt to have a defined zero value in the final generated code that could be used for compares, as clever as the jeq solution is. ;)

 

   DEF START

_CZERO
  DATA >0000

* program enters here
START
  LWPI >8300
* do a bunch of work here
* compare byte at R0 to zero
  CB *R0,@_CZERO
Like that. :)
  • Like 1

Share this post


Link to post
Share on other sites

My apologies if this has been covered here before, but how can I implement a single function in assembly and link it with the rest? Not inlining (God forbid!), but a self-contained function.

 

What is the calling convention? I looked at some generated functions, but only see a lot of registers moving around ...

Share this post


Link to post
Share on other sites

My apologies if this has been covered here before, but how can I implement a single function in assembly and link it with the rest? Not inlining (God forbid!), but a self-contained function.

 

What is the calling convention? I looked at some generated functions, but only see a lot of registers moving around ...

 

There is an enlightening thread somewhere on bank switching that details the calling convention the most I've seen..... I've probably been learning assembly mostly from looking at the generated code when my C goes wrong.. :)

 

R10 is used as the stack pointer...

BL is used to call a function. and RT to return (b *R11)

when a function is called it expects arguments on registers R1, R2, R3, etc... until they don't fit...

 

An .asm file that defines an address to code can be called as a function, but you'll have to declare a prototype in C for that symbol...

 

so for a function in a .asm referenced as 'myasm' that takes zero arguments, and returns none:

 

 

extern void myasm();

 

 

If you want to be able to pass arguments in a few registers, expect them in order starting with R1... not sure how many it'll use... upto R1 through R9 I think, after that things get put on the stack by the caller.

 

 

extern void myasm(int argR1, int argR2, int argR3);

 

 

It seems that since the instruction set prefers the most significant byte for char type args, that passing an 8 bit type will be in the high side of a register...

 

 

extern void myasm(char argR1); // will put the 8 bit value in R1 << 8 (if that makes sense) 

 

 

return value is returned in R1. Same rules for 8 bit values or 16 bit values..

 

 

 

extern int myasm();

 

 

I believe it is the responsibility of the function to 'pop' the stack for any 'pushing' it has done... So R10 should be restored if moved... the stack grows down... so pushing is subtracting 2 from the R10, and storing the value in that location. pop is read, and increment.

 

Things get more complicated if you have a large number of arguments and the system resorts to the stack to pass them.

 

[email protected]

Share this post


Link to post
Share on other sites

My apologies if this has been covered here before, but how can I implement a single function in assembly and link it with the rest? Not inlining (God forbid!), but a self-contained function.

 

What is the calling convention? I looked at some generated functions, but only see a lot of registers moving around ...

 

I'm sure it's possible to link in a "naked" assembly function, but I offer an alternative - write a wrapper method in C that calls your assembly function. The function would contain inline assembly to conform to your assembly function's calling convention.

 

Or inline your assembly function inside of an "empty" wrapper C call. That way you don't have to worry about calling convention.

Edited by chue

Share this post


Link to post
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Loading...

  • Recently Browsing   0 members

    No registered users viewing this page.

×
×
  • Create New...