Jump to content


  • Content Count

  • Joined

  • Last visited

Community Reputation

779 Excellent

About speccery

Recent Profile Visitors

4,279 profile views
  1. Thanks. Yes, the 9902 is connected to the USB port after the FPGA is configured. My setup is super simple, so there is just the CPU, the 9902 and memory (8k ROM in the bottom of address space, 32K RAM in the top half of the address space). The memory layout is not as in the TI-99/4A. But as this is FPGA based, one can configure the memory anyway one wants. If your kernel only needs the CPU and for example 64K RAM, it would work. I will post this “breadboard” Verilog core later today to github.
  2. Somewhat off-topic: in my exploration of different low-cost FPGA chips, I sometime ago came across the Lattice ICE40UP5K chip. It can be targeted with the open source icestrom toolchain. I ordered from Tindie two Upduino V3 boards to test these chips, I got them about a week ago after some exercises with customs. UPduino at GitHub I put quickly together a simple "breadboard" design (as I call it) and now have it successfully running on this tiny chip. Basically it's a minimal TMS9900 system, consisting of: TMS9900 processor core (my verilog version) running at 12MHz TMS9902 UART (this is @pnr's design, also verilog version) A slightly modified EVMBUG ROM to support 9600 bps 8N1 serial format. This is a 8K ROM. 32K RAM I haven't done any extensive testing, but hey, if it runs the processor core and provides a stable UART connection and is able to receive commands and does disassembly of the ROM it cannot be too far off. I am interested of this chip since it is tiny, cheap, has only 48 pins (well if you can call them that with the QFN-48 package) so it should be useable in many projects. The aspect which makes the chip interesting for retro computing stuff is that is has on-board 128 kbytes of RAM single port RAM (in 4 blocks) as well as 16 kbytes of dual port RAM (in 32 blocks of 512 bytes). So this chip alone, with no external RAM, could be able to support TI-99/4A. The logic capacity of the chip is small, and my breadboard project already took 57% of logic cells, but it seems it might be possible to make a version of Icy99 for this one too.
  3. I think I just created my first multibank cartridge! In order to see where the bugs I experience in Megademo come from, I created a super simple cartridge to enable me to load VDPDUMP.BIN files created with classic99 on the FPGA hardware. This enables me to check if my VDP shows the correct picture when provided with a valid dump from classic99. So I should be able to distinguish between VDP bugs and system bugs. Classic99's debugger has the dump RAM option, which creates the VDPDUMP.BIN file. The file length is 16k+8 bytes, and thus exceeds the max size of a non-page cartridge. I created a short assembly cartridge, which occupies only a few hundred bytes. I pad this to 4K and call it vdpload0.bin. It occupies the lower half of each bank of the cartridge, i.e. is at >6000 regardless of the cartridge ROM bank I'm in. The VDPDUMP.BIN file I split into five pieces: 4k + 4k + 4k +4k + 8 bytes. The last 8 bytes I then pad to 4k as well. These 4K data blocks become the top half of the 8K cartridge image. Then I just create the cartridge ROM image by concatenating vdpload0.bin followed by a 4K data block, five times. (and for good measure pad it to 64K with a few more useless copies). The cartridge program is very simple, it first switches to ROM page 4 (VDP register values), reads the 8 bytes of register values and writes them to the VDP registers. Then I go through ROM banks 0,1,2 and 3, copying from each the top 4K to VDP RAM. And voila, I have the data captured with classic99 in the VDP memory. That verifies that multicolor mode really does not work, but the first phase of megademo produces a correct image on the VDP - and the bug with that is with the rest of the system. Should have done this earlier, but good to have more generic debugging tools available.
  4. A few more updates. I have worked a bit more on the ULX3S FPGA board and the icy99 core. I have found a few more bugs, fixed some, and done general improvements. Most of this is done in the platform independent part of the Icy99 core, so should be portable to other FPGA systems. Text mode at both 40 and 80 columns showed some rubbish on the top right hand corner. Took a while to find the problem, but now fixed. Found some picture vertical alignment issues, fixed those. Took SDRAM into use for real, now cartridge ROM space is 2 megabytes, i.e. 256 pages of 8K. I could make it still bigger, as the board has loads of memory. This enabled me to test with many more programs, and I found some more problems. Megademo continues to be an excellent test case. It seems that the 5th sprite detection logic is not yet working, as the megademo get stuck. I had the same problem in the past with the VHDL version too. Also found problems with multicolor mode. Joystick support was lacking, so I just mapped the PS/2 keyboard cursor keys and right control key as joystick directional control and fire. I had good fun playing a bit Knight Lore after adding the joystick support. I read from @Asmusr comments that the original port had suffered a bit from performance problems, i.e. was a bit slow. No such problems with icy99, it actually runs quite a bit too fast. Also played around with the 4K competition games. I didn't quite understand what to do in some of them. Icy99 runs quite a bit faster than a regular TI-99/4A, so I need to test with classic99 as well to see if some of those are just uncovering bugs in in Icy99. I integrated my version of the code better to the ULX3S platform. Emard (ULX3S developer) had already integrated ULX3S to my core, but my own "base" version didn't have this integration yet. The ULX3S has a ESP32 microcontroller, and it can now load very fast very large cartridge images from SD card to the icy99 core. There is an overlay screen which enables one to select and load ROMs to load. Still many things to do: Audio output support Cassette reading support (should be interesting from timing perspective) 1M AMS memory support Slow the system down to approximately original speed (optionally) Filesystem support (this a bit more long term thing) Port to other FPGA boards
  5. I'm curious - are there pictures of those 16-bit memory boards? Watching at recent TI-99/4A repair videos made me think that for the repair purposes alone it would be useful to have a memory plugin board (for RAMs and ROMs). I could design such a board.
  6. The answer is no, like @Tursi wrote. The VDP sits actually on the high byte of the 16-bit bus. The 16-to-8 multiplexer sits between the 16-bit and 8-bit bus. There is no DMA capability anywhere, let alone from external 8-bit source to the 16-bit bus. We could build another VDP which could reside in the sidecar, with its own memory, co-processor and potential mass storage. However, this VDP could not respond to normal VDP accesses without modifying the console. Perhaps the F18A Mk2 could be used to do something along these lines too.
  7. Wow again it has been a very long while since the last update... I have worked on some TI-99/4A related items in the meantime, but with very little time. This week I received from Crowdsource my 2nd ULX3S development board. Motivated by that, I worked a bit on the icy99 core. I added two things: - support for 80 column mode (minor mod) - support for SDRAM (major mod) The 80 column mode went almost as I hoped, in accordance to what @Asmusr wrote: enabled by bit 2 of VR0 register. However, what nobody warned about was that the interpretation of VR2 (name table base address, i.e. the base address of screen tile table) also changes. I was using TurboForth in 40 and 80 column modes to test, and the 80 column picture was just static seemingly random character display. It did display 80 columns per line with the right fonts, but the text was bogus. What I subsequently learned was that apparently with the enhanced VDP 9938 (and I assume 9958) you need to set the two LSBs of VR2 high, but they seem to to not be used (i.e. interpreted as zeros). TurboForth did this, and in my first attempt the name table address changed in accordance to the VR2 change. After some head scratching and random googling I found the critical information, and now if 80 column mode is enabled, the two LSBs of VR2 are just ignored and treated as zeros when forming the memory addresses. What this means is that there are only two bits (bits 3 and 2, weights >8 and >4) of VR2 which are actually used in 80 column mode. Thus the 80 column name table can only be located to 4k boundaries in the 16K VDP RAM. With this change TurboForth now displays properly in 80 column mode. I don't have much other software to test. SDRAM: With help from @pnr who has continued to work on the ULX3S apparently for quite some time, I got SDRAM working using his controller. This is a pretty major step, and enables the development of support for extended paged memory. So I will be adding support for 1 meg of AMS memory some time soon. What I did so far was that I moved the scratchpad RAM and 32K RAM expansion from the FPGA's internal memories to SDRAM. This was mostly to test that the SDRAM interface works, and it does now. The ULX3S has 32 megs of SDRAM, so for the TI-99/4A's purposes that is more than plenty.
  8. Well I got the first version of the madness with multiple processors working. It is not anything exciting visually (yet - I hope that changes soon). I did a test program, which just fills the screen with 'E' characters in graphics mode 2. By sending a commands to the USB serial port of the cartridge, the software does the same again. Not very exciting, right? Right. It's not what, but how. What follows is a bit technical, a nice interplay of three processors: What happens in this test first is that the TI-99/4A boots normally. During boot-up the strangecart does a bunch of stuff: - the M4 processor core starts, and initializes the hardware of the MCU - then the M4 copies from its internal Flash memory a small TMS9900 cartridge program to one of the internal SRAM blocks (SRAM 3). - next the M4 copies from its internal Flash memory a small Cortex M0+ program to another internal SRAM block, and starts this program. It's what I call the "busserver" program, where the M0+ sits in a tight loop waiting for cartridge port accesses, either to ROM or GROM. If it sees a ROM or GROM read request by the TMS9900, it fetches the corresponding data from SRAM 3 and presents it to the TMS9900 on the normal data bus. And again it goes. At this point the TI-99/4A is still sitting in it's boot screen. When the user pushes a key, the normal menu is presented. One of the options is my test program strange1. If the user chooses this, the strange1 cartridge starts. There is some TMS9900 code to initialize the VDP to GM2. After this the TMS9900 writes to a magical memory location, namely >7FFE. The TMS9900 then sits in a loop, and checks if memory location >6166 becomes non-zero. If it does, the TMS9900 branches to that location. ; Signal the StrangeCart that we're ready to go! INC @>7FFE ; Write cycle to 7FFE LWPI WRKSP ; Stop here Stop MOV @VECTOR,R0 JEQ STOP ; We did get a jump vector from StrangeCart. Go there. B *R0 vector DATA 0 END MAIN The M0+ core maintains a counter of all writes to >7FFE. This counter is read by the M4 core. When the M4 notices that the counter has changed, it knows the TMS9900 is now waiting for something to do. Before even checking for the counter to change, the M4 generates dynamically some code, from TMS9900 address >6200 onwards (the following routine is executed by the M4 core): void vector_tms9900(volatile struct bus_config *bc) { static int count = 0; int start = 0x200; // Our offset for writing the program to execute. unsigned vdp_addr = 0; int cells = 0; while(cells < 768) { unsigned char *p = &__base_Ram3_32[start]; p = put_magic(p); p = put_lwpi(p, 0x8C00); p = put_li(p, 1, (vdp_addr & 0xFF) << 8); // VDP write pointer to beginning of VRAM 1/2 p = put_li(p, 1, 0x4000 | (vdp_addr & 0x3F00)); // VDP write pointer to beginning of VRAM 2/2 // Let's write as many characters as we can. while(p < &__base_Ram3_32[8*1024-32] && cells < 768) { const unsigned char e_char[] = { 0, 0xFE, 0x80, 0x80, 0xFE, 0x80, 0x80, 0xFE }; for(int j=0; j<8; j++) p = put_li(p, 0, e_char[(j+count) & 7] << 8); vdp_addr += 8; cells++; } // Ok cannot write more E chars. // Write an instruction to return back to where we started. p = put_b(p, 0x6156); // Finally send the TMS9900 to it's merry way. launch_tms9900(bc); long_wait_magic(bc); // wait until done } count++; } In the above, the function put_magic writes "INC @>7FFE instruction" into memory, put_lwpi writes a LWPI instruction, put_li writes a load immediate instructions (the arguments are the register number and the data word to be written). Finally put_b(addr) writes simply a "B @ADDR" instruction. These four opcodes are all that the test software uses for now. Thus from the viewpoint of the TMS9900, what the above vector_tms9900() function does is that is simply first inserts the magic instruction, sets W pointing to VDP, and then loads a load of stuff to the VDP. In this case that constitutes of E characters, as defined in the e-char array. The last instruction written is the B >6156 instruction, which branches back to the cartridge stub. As the cartridge space is 8K, and the first >200 bytes are reserved for the cartridge stub, so there is only 7.5K of code that can be written. Each byte written to the VDP takes 4 bytes of cartridge space in my case, since each LI R0,VALUE instruction takes 4 bytes. This is the fastest method to write data to the VDP. The payload is the high byte of value. Writing all 768 characters requires thus 768*8*4=24576 bytes of ROM code. As this does not fit into one bank of cartridge space, the M4 will write as many instructions as possible into the cartridge space. Once the space gets tight, it launches the TMS9900 with the launch_tms9900() function. The launch_tms9900() function waits for the magic variable to increment before returning. As can be seen from the above, the first instruction in the dynamically created TMS9900 code is put_magic(). Interestingly the M4 core needs to wait for this to happen for a long time, between 200 and 400 iterations of a C code NOP loop... long_wait_magic then waits for the TMS9900 to execute the whole bunch of code. This takes normally 150 000 iterations of a NOP loop on the M4 core. The magic variable increments when the TMS9900 is done writing the stuff to the VDP, and jumps to address >6156. This corresponds to the first instruction of the TMS9900 code snippet above. That took a bit of explaining, but is very simple really. It's just a sequence of events, managed by the M4 processor, effectively in this example reducing the TMS9900 into a write-to-VDP engine. In a more practical example the M4 would render the next frame, i.e. stream of TMS9900 instructions, while the TMS9900 is running the previous stream. In the function above it would be before the long_wait_magic() function.
  9. ..and I am back. No intention of being a stranger. Sorry for the long absence, not intended. It's just that I have been very busy with work etc, more stress due to numerous reasons not under my control. In situations like these I tend to use my spare time for exercise to cope with everything that's going on. In addition to StrangeCart, I'll also discuss some other random off-topic stuff I've worked a little in case someone finds it interesting. Anyway, I've only had little time to work with the StrangeCart, and I've used that time to get the rest of the software pluming on the cart working properly. This has actually been done standalone, without the TI connected. One the things I've accomplished to do is the get the USB serial device running on the microcontroller. This removes some of the wiring mess, as I no longer need to use an external FTDI USB to serial converter to talk to the MCU (view debug messages, issue debug commands). It's also now working the way I wanted: the M0+ core is spending its time monitoring and serving the external bus (i.e. the TI cartridge port), while the more powerful M4 core is handling USB code with interrupts enabled. One thing I want to implement - and this is going to require a little GPL programming (which I have never done) is to modify the standard Mini Memory cartridge running on the StrangeCart so that I could save the 4K SRAM contents into the MCU's flash memory, to achieve the same rentention capability the original Mini Memory had with the battery backed RAM. The other retro computer related stuff I have done is some kind of random rambling on different things: on the TI-99/4A front I noticed that someone has ported my EP994A TI-99/4A FPGA core for the MIST. I tried that out, seemed to work well. I really want to port my newer and better organised icy99 core for the MIST as well. As a small exercise and to see that my toolchain with Altera FPGAs is working, I did synthesise the MIST TI version myself as well. Kind of strange to get back to my own code, that someone else has ported for the Altera platform. Another project I also built (this was almost too fast to enjoy) was to assemble the RC2014 kit I won back in 2016 for the retrochallenge. I can't believe it took me this long - after moving to our current home this summer I found the kit. It wasn't really lost, it was where I left it, but I kinda forgot that I had it. Anyway I enjoyed building it - this actually is probably the first Z80 system I have built, even if Z80 was the CPU I first learned assembly programming on. The first time I applied power, it worked. In its current form is one of the base versions of the kit, having just backplane, Z80 CPU, 32K RAM, a 64K ROM chip and serial interface. Now that all of this was working I did order from Tindie a couple add on boards, as I want to make my system CP/M capable. The RC2014 is such a simple and lovely system, I feel I am almost obligated to next build a TMS9995 CPU board for it...
  10. Thanks for the comments @Asmusr and @Lee Stewart. I quickly looked at my code, and currently it is only looking at the 3 LSB bits for register selection, but expanding that is trivial. I actually have already extended the interface to my TMS9918 last year, so that instead of the VDP only decoding one bit from address bus, I have a 8-bit address bus and support a bunch of 16-bit registers which can be read by the CPU. For reading, in addition to reading from address 8800 and 8802, with my VDP there are 16-bit wide registers in the range 8880..88A0, enabling reading back all registers, with proper bit shifts to have the direct VRAM addresses for all tables, and additionally the VRAM pointer can be read, the screen refresh row and column, whether the display is in the blanking area, plus some debug stuff.
  11. Thanks @Asmusr this is what I was looking for! I would initially be looking to implement a super simple 80x24 mode - but still something that at least some software can directly utilize, it can be made fancier later. It would be a nice addition to the icy99 code base. If it as simple as adding one more bit, this should not take long at all to implement (if I had some time, but that is another story).
  12. I bought mine directly from them, by e-mailing the contacts I got from @pnr. There is also crowd supply campaign at https://www.crowdsupply.com/radiona/ulx3s, and I did order another one probably around 6 months ago. Their delivery schedule has slipped, and is now supposed to be in August. I believe you can order directly from there. And as I wrote this while catching up the thread, I see that the question was already answered... Self assembling the ULX3S is not an easy option, as the ECP5 FPGA is BGA chip, and there are a ton of other components on this board.
  13. Thanks for sharing, sounds interesting. That sort of WP manipulation possibilities are certainly unique to the TMS9900 and TMS99000. Luckily TMS99000 instruction set seems to also add support for normal stacks
  14. I just remembered that I wanted to ask how much 9938 VDP features I would have to add to support 80 columns text mode? For example to support fbForth or TurboForth with 80 columns? I hope that this would not require too many additions. Internally my VDP core already renders pixels to a 512 pixel wide buffer, so the underlying engine has been partially designed with 80 columns in mind. To be more specific, I guess what I am asking is that what extended registers I would need to add to support simple 80 column mode?
  15. A quick update: I have been working together with the ULX3S developers to make the icy99 implementation on the ULX3S FPGA board better. emard and lawrie have been testing the TI-99/4A core. emard added support for the screen overlay feature he is using with many other retro computer implementations. The cool thing is that this is running on an ESP32 micro controller running micropython. The overlay screen already allows loading of ROM images from SD card. When I have time I will continue to work on the core itself, for instance to add support for disk subsystem with the ESP32. I can probably reuse some of the code I created for my ET-PEB project. In the process I accidentally broke my FPGA board, the primary micro USB socket became loose and broke the PCB pads in the process. Luckily I could fix this with a new micro USB connector, as it is the same connector used in my StrangeCart board. Some pictures are here in the gallery.
  • Create New...