Jump to content

GCC for the TI


Recommended Posts

OK, the multiply bug was more involved than the earlier ones, but here you go:


In gcc-4.4.0/gcc/config/tms9900/tms9900.md. remove lines 1455 through 1484 (the "mulhisi3", and "*multhisi" patterns).


Replace them with this:


(define_insn "mulhisi3"
 [(set (match_operand:SI 0 "register_operand" "=r,r")
(mult:SI (match_operand:HI 1 "register_operand" "r,r")
	 (match_operand:HI 2 "general_operand" "rR>,Q")))]
   /* When both input operands are registers, we may need to swap them. */
   if(REG_P(operands[1]) && REG_P(operands[2]))
     /* Check for forms like: r0 = r1 * r0 */ 
     if(REGNO(operands[0]) == REGNO(operands[2]))
       /* Swap operands, otherwise we will emit code like:
            mov r1, r0
            mpy r0, r0

          instead of:
            mpy r1, r0
       rtx temp = operands[1];
       operands[1] = operands[2];
       operands[2] = temp;

   if(REGNO(operands[0]) != REGNO(operands[1]))
     output_asm_insn("mov  %1, %0", operands);
   output_asm_insn("mpy  %2, %0", operands);
 [(set_attr "length" "2,3")])


The original code was an attempt to force the register allocator to use registers which were most convenient for the MPY instruction. Obviously,that didn't work out so well. This new code is less aggressive, accepting any register choice GCC may make. It also works in all optimization levels. An optional MOV instruction is now used to prepare for the multiply if the register allocator is not kind.


Moving on to questions...


* What registers are used by the compiler generated program? Are all 16 registers used or are some of them "free" for own use?


GCC will attempt to make maximum use of all 16 registers, so there's no guarantee that there any lying around unused.


If you have an assembly routine you would like to interface with C code, the information needed for that should be shown in earlier posts. If you would like more detailed information (calling convention, register usage and allocation order, etc) I'd be happy to let you know. I have a document I've been neglecting which should include all this stuff.


* What (scratchpad) memory is used by the compiler generated program?


GCC (or any compiler) is basically an engine to convert source code into assembly. That means you have complete control over what memory is used, and for what purpose. So unlike Basic, Java or Forth (I presume), there is no other code working behind the scenes you need to be aware of. All of the machine's resources are available to you, and any other limitations are of your own making.


In the example code I've posted earlier, the registers are located at >8300 by the ctr0 code. Except for what's used by the registers, all of scratchpad memory is available. If you wanted to, you could put the registers elsewhere in scratchpad or 8-bit memory with no impact on the C code.


You can store data anywhere in the system you like (like Lucien did in his bricks code). Using the linker, you can build your code to run from anywhere in memory. You could even put (small bits of) code into scratchpad and run from there for that extra performace boost (as I believe was done in Parsec).


Remember, C was originally designed to write device drivers and operating systems, so the sky's the limit here.

Link to comment
Share on other sites

Thanks for the update. :)


Good to know that there is no memory used, except for the registers.

Reason I'm asking is because I'm toying with the idea of interfacing my spectra2 assembly library with C code.


No rush though, first need to get the next release out and wanna get myself confident with C :)

Link to comment
Share on other sites

  • 2 weeks later...

OK, it's patch time again.


This patch includes the fixes for all bugs mentioned here since the last patch release in addition to a few I found on my own. The same patch and build directions used before are used for this one too.


Here's the changes in this release:


Fixed a bug with byte initializers, it was handling negative values wrongly

Fixed multiply bug, it was using the wrong registers

Changed frame pointer from R8 to R9. Frame was being lost

Byte reads from memory were assumed to be copied into register's LSB.

Fixed a problem with AND improperly modifying input values.

Fixed a bug where R11 was not saved if used as a data register.

Modified output to use hex values for all constants and addresses


I've also packaged up an ELF to EA5 converter and an example program made to run as an EA5 image. The program does the same useless flashing text thing that the cart example did. This was done to make the differences easier to spot. The changes made to the EA5 crt0 are a bit safer than the one used in the cart version (this one better handles zero size sections). I'll probably release a new version of the cart tool and example sometime soon which incorporates these changes.


The next thing on my list is to update all the documentation. Everything I've posted so far is still valid, but there are probably holes where some subjects need more description.


I also need to put together a library for the missing 32- but functions (multiply, divide, modulus, shift). These functions are already written and tested for the most part, so releasing them should be quick and easy.


Finally, I need to make my V9T9 disk management tool ready for public consumption. It currently works, and the disk images it creates were used to test the EA5 converter, but it's super hacky at the moment. Once I spruce it up a bit and turn it into a useful tool, I can send it out the door.


As always, the gory details are on my blog for those who are interested.




  • Like 1
Link to comment
Share on other sites

  • 2 weeks later...

I found 2 new bugs. :ponder:


void vdp_copy_from_sys(int index, char* src, int size) {
char* end = src + size;
VDP_ADDRESS_REG = (char)(index >>  | VDP_WRITE_FLAG;
while(src < end) VDP_WRITE_DATA_REG = *src++;

void main() {
int i=0;
char c;
while(1) {
	if(key_scan(&c)) {
		i++; if(i>=32*24)i=0;


Code generated for the call of "vdp_copy_from_sys" ("i" is in R9):


movb r9, @>8C02   INCORRECT
mov  r9, r2
ori r2, >4000
movb r2, @>8C02
movb r1, @>8C00
inc  r9
ci   r9, >2FF


If I comment the line "c=random_byte(26)+65;" ("i" is in R2):


mov  r2, r1
swpb r1           CORRECT
movb r1, @>8C02
mov  r2, r1
ori r1, >4000
movb r1, @>8C02
movb r3, @>8C00
inc  r2
ci   r2, >2FF


And the other bug, with the assembler.


gplws	equ	0x83E0

def	kscan
kscan	lwpi	gplws
bl	@>E
lwpi	>8300
b	*r11


If I put "gplws equ 0x83E0" after the "kscan" routine, there is no error message, but "kscan" does not work anymore.


Here is the complete source: UTILS.zip

Link to comment
Share on other sites

I found another bug with the assembler. I think this one should be easier to correct. :) The STST instruction can't be assembled. The error message is "Error: missing comma separator".


I could not find the string "STST" or "LWPI" in the "complete_files" folder, where are the assembler instructions in the source?

Link to comment
Share on other sites

I could not find the string "STST" or "LWPI" in the "complete_files" folder, where are the assembler instructions in the source?


Ok, I searched in the GCC source, but it's in the BINUTILS source.


I see that in the "parse_table level_4" structure there is "{parse_type_8a, "stst"}".

So, here is "parse_type_8a":


static void parse_type_8a(struct buffer *buffer, disassemble_info *info, char *text)
 //                  |15|14|13|12|11|10| 9| 8| 7| 6| 5| 4| 3| 2| 1| 0|
 // register, count  |Opcode                          | 0|Register   |

 info->fprintf_func (info->stream, "%s r%d", 
                     text, buffer->opcode & 0xF);


It seems correct, why is it waiting for a comma?

Link to comment
Share on other sites

Actually, the code that you found was in the disassembler, which isn't much help. What you want is line 722 of binutils-2.19.1/gas/config/tc-tms9900.c


Change that line to

 { "stst", 0x02C0, {ARG_REGISTER,  ARG_NONE}},

and you are back in business.


The error you were seeing was due to the fact that this instruction was falsely insisting upon a second argument for STST. Something like this would have made it happy:

stst r0, >0000


It also seems like there is a problem with the SBO, SBZ and TB instructions. During the assembly process, the bit offsets are being reduced by half. They are currently using constants in the same way as JMP, which is wrong. I need to add a new constant type for these CRU instructions for correct operation. At least LDCR and STCR look right. I haven't had a chance to look at your other issues yet, but I should have some answers for you tomorrow.

Link to comment
Share on other sites

Actually, the code that you found was in the disassembler, which isn't much help. What you want is line 722 of binutils-2.19.1/gas/config/tc-tms9900.c


Thanks! I only checked the first file where it founds "stst". :D


I haven't had a chance to look at your other issues yet, but I should have some answers for you tomorrow.


There's no hurry, I'm not blocked with these ones.

Link to comment
Share on other sites

  • 6 months later...

Well, after a really long time without any signs of life from this project, it's patch time.


A big "thank you" goes out to Lucien. A lot of the updates here are a direct result of the effort he put into making Rush Hour. He did a great job wading through all the brokenness to make a functional game. Now it's time for everyone to benefit from that work.


New Binutils fixes in this release:


STST was incorrectly looking for two arguments

SBO, SBZ and TB incorrectly using constants

EQU'ed symbols sometimes replaced using wrong endianness


GCC fixes:


Fixed several word-to-byte conversion errors

Fixed "unrecognizable instruction" for zero comparison operations

Made optimizations for most comparison operations

Improved correctness of condition flag handling

Switch statements now work properly

Fixed divison and modulus, operands were used in wrong order

Fixed subtract, operands were occasionally used in wrong order

Fixed stack frame corruption when local variables are in use

Added optimizations for forms like (int Y)=((int)(char X))<<N


The patch and build procedures are the same as always. Development notes are on my blog for those who are interested.


Things are shaping up pretty well so far. (Yes it is taking forever, sorry about that.) I don't see any obvious holes to fill, or optimizations yet to do. At this point, I just need to exercize the compiler with larger programs and increase test coverage. If anyone finds a problem, or sees an area where improvements can be made, please let me know.


I'm continuing to work on related projects (disk management tool, libc library, documentation). There's still lots to do, so these updates will keep coming.



  • Like 3
Link to comment
Share on other sites

Great work.


(int y) = ((int)(charx))<<8

sra r2, 8 12+2*8+4=32

sla r2, >8 12+2*8+4=32

total: 64 clocks, 4 bytes


Sorry for some maybe stupid questions. And sorry for not posting on your blog.


So "charx" must be something between >00 and >FF. Right ?


Looking at the first instruction, being SRA (Shift Right Arithmetic), I assume "charx" is represented in memory as a word (looking at the code it's in R2 at that stage) like something between >00xx and >FFxx ?


SRA fills vacated bit positions with original MSB (Most Significant Bit).


So "SRA R2,8" would turn >00xx into >0000, and >FFxx would be >FFFF ?


But wouldn't that make (int)(charx) wrong ? - Shouldn't >FF00 be turned into >00FF ?

Link to comment
Share on other sites

So "charx" must be something between >00 and >FF. Right ?


Looking at the first instruction, being SRA (Shift Right Arithmetic), I assume "charx" is represented in memory as a word (looking at the code it's in R2 at that stage) like something between >00xx and >FFxx ?


SRA fills vacated bit positions with original MSB (Most Significant Bit).


So "SRA R2,8" would turn >00xx into >0000, and >FFxx would be >FFFF ?


But wouldn't that make (int)(charx) wrong ? - Shouldn't >FF00 be turned into >00FF ?


The example was written assuming the values are in currently in registers, and doesn't use correct C syntax. The idea was to use shorthand and c-like pseudocode to get the idea across quickly. A real-life example would look something like this:


void do_something()
 char x;
 int y;


You're right X must be a value between >00 and >FF, and if the X value is in memory, it need not occupy a full word. Once copied into a register (using MOVB or something) the value will be stored in the high byte.


What you wrote would be true for unsigned values, but not for signed ones.


>FFxx in a register can be interpreted as either (char)(-1) or (unsigned char)(255)

>FFFF is (int)(-1)

>00FF would be (int)(255)


There are optimizations for both of these, but I only used an example for signed values since the timings are the same and only differ by the SRA or SRL instruction.


The initial implementation would produce this code:

* Assume x has a value of -4 (>FC), and is stored in r2 as >FCxx
* y = (-4)<<4 = -4 * 16 = -64 = >FFC0
sra r2, 8   * Convert to signed integer (r2=FFFC)
sla r2, 4   * Left shift converted value (r2=FFC0)


The optimization emits this code:

* Assume x has a value of -4 (>FC), and is stored in r2 as >FCxx
* y = (-4)<<4 = -4 * 16 = -64 = >FFC0
sra r2, 4   * Shift into final position (r2=FFCx)
andi r2, >FFF0	* Mask unknown bits (r2=FFC0)


And finally, for unsigned values:

* Assume x has a value of 252 (>FC), and is stored in r2 as >FCxx
* y = 252<<4 = 252 * 16 = 4032 = >0FC0
srl r2, 4	* Shift into final position (r2=0FCx)
andi r2, >FFF0	* Mask unknown bits (r2=0FC0)


Since fewer bit shifts are required, the optimized code runs faster (I figure about 33% faster on average), but uses one additional code word.

  • Like 1
Link to comment
Share on other sites

I think a "small" tutorial to install GCC on Windows is missing. Here is one:




1. Install Cygwin


I chose the mirror "ftp://ftp.uni-kl.de"that is more than 10 times faster than the first mirror in the list "http://mirrors.163.com"


2. Choose the following Cygwin options ("bin" checkbox, the source is not necessary) (54MB)


Category "Devel"

- gcc-core

- libiconv

- make

- patchutils


Category "Libs"

- libgmp-devel

- libmpfr-devel


Category "Interpreters"

- m4


3. Download the GCC 4.4.0 sources (60MB)



4. Download the Binutils 2.19.1 sources (15MB)



5. Run Cygwin to create your home directory (\cygwin\home\yourname)


6. Extract the source files to your home directory

You can use "tar" in the Cygwin console:

Put the .tar.bz2 files in your home directory

$ tar xvfj gcc-4.4.0.tar.bz2

$ tar xvfj binutils-2.19.1.tar.bz2


7. Extract the patches


Put binutils-2.19.1-tms9900-1.3-patch.tar.gz in the binutils-2.19.1 folder

Put gcc-4.4.0-tms9900-1.5.patch.tar.gz in the gcc-4.4.0 folder


$ cd ~/binutils-2.19.1

$ tar xvfz binutils-2.19.1-tms9900-1.3-patch.tar.gz


$ cd ~/gcc-4.4.0

$ tar xvfz gcc-4.4.0-tms9900-1.5.patch.tar.gz


8. Build binutils and gcc as explained earlier in this thread


Patching the original files:

$ cd ~/binutils-2.19.1

$ patch -p1 < binutils-2.19.1-tms9900-1.3.patch


$ cd ~/gcc-4.4.0

$ patch -p1 < gcc-4.4.0-tms9900-1.5.patch


Building binutils

$ cd ~/binutils-2.19.1

$ ./configure --target tms9900 --prefix /home/yourname/binutils

$ make all

$ make install


Building GCC

$ cd ~/gcc-4.4.0

$ ./configure --target=tms9900 --prefix=/home/yourname/gcc --enable-languages=c

$ make all-gcc

$ make install


There are some errors during "make install", you can ignore them


9. Replace the i686 assembler (/bin/as) with the tms9900 assembler

- Rename /bin/as.exe to something like /bin/i686-as.exe

- Copy tms9900-as.exe from ~/binutils/bin to /bin/as.exe




If you don't want to do all these steps, I prepared a compiled version (26MB) http://lb3.one/public/TI99-GCC-1.5.zip


Here is how you install it:


1. Install Cygwin (22MB)


Choose the second mirror in the list


Choose the following options (bin checkbox):

Category "Devel", "make"

Category "Libs", "libmpfr-devel"


2. Run Cygwin to create your home directory


3. Extract "TI99-GCC-1.5.zip" to your home directory "cygwin/home/yourname"


4. Copy "/cygwin/home/yourname/binutils/bin/tms9900-as.exe" to "cygwin/bin/as.exe"



To try it, change the paths in "~/RUSH_HOUR/Makefile" from "/home/-" to "/home/yourname" and type this at the Cygwin console:

$ cd ~/RUSH_HOUR

$ make


It creates "rush_our.ea5.bin" and "rush_hour.c.bin" in the "cygwin/home/yourname/RUSH_HOUR" directory.

The .c.bin file is a cartridge binary ready to use. The .ea5.bin must be converted to the TIFILE format with Ti99Dir


To check the generated assembly code, type this:

$ ~/gcc/libexec/gcc/tms9900/4.4.0/cc1 -O2 main.c

Edited by lucien2
  • Like 3
Link to comment
Share on other sites

  • 5 weeks later...
  • 2 months later...

Well, it's patch time again.


First off, an aplogy to Lucien for not responding earlier, but the short answer for "how to use a single quote?" is "you can't". There was a bug in binutils which prevented its use.


I tried to be clever and allow either TI-style or C-style strings in the assembly code, but did a terrible job of it. The parser always treated escaped single quotes as the end of the string, which causes some frustration, to put it mildly.


Fortunately, that's all been fixed in the latest patch.


So here's the official changelog for binutils:


Fixed bug prohibiting the use of single quotes in a string

Strings my be in either TI-style 'stuff' or C-style "stuff"

TI-style strings follow E/A text rules

C-style strings may include standard escape codes "example\n"


And the other things fixed in GCC:


Fixed comparison against +-1 and +-2, they got broken in 1.5

Prevented incorrect use of fake PC register for real work

Improved AND operations to use fewer setup instructions

Fixed incorrect long-to-char conversions

Fixed post-increment pointers which live on the stack

Added optimization for setting byte quantities to zero

Added optimization for (int)X = (unsigned char)((int)X)

Removed double-counting space for saved registers on the stack

Reduced overhead needed for multiply instructions

Fixed bug causing structures to be loaded into registers

Structures used as function arguments now passed by reference

Fixed more bugs causing bad int-to-char conversions


Work has kept me pretty busy lately, but progress continues.


As always, if anyone finds problems or has suggestions, please let me know



  • Like 3
Link to comment
Share on other sites

Thanks for the update!


I had this warning with the compilation of the assembler: "assignment discards qualifiers from pointer target type". Even if it's only a warning, it did not compile.

I had to replace the line 470 of "tc-tms9900.c" from "input_line_pointer = p;" to "input_line_pointer = (char*)p;".


The was also a bug with the address of a stack frame slot "@>XX(r10)" in the function "display_at_multiline" in "lib.c".

It saves 5 registers on the stack (mov rX,*r10+), then it overwrites the last one at r10+8 with a "mov r15,@>8(r10)".


I replaced the function "tms9900_get_saved_reg_size" from "tms9900.c" with this one, from the 1.5 version, and it replaced "mov r15,@>8(r10)" with "mov r15,@>A(r10)".


static int tms9900_get_saved_reg_size(void)
  int idx = 0;
  int size = 0;
  while(nvolregs[idx] != 0)
     int regno = nvolregs[idx];
     if ((regno == HARD_LR_REGNUM && !current_function_is_leaf) ||
         df_regs_ever_live_p (regno))
        size += 2;



The only other bug I found is in "main.c" from RUSH_HOUR. If I replace x and y of the struct "point_cursor" from "char" to "int", the function "move_block" does not work correctly anymore.


Apart from these problems, all the other bugs seem to be fixed. :) I recompiled all my GCC programs and carefully tried them.


lb sources.zip

  • Like 1
Link to comment
Share on other sites

  • 4 weeks later...

Would this be something that could be compiled for the TMS9900 and work with the CF7+ ?

You'd need to include your own sector read/write routine.



GCC is rather large and slow. Even if you could squeeze the compiler into memory (which I'm pretty sure you can't), you probably wouldn't have enough memory to compile anything. Even if you could, it would take forever to compile anything.

Many C compilers from the pre-multi-mega/gigabyte days used multi-stage compilers to fit into small memory. They parsed in one module, and generated code in another and those compilers were smaller.


To be honest, I'd rather use a cross compiler and have the better compiler.

You can probably compile, link, and transfer the executable over to the TI faster than the TI could compile.

The only reason I see to have a native compiler is just to say it exists and that's more of an ego thing than practical.


Going slightly off topic...

There are better compilers than GCC now anyway and GCC is gradually going to be replaced on other platforms.

Apple is already pulling their support from GCC and migrating to LLVM over the next few years for it's dev tool.

LLVM is about 3x faster than GCC, and has a better code optimizer.

Someone would need to port the 9900 code generator to use it though.

I think right now time would be best spent working out the kinks in GCC given it's stage of development, but somewhere down the road an LLVM code generator might be a worthwhile project. You still wouldn't be able to have a native version.



If you really want a native compiler, something like SDCC would probably be a better place to start since it has a smaller code base.

Link to comment
Share on other sites

  • 1 month later...

Hi All,


My name is Danny, I'm new to this forum but when growing have used a TI extensively until as late as 1996 when I got my first "real" PC. As of late I've been working on integrating a Sega master system VDP into a TI-99/4A. The master system VDP is identical to a TMS9918A software-wise, but implements a new mode (called "mode 4") that is closely related to graphics I, with the added benefit of hardware scrolling and up to 16 colors per tile. The first step towards this has been implementing mode 4 support in the ti99/sim emulator (I use Linux, and found this code base easier to start from than MESS) and writing some programs on the TI that activate and use mode 4. I've made some progress and things look ok. My main problem though is lack of assembler knowledge.


So, I've compiled gcc with the patches found in this thread and everything looks ok. I compiled the hello world example, which again seemed to work fine. But when I try to run the resulting cartridge image in ti99/sim and choose the 2nd option ("HELLO"), it basically takes me back to the home screen. Every program I've tried to compile exhibits the same behavior. I tried the cart in Classic99 through wine (by renaming the file to helloC.BIN) but it doesn't even show up in the menu there.


Any ideas what I might be doing wrong? Using Ubuntu 12.04 and the latest patches found in this thread.





Link to comment
Share on other sites

That's strange. I just compiled and ran the 3 "hello world" examples from posts #1, #64 and #79 without problem.


Which versions did you patch? gcc 4.4.0 and binutils 2.19.1?


Could you post the source and the generated objects? So, I can compare with mine.

Link to comment
Share on other sites

Yes, I'm using the exact versions of gcc and binutils, the patches also applied cleanly so I don't think that's the problem. It might very well be that this is a bug in ti99/sim, but I don't know how to load the resulting .cart file into either MESS or Classic99 (through wine). I'm rather "new" to this modern-day TI development thing ;).


I'm compiling this example, and I've attached the output files (main.o, hello.elf & hello.cart)


Define macros to access VDP registers
These macros can be used as if they were variables.
This makes things easier to read and we don't have a lot of
typecasts troughout the code.
Read data register located at address >8800
Write data register located at address >8C00
Address register located at address >8C02
#define VDP_READ_DATA_REG (*(volatile char*)0x8800)
#define VDP_WRITE_DATA_REG (*(volatile char*)0x8C00)
#define VDP_ADDRESS_REG	 (*(volatile char*)0x8C02)
/* Flags used during VDP access */
#define VDP_READ_FLAG 0x00
#define VDP_WRITE_FLAG 0x40
#define VDP_REG_FLAG 0x80
/* Location of the screen in VDP memory.
This is the based on the VDP configuration set by the TI firmware */

*		 vdp_copy_from_sys
* Description: Copy data from system memory to VDP memory
* Parameters : index - Starting index into VDP memory
*			 src - Address of source data in system memory
*			 size - Number of bytes to copy
* Returns : Nothing
static void vdp_copy_from_sys(int index, char* src, int size)
/* Find the address of the end of the source data */
volatile char* end = src + size;
/* Set the address in VDP to which we will copy.
 We must do this in two parts.
 The first part: Write the low byte of the VDP address
 and include a flag informing VDP we will soon be writing data */
/* The second part: Write the high byte of the VDP address.
 After this, the VDP will be ready to have data written into
 it's memory */
VDP_ADDRESS_REG = (char)(index >> ;
/* Copy data to VDP memory via VDP data write register
 Keep copying until we hit the end of the source data */
while(src < end)

*						 display_at
* Description: Display a text string at a specified location on the screen
* Parameters : row - Screen row for display
*			 column - Screen column for display
*			 text - Text to display
* Returns : Nothing
static void display_at(unsigned int row, unsigned int column, char* text)
int size;		 /* Size of text string to display */
int offset;	 /* Offset into screen buffer */
char *ptr = text; /* Pointer to a character in "text" */
/* Find length of display text
 C strings are ended by a zero byte, so look for that */
size = 0;
while(text[size] != 0) size++;

/* Convert row and column to screen offset.
 There are 32 columns per row */
offset = row * 32 + column;
/* Copy text to the screen, which lies in VDP memory */
vdp_copy_from_sys(offset + VDP_SCREEN_ADDRESS, text, size);

*								 main
* Description: Entry point for program
* Parameters : None
* Returns : Nothing
void main()
/* Display flashing text at the center of the screen
 We will do this by displaying text, then spaces, then text again.
 To keep things simple, we will use the character set defined by the
 TI firmware. Unfortunately, it only defines symbols and uppercase
 letters. Keep that in mind if you change the displayed text
 This loop will never exit. Since the interrupts have been disabled
 in the setup code, the console must be reset to exit the loop */
display_at(12, 10, "HELLO WORLD!");
display_at(12, 10, "		 ");


Link to comment
Share on other sites

I ran your cartridge with classic99 and ti99sim under windows without problem.


With classic99, you said it does not show in the menu. The TI99 boot menu or the classic99 cartridge menu?

It shows in the cartridge menu only if you declare it in classic99.ini. Otherwise the menu "Cartridge / User / Open" works fine if you rename it "helloc.bin".


I tried ti99sim for the first time. You must also rename it "helloc.bin" and run "convert-ctg hello.bin" without the "C", then it says "1 bank of ROM at 6000, 1 bank of ROM at 7000".


If you type "convert-ctg helloc.bin", it says "GROMS: 3" and does not work.

Link to comment
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.

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.

  • Recently Browsing   0 members

    • No registered users viewing this page.
  • Create New...