Closed Fabrizio-Caruso closed 4 years ago
I don't have quite enough to go on here, I would suggest boiling down to a basic hello world project that you have created but we both can look at - I can't build the text you pasted there, as it needs a crt0, tools, etc.
I can tell you that I don't ship warnings on purpose - and if I do I document them. I haven't build much with this lib lately, but as far as I know the test app builds and runs (will try that again in a sec).
/What/ symbols are you getting multiple definitions on? Is it not possible that you have functions in your code that match the names of functions in the library?
Looking at your cart file - the build is a little suspect since it's a 16k binary image with 11 extra bytes at the end. 16k is possible if you're bank switching. How are you loading it into Classic99? There is no metadata in the filename to tell Classic99 whether it's inverted bank order or not -- probably this particular demo should just be 8k with a 'C.BIN' extension for 8k ROM.
If I just drag and drop, it looks like Classic99 decides it must be an 8k ROM anyway:
Loading file (D:\new\ti99_cross_chase.Cart) from disk: Type C, Bank 0, Address 0x6000, Length 0x400C D:\new\ti99_cross_chase.Cart overwrites memory block - truncating.
It does crash for me as well... without a symbol file it's a little hard to step through it, but if I assume that set_text() is the first code called by your main() function, I can set a breakpoint on the VDP register being set. The first register set in set_text is Register 0 to 0, so that's easy. However, that breakpoint doesn't happen, so it's probably not making it to INIT_GRAPHICS().
So, I stepped into it a few times -- I'm not really keen to reverse engineer your software. ;) Interrupts off, workspace to scratchpad, stack to top of low memory save the (uninitialized) return address to the stack, copy initialized data from >7A12 to >A000. 7A12 is all zeros, but maybe that's on purpose. Then zero the bss area at >A00C up to >A192. Then it looks like we jump to (main?) at >68CC. Some stack setup, zero >A06A, load a bunch of registers and call >6234. This seems to initialize a few variables and return. Next we call >68BE, who saves the return address and calls >60C0. Fair bit of stack setup here and off to >6DBC. Is this character out? Stack setup, then a check for greater than >1F which looks like a check for printable. We have >20, space, so it is. We get data from >A00A and jump to >6B18. For better or worse, the number we got was 0. There's a bit of bit manipulation here that I can't place at the moment... but for the first pass we have 0 anyway. It's calculating some kind of an address, not sure. We come back, get a copy of our character, and branch off to R13 which was never initialized and is >0000, crashing.
So, basically, without at least the map file I can't tell if that's in my code or yours, but I /can/ tell you that it never tries to set the screen mode to text, and it MIGHT be trying to print spaces before anything is initialized.
(TESTLIB still builds and runs without warnings)
@tursilion Thanks a lot for your long reply! I should have tried with a simple hello world to start with. I am new to the TI99.
My ultimate goal is to integrate GCC for TI (+libti99) into CrossLib which is my universal retro-development framework (https://github.com/Fabrizio-Caruso/CROSS-LIB), which supports more than 200 different retro-systems and it already supports more than a dozen cross-compilers.
So I am doing some experiments without really understanding all the details. This is what I have done:
The multiple definition errors I get are about (probably) all the libti99 functions:
(.text+0xe): multiple definition of `VDP_SET_ADDRESS_WRITE'
cross_lib/display/display_macros.o:(.text+0xe): first defined here
I am getting this error when I run the linker:
ti99_cross_chase.elf: $(PREREQUISITES)
$(LD) $(OBJECT_LIST) $(LDFLAGS) -o $@ -allow-multiple-definition
which is the only place where I use libti99.a
:
LDFLAGS=\
--script=$(CFG_PATH)gcc4ti99/linkfile.cfg -L$(LIB_TI99_PATH) -lti99
How do I run your TESTLIB binary? Is is it in ea5 format?
My game (on other systems) requires at least 16kb of code + some ram (probably more than 256 bytes). What do I need to do to produce "something" (cartridge or other format) that can have 16kb of code and use more than 256 bytes of (real CPU, non-video) ram? I might need more than 16kb for the code. Is it possible?
I will try to generate a map file and see if it gives me more information.
I am a real noob on the TI99. So sorry for the silly questions I am asking.
No, that's fine, I don't mind. You have a neat project there!
GCC for the 9900 is probably about 99% of the way there, but it sill has bugs, so you have to be aware of that sometimes. It's pretty reliable these days, though. If you visit AtariAge.com, in the TI programming forum you'll find Insomnia who did /all/ the work on the GCC compiler.
VDP_SET_ADDRESS_WRITE is an inline function in my vdp.h header - this hasn't caused me issues in the past, but it appears for some reason inline functions are not being treated static? Do you have a switch set that might affect that? It's been a while since I've messed with it. ;)
The warning, though, is showing you that the function is first defined in display_macros.o, which is definitely your code and probably due to including vdp.h (which you should of course do... just curious that the inline functions are biting you).
Yes, the output files are TESTLIB1, TESTLIB2 and TESTLIB3, and they are Editor/Assembler PROGRAM files (Option #5, as you guessed).
Cartridge ROM space on the TI is limited to 8k, you would need to (manually) code bank switching to exceed that. However, if you build for EA#5 files, you can have up to 24k of contiguous space (plus a separate 8k block which is used for data and stack). You're kind of stuck on that one.
[PARTIALLY SOLVED!] I have followed your Makefile and I am now getting something on the screen that makes some sense (to me):
I need more than 8k of contiguous memory, so I will use the ea5 format.
How do I build an EA5 binary for a contiguous binary with no bank switching ? I see you are using some tools that are not included in your repo. Do I need to use ea5split?
P.S.: I cannot code bank switching inside my framework because it would have to be in the framework and not the game code. My game are totally hardware-agnistic. I recompile the very same game code for more than 200 targets as CrossLib does the "magic abstraction".
@tursilion I can now create ea5 file and also split it. Is the .bin file your Makefile a sort of standard TI99 .bin file that can be used as a cartridge?
Can a .bin file be bigger than 8k and be read as cartridge?
It seems that tools such as Module Creator can produce .BIN cartrige files that are bigger than 8k from a list of EA5 files. Is this right?
Ah, cool! ea5split is a tool that came from the GCC port author, sounds like you found it. GCC outputs an ELF file, then there is an ELF2EA5 which creates a single file. But the convention on the TI is to split program files into 8k loadable chunks, so ea5split handles that.
I'm pretty sure I heard about your project before - that's an impressive task!
Anyway, if you mean the makefile that creates testlib, I don't (intentionally) create a cartridge image there... in fact running a GCC program from cartridge actually has some restrictions - in particular you need to decide whether you will require the optional 32k memory expansion (most people do - in that case it's much easier).
Some people create the EA#5 file, then for distribution, they make what we call a "loader cart", and there are tools to automate that. This just copies the data from ROM to RAM and then runs it from there. (So requires 32K).
And EA#5 always requires the 32K, since there's nowhere else to load the program. ;)
So to that, no, for a flat (non-banking) cartridge, 8k is a hard size limit that can't be exceeded.
The module creator that you mention creates the "loader carts" I mentioned above. These are bank switched cartridges that just copy your program to RAM and run from there. Perfectly acceptable, you just have to require the 32k (which is also perfectly acceptable).
@tursilion Thanks again for your reply! Sorry if one point is still no clear to me. I don't understand what you mean by 8k restriction. I see you TESTLIB can be run as a cartridge and it is bigger than 8k and your test does not seem to have bank switching code. So what do you mean by bank switching cart?
Can I put a bigger than 8k (actually 24k) contiguous code with no bank switching code in it into a cartridge image that can be run on an 32k-expanded TI99 (emulated or real)? Which tools should I use to do this? ModuleCreator? A linker config file + some cart header? Can I do it in a scriptable way in my Makefile?
To my knowledge, TESTLIB can not be run as a cartridge. If it is running, it is inadvertent and if it's bigger than 16k, then it will probably crash before getting to the end. ;)
There is only 8k of space in the TI memory map for the cartridge port. The area immediately before it is reserved for all plug in cards (like the disk driver) and the area immediately after it is the system I/O space, so it can't be expanded artificially or any other way.
So NO. A cartridge can NEVER contain more than 8k of contiguous program ROM space. I have to use all of these words because everything on the TI is "more complicated than that". ;) Module Creator creates a program which uses bank-switched ROM to store your EA#5 program, copy it to RAM, and run it from RAM. Think of it as loading from a fast disk.
Ok, than a cart can contain raw code to be moved to RAM and then it can be run in RAM. That is one TESTLIB can be distributed in a cart as long as the cart has the code to copy it into RAM by taking care of the bank-switching just to copy it to RAM. Module Creator is probably doing all of that.
Yes, it is.
I would like to generate with my Makefile something that can be used easily such as a 32k cart image (which copies its content into RAM) and/or autobooting disk image and/or tape (wav?) image. Which tools would you suggest to produce at least one of these images?
Tape has no such thing on the TI, so we can rule that out.
First, create your game as an EA#5. The other options will work with that. You can test by loading with Editor/Assembler, and when it is functioning correctly, then we move to the next step.
For an auto-booting disk, your only option is Extended BASIC with an EA#5 loader. This is quite common and nobody will have a problem with this in emulation. On real systems many people do not have the tools to get from the internet to a real diskette - but this is less and less a problem as new hardware comes out.
I have a simple Extended BASIC loader you can use here - http://harmlesslion.com/software/xb
If you load the disk up in Extended BASIC in an emulator, hold ALT+4 to breakpoint at launch. Then you can list the program, edit it to just run your file instead of asking the user for one, and save it (SAVE DSK1.LOAD)... the startup program is always called LOAD. The only way to script getting your files onto a disk as far as I know are the tools that come with xdm99 - a set of Python-based utilities including assemblers and command-line disk editors - https://github.com/endlos99/xdt99
For cartridge I do not believe there are any command line tools suitable for Makefile. I can send you the source code for my old Windows-based tool (which predates Module Creator)... you could convert the code into a command-line tool for whatever OS fairly simply.
Actually, with apologies, the source for my old Windows-based tool will NOT help... it only created GROM cartridges, and that was why I stopped using it. I put all the functionality into Classic99 instead.
It's not the easiest code to follow, but it is at least split out. Have a look at https://github.com/tursilion/classic99/blob/master/addons/makecart.cpp
This code creates cartridges from the memory of Classic99. You can adapt the class Make379Copy, which encapsulates converting a binary memory image into a bank-switched loader cartridge. (For modern use, set opt.bInvert to FALSE, everyone now uses non-inverted carts).
That would just leave getting your EA#5 into a memory image. You might be able to use the output of elf2ea5, since it's already one large file. An EA#5 file starts with a 6 byte header:
A quick look at the output of elf2ea5 suggests that it follows this header pattern, so it should be straight-forward to write a tool that converts it into a cartridge.
Sorry, I wrote a lot of stuff. I hope it's helpful, but I need to run to bed now. ;)
@tursilion I got the first (sort of) playable version of one of my games running but I have a problem with kbhit()
and cgetc()
which only reads one character with no-autorepeat. How can I read the keyboard with auto-repeat? i.e., if I keep a key-down, I expect kbhit()
to be true and cgetc()
to output the same key.
Moreover if I only use cgetc()
to have a turn-based type of game, it displays the cursor while it waits for input. Can I disable the cursor?
The TI doesn't have auto-repeat, so I didn't implement it in there... you might be better off to write a wrapper for kscan() instead, which will always provide the current status of the keyboard.
I think there is a way to turn off the cursor, but I don't use the conio stuff, that was added by someone else's request, so I don't remember it well. Look in conio.h - it looks like the function is "cursor", pass 0 for off, non-zero for on.
Looking deeper, you should be able to get the key specified by kbhit() -- there is just a single key buffer implemented to support that. I guess that's why you are asking about auto-repeat. ;)
cgetc() is by far the slowest way to get input in this library - since it manages the cursor, fakes a single-key buffer, and enables interrupts. If you can wrap kscan() with the functionality you want, it will work better. Use kscan.h, and call kscan(KSCAN_MODE_BASIC) to scan the keyboard. Then read the result as a char from KSCAN_KEY. If the key is 0xff, then no key was pressed, otherwise it is the ASCII value of the current key. The value will not change until you call kscan() again.
I was curious why it doesn't just work the way you expected, and it appears that I read the docs for kbhit() to indicate a /new/ keypress only. If I change it to repeat the same key continuously, that will break code that relies on that behaviour for input (ie: typing "apple" instead of "aaaaaaaaaaaaappppppppllllllllleeeee" ;) )
I don't think I will change it at this time, but I'll keep it in mind when I revise the lib. In the meantime, I recommend kscan as above.
Yes. When auto-repeat is present and enabled, it is a way to generate /new/ key-presses.
Auto-repeating systems have a little lag before auto-repeat kicks-in in order to avoid things like "aaaaaaaaapppppplllle".
All the kbhit()
implementations I have seen do produce a new true value when auto-repeat kicks-in.
If auto-repeat is off, it should not.
By default kbhit()
should behave as the O.S. So if the TI99 by default has no auto-repeat, then no auto-repeat should be enabled by default but if auto-repeat can be enabled and it is enabled, then kbhit()
should detect auto-repeated keys.
At least this is the behavior I see on all other devkits with CONIO support such as CC65 and Z88DK.
So, I will follow your advice and use kscan
and/or the joystick (if I figure it out).
I know it's been a while, sorry. Hope you solved it! :)
The problem is, kbhit() has no way to do auto repeat. The TI doesn't have an OS. TI BASIC and Editor/Assembler do implement an autorepeat as you describe, but that's done at the application layer. The only thing the ROM provides is the hardware scan and whether a keypress is present, and whether it's different from last time. There's no clock to check time on, and my lib is intended to be closer to the hardware - conio exists at all only because the Plato guy asked for it. ;)
So /could/ it implement auto-repeat? I suppose. It would be a bit of coding to maintain the state - though there's already some since kbhit() itself is not something the TI could natively do (scan the keyboard, and then guarantee the key is still pressed when kscan() is called! It's just an array of switches!) Would need to be a counter, and then the delay period and repeat rate would depend on how often the calling application called it. It would be a bit hacky, since only two functions would be aware of it. Plus it would no longer be good for gaming.
Not sure. I'm trying to talk myself either into or out of it. But I guess for right now, not going in. ;) I kind of want to re-organize this library and get the high level stuff away from the low level stuff, right now it's mixed together and that fills me with hesitations like this one, cause the goals are conflicted. ;)
Gonna close this for now though!
Thanks for your hints and help! I still have a very initial TI99 support in CrossLib because I am working on all targets at the same time. If you are curious you can take a look at: https://github.com/Fabrizio-Caruso/CROSS-LIB/blob/master/docs/STATUS.md
I will try to improve and provide complete support on the TI99. Maybe I will bother you again with some questions. The current version of my CrossLib game(s) that I can build are (sort of) playable despite TI99 support in CrossLib lacking many important features.
Hi @tursilion ! Sorry to bother you again... with something similar to what I have already asked 18 months ago... I am no longer able to produce with GCC for TI + your CONIO implementation and load EA5 files into Classic99.
My (messy) Makefile is here: https://github.com/Fabrizio-Caruso/CROSS-LIB/blob/master/src/games/chase/makefiles.chase/Makefile.gcc_tms9900_targets
I understand that GCC produces an ELF file, which has to be converted into an EA5 file, which has to be conventionally split into 8K blocks (I wonder whether this is necessary if the EA5 is smaller than 8K).
If I try to load such an EA5 file, I get an error code 7 in the Editor/Assembler. I guess the format is not good.
Maybe I am doing something wrong with the emulator. If I look at your example here: https://atariage.com/forums/applications/core/interface/file/attachment.php?id=872708
Is MAIN1 a file that can be loaded by Classic99? How? If I try, it fails with the same error.
If you are loading with Classic99, the first thing to do with any file error is to open the debugger and read the log to see what caused the error. Error 7 literally means "file error", it's not very useful. ;)
MAIN1 works for me here. Yes, it's Editor/Assembler Option 5 (RUN PROGRAM FILE). Make sure it's in the right folder, that the folder is configured for Files (FIAD), and make sure you are entering the right case (MAIN1 is uppercase, if you enter lowercase it MAY not load, depends on a lot of things.)
But since 'File Error' includes "not found", it's likely a path thing. The debug log will outright tell you if that's the case, as well as where it looked.
I got a message I don't see here anymore, I don't know if that means you solved it or not. But I want to see the debug log, you can copy/paste the text from it instead of screenshots.
Thanks @tursilion!, it was indeed a stupid path problem due to a directory name change. So my bad.
I have managed to load the split EA5 file, which it is not "split" as it is just one but I have to use ea5split
on files created with elf2ea5
to get them to load.
So the names of the tools are confusing.
elf2ea5 does not produce a real runnable ea5 file (it probably lacks a necessary header).
ea5split seems to add something necessary to get a "real" runnable ea5.
So my current build chain does:
source -> elf -> (header-less?) EA5 -> (split if >8k) runnable EA5
How can I convert the runnable EA5 file(s) into a self-expanding and auto-executing cartridge image? I am always assume a 32k RAM expansion is present.
I believe that someone on Atariage has a standalone cartridge making tool that starts with EA#5 files... you can do it from Classic99 but you need to understand the memory range that you want to save. (Although, for gcc files it will start at >A000 and run to however big the program is - you could determine that from the EA5 files). That's in the debugger - you need to load your program, breakpoint, then use Make->Save Memory As Program. You would save High RAM as listed, set the Start Address to A000, enter a name in 'cartridge', and it's a good idea to also tick Load Character Set, Initialize Keyboard and Restore VDP Registers (whether they are needed depends on the software, but they don't add much to the size).
You can get the module creator from https://www.ti99-geek.nl/ - look under 'Modules', 'Module Creator'. I can't give you a direct link, looks like the site uses scripts instead of links. I can't offer any support, I've never even downloaded it, but it replaces my older one that was GROM only.
Ciro is expressly defying my wishes. That tool is deprecated because GROM output is less useful to people than ROM output, which that tool doesn't do. Fred's tool is the newer and only currently supported tool, besides Classic99. I also do not support zips downloaded from anywhere other than my own website or my files here at Github. I keep asking people not to share that tool... but if they are going to, it's on my own website and could be linked there. Grr.
C doesn't have any hooks for the CRU instructions, you need to use inline assembly. You can look at libti99's vdp.h for some examples such as VDP_WAIT_VBLANK_CRU, which reads bit 2.
But since you're using my library anyway, why not just use kscan() and joyst()?
#include <kscan.h>
...
kscanfast(1); // read joystick 1 fire button
if (KSCAN_KEY == 18) { fire is pressed }
joystfast(1); // read joystick 1
// returns as -4,0,+4 in KSCAN_JOYX and KSCAN_JOYY
I see you're also posting on AtariAge. Could we please move this off of Github? I don't normally log into this website.
Thanks! Ok, so it is your tool. Sorry. I have removed the link. I will look at Fred's tool, so.
Hi, first of all sorry if I am using the Issues section to ask a question that may not be a bug. I am trying to use your lib to get my game to compile. I am getting several "multiple definition" errors that I can silence with
-allow-multiple-definition
Is this expected when using your package on multiple files? Am I doing something wrong?
I am using this Makefile: https://github.com/Fabrizio-Caruso/CROSS-LIB/blob/master/src/makefiles.chase/targets/Makefile.gcc_tms9900_targets (which uses the option
-allow-multiple-definition
to get a successful compilation)I get a .cart and a .elf file. If I attach the .cart file into classic99, I can start it but I get a repeating high-pitched sound instead of a hello world message as I expect in my code:
The .cart and .elf files are in the attachment ti99_cross_chase.zip