apersson850
Members-
Content Count
1,063 -
Joined
-
Last visited
Content Type
Profiles
Member Map
Forums
Blogs
Gallery
Calendar
Store
Everything posted by apersson850
-
I agree. I've never used the SAMS thing. Does it bank switch based on writing something to a specific memory address? My own internal to the console 64 K RAM memory design bank switches by CRU bits. I wanted a CRU bit to control the hardware wait state for VDP access as well, and since I have eight output bits, I couldn't associate one bit with each 8 K RAM segment. What I did was associate the 8 K RAM segments between >0000 and >9FFF with one CRU bit each, and then the remaining 24 K RAM at >A000 to >FFFF with one single CRU bit. When all CRU bits are reset to zero (default state), their output is inverted for the memory segments that make up the normal memory expansion. Thus the memory at >2000 - >3FFF and >A000 - >FFFF is enabled, but the rest is disabled. This means the console starts normally, with 32 K RAM expansion, supporting two instead of six clock cycles for accessing a word, available. By setting memory segment bits, the segments which normally are not RAM page in RAM in their address space, but the segments that normally are RAM expansion instead page out their RAM, and let you access a standard 32 K RAM expansion, if there is any. The advantage of that CRU bit selection is that no special memory address need to be reserved to control bank switching, and you can flip all the switches with one LDCR instruction. As long as you don't switch yourself out of the address space, it's fine to do that. Personally, as far as we limit ourselves to memory expansions built inside the console, I think my design is superior to everything else I've seen. It gives you a fast 32 K RAM expansion. I makes it possible to get a 64 K contiguous RAM in the machine, if you want to. But you can't access the VDP or any other such thing as long as you have all that memory enabled, of course. You can use the 8 K RAM then covering the memory mapped devices as a buffer, for example. While making RAM possible everywhere, it still allows you to access all standard devices in the machine. Even the standard 32 K RAM expansion can co-exist with this internal memory, providing another 32 K RAM for buffering, making a small RAM disk or whatever. Being able to copy console ROM to RAM makes it possible to redefine interrupt vectors and such stuff, if you want to make full use of the interrupt capability in the machine. For a terminal emulator, for example, using the TMS 9902 interrupt and servicing it in the fastest way possible. Nowadays 64 K RAM extra isn't much to brag about, but remember that this design comes from around 1985. If internet had been available at that time, then maybe enough people would have liked it enough so we could have had an interest in writing software using it. But there are only three consoles I know of that has exactly this same modification.
-
Fair enough, now I see what you were thinking about. I'd call it chip select, or perhaps bus enable, but then I'm not a native English-speaker, so it could be me. Anyway, should anyone with beginner level competence read this, they now know the difference between enabling the basic functionality of the chip vs. enabling the electrical interface of the same. Do you guys (gals?) here see it as a common thing to write an entire application in assembly language? I'm asking because I don' think I've ever done that on the 99/4A, not for anything useful, at least. OK, some small program which actually was intended to provide stimuli to drive hardware circuitry I've designed, to facilitate computerized testing of the same, that I've done. But even then, frequently the assembly part was a subroutine called by a program written in another language. When doing this for a wider audience, the "other language" was frequently Extended BASIC. Using some CALL LOAD statements to install a memory image file loader, which then loaded the real assembly program, made these designs useful even if you had no other expansion than the 32 K RAM. You could still load such a program from cassette, even if it took a special tool to create the files on the tape. For my own use, it was almost always assembly routines called from a Pascal program. Unlike in BASIC, as soon as the assembly procedure was declared as an external procedure, you didn't see any difference compared to a Pascal procedure. And since all normal memory in my machine is just as fast as the RAM PAD at >8300, I didn't have to bother with thinking about where to load the programs either. I've designed my own embedded computers, based on the TMS 9995, but that's something else. They were programmed entirely in assembly, due to the lack of a suitable compiler.
-
I've just used the Zilog Z80 a little, in a Festo industrial controller, actually. But I've used the predecessor Intel 8085 quite a lot. Almost everything the 8085 could do, the TMS 9900 can do in an easier way. Frequently several instructions were used to do arithmetic with more bits than the basic eight, functions the 9900 often can do in one single instruction. You need to implement a stack, but that's easy enough using a register and the indirect (with autoincrement) addressing modes.
-
I have a memory of trying to buy that book back in the 80's, but I had to get the 9900 Family data book instead. The one you mention wasn't available where I looked.
-
Now I've not really used my TI 99/4A in recent years, but I know that during the "golden years", when it was used every day or so, the game Tennis was the only one that definitely didn't work when running on my console. Which has 64 K RAM on 16-bit bus without additional wait states, that is. The players, obviously made up of two sprites placed adjacent to each other, split in half, with the legs running one way and the body floating away in the other direction. My memory modification allows me to simply disable it. When I do, the normal 32 K RAM expansion becomes visible, if present. When run like that, Tennis plays as it should. But I also added the ability, in hardware, to run 16-bit wide RAM but enabling a wait state when decoding a VDP access only. Thus even code that leaves out the NOP (or other time delay) between VDP access instructions runs properly. But the game Tennis becomes virtually unbeatable... Enabling/disabling of memory and the hardware wait state generation is done via I/O bits (CRU, base address >400), so it can be done under software control.
-
It's misleading to say that using any of these addresses will "enable" the VDP. You don't need to do anything special to "enable" it. It will do its task of displaying the screen anyway. But the VDP have these ports available to load an address, read data from it etc. at the specified addresses in CPU address space. By writing data to the VDP, or actually to the RAM memory managed by the VDP, you can change what's shown on the screen. The only "enable" you can do is that you can actually turn off the screen, displaying only the backdrop. You do that by writing to a register in the VDP, an operation which is the only one that doesn't involve the RAM in VDP address space. Unlike VDP RAM, the VDP registers can't be read from, so you need to remember what you store in them. That's why the operating system in the 99/4A keeps a copy of the VDP register where the screen blanking time out is done, so it can restore it when you press a key to cancel the timeout.
-
There you go. Didn't know about the different multiplexing between the external hardware in the 99/4A and in the CPUs with 8-bit data bus. Are the microprograms for these processors published, or just some general comments? In some data manual? Don't remember seeing them in my 9900 data book. I've microprogrammed some processors a long time ago, so it could perhaps be interesting. Returning to data processing, the addressing modes available in the 9900 can be put to good use there. So perhaps some general hints about how to handle such structured data would be appropriate here.
-
That makes sense, since there's no clear byte, and you don't care about the prior content of an address you're going to set to zero anyway. Alas, it's not like that either. CLR does access memory three times. Which is one too many for just fetch instruction and zero destination. If you leave the interrupt on, then the sequence between setting up a VDP address and writing to it, by your code, may be interrupted. If then the interrupt service also writes to the VDP address register, your setup is destroyed. But you don't know that when the control is returned to your program, so you'll keep on writing but to a different location than you think.
-
Well, the read before write is what I discussed, but perhaps you were writing at the same time. Another thing is that the general procedure used by MOV and MOVB are actually the same. Both read before write, since they use the same memory access principle internally in the CPU. They both have four memory accesses, i.e. fetch instruction, fetch source, fetch destination, write destination. The only difference is that with MOV, the entire destination is replaced by the source, with MOVB only half of it. This thread has mainly been about games, and such games that focus on nice screen graphics. Nothing wrong with that, but have you considered adding anything about data management and processing? Games need to handle data too, especially if they are supposed to be clever. Intelligence frequently involves allocating, creating and traversing decision trees. Is it perhaps time to add something about how to handle not just single integers, but arrays, structures, linked lists, content addressable arrays and such stuff? Principles for evaluating a game situation and selecting the next move, depending on what the player does? This could be interesting not just for turn-by-turn based games, but also for action games. Provided you can do the coding efficiently enough, something there has been a lot of focus on in this thread.
-
I can add that MOVB always writes a word, not a byte. If you do MOVB R1,@100, then the CPU will write to the bytes @100 and @101, placing the most significant byte of R1 in @100. If you instead do a MOVB R1,@101, the CPU will write to the two bytes @100 and @101, placing the most significant byte of R1 into @101. To prevent destroying the other byte, the one not explicitly addressed, the CPU actually reads the word @100, modifies the left or right byte of that word and writes it back to @100. The TMS 9900 has to work like this, since it's a byte addressable device that can only address 16-bit words. After all, it has a 16 bit wide data bus, but only a 15 bit address bus. Thus it can actually access 32768 words, not 65536 bytes. Bytes are handled by manipulating one half of a word. Normally, you never need to think about this, as it's transparent to the user. But there is one instance when it's important, and that's when addressing memory mapped devices that are auto-incrementing their internal address. Like the VDP. Since it increments the address on both a read and a write, you can't have the VDPWD and VDPRD decoded on successive bytes. If they were, the read-before-write concept in the TMS 9900 would increment the address when fetching the word that contains the byte that shouldn't change and again increment it when storing the word that contains the byte we want to store. Another thing that's not important for programming by itself, but could be good to know if you talk to other programmers, is that the programs in this thread aren't compiled. They are assembled. I understand that everyone knows what's implied here, but using the correct terminology is sometimes important, when sharing ideas. If somebody doesn't know the difference, then assembly is the process of translating mnemonics like MOV R1,R4 to the binary code this instruction has, in this case >A044. But there's always a direct relationship between the instruction as written, MOV R1,R4, and the instruction assembled, >A044. Compiling is instead the art of translating a language the CPU doesn't understand to assembly language, so it can be executed. This is much more complex, since the same statement, say a := b+c; can be compiled to an almost infinite number of instructions. Just to see what it can be, lets assume we have this code: var a,b,c: integer; begin a := b+c+2; end; If we assume variables are allocated at VARBASE and we have R9 as pointer to this variable area, the compiler may generate VARBASE BSS 6 EQU ERECP 9 LI ERECP,VARBASE MOV @2(ERECP),*ERECP A @4(ERECP),*ERECP MOV *ERECP,R0 AI R0,2 MOV R0,*ERECP Another compiler may generate code that looks like this VA DATA 0 VB DATA 0 VC DATA 0 MOV @VB,R0 A @VC,R0 AI R0,2 MOV R0,@VA Or the first one could be VARBASE BSS 6 EQU ERECP 9 LI ERECP,VARBASE MOV @2(ERECP),*ERECP A @4(ERECP),*ERECP INCT *ERECP Note that it's never sure exactly what the source was when the program is compiled, but the source can be reconstructed instruction by instruction by disassembling an assembled program. You lose the label names, but you can see the instructions exactly as they were written. In the first compiled example, the source code var d,e,f: integer; begin d := e+f; end; would give the same result. It's not essential to know about the difference to be able to write assembly language programs, but it's always good to know the distinction.
-
Because they thought it was reasonable that you at least wanted to read from one file, do processing and store in another at the same time, maybe? Not too rarely do you also need to reference something else, or have a temporary file open for storage, while doing the work you are doing. I don't know, I'm just guessing.
-
Back in the days, TI bragged about that since every peripheral has its own DSR in ROM, accessories didn't need any of your valuable RAM. But that's not 100% true, since the disk controller does need file buffers. These are allocated in VDP RAM, as that's the only RAM all machines have. When using BASIC, you can set up the system to allow from one to nine files open simultaneously. The more you want, the more memory will be used. The CALL FILES(x) command is used for this. Default is CALL FILES(3). Thus the beginning of the buffers for three files is the most common end of available VDP RAM.
-
Yes, that's what I referred to. I associated this with a similar behavior in the UCSD p-system. Normally, when you start up the TI 99/4A, and have an active p-code card in the machine, the p-code card will never return control to the machine after executing it's power up routine. That's why they gave the p-code card CRU base address >1F00, so that it would always be the last one to run. But if you let the p-code card start, then stop it by the Halt instruction, it will load "NO" in a certain memory location in 8 K RAM. If you then reboot the 99/4A, the p-code card will find "NO" at this location and then return from the power up routine, without taking over the console. Back when I used the 99/4A a lot, I knew the correct CALL LOAD(xxxx,78,79) from BASIC to disable the p-system, but I don't remember the address any longer. The p-system only stores "NO" in one place, to prevent a start. This has nothing to do with the opcode for JMP 0, sometimes called NOP.
-
I'v never had that much fun with any other computer than the TI 99/4A.
-
But each time you run a BASIC program, the prescan starts and all that housekeeping takes place. That would not happen if you create a "false" return to BASIC, as in such a case, it would presume this was already done.
-
Floppy disks only accessible part of the time?
apersson850 replied to adamantyr's topic in TI-99/4A Computers
If you go through the drive number assignment in a systematic way, then you'll get rid of all such possible spurious reactions. -
Floppy disks only accessible part of the time?
apersson850 replied to adamantyr's topic in TI-99/4A Computers
Cables with twists shift the drive select line one step for each twist. The idea is that if you have all shifted cables, you strap all drives to react as number zero. On the other hand, if you have all straight cables, you have to strap the drive select in the drives as DS0, DS1, DS2 etc. If you think carefully about what you do, you can mix the two. If you have, from the controller, a straight cable, a twisted one and another straight cable, to connect three drives, you have to strap the drives like this: The first drive is set as DS0. It will react as DSK1. The second drive is also set as DS0, but due to the twist everything is shifted one step, so it will react as DSK2. The third drive is set as DS1, but since there's one (but only one) twist on the way to this drive, it will react as DSK3. If you by accident map more than one drive to the same number, then you'll get problems. -
It seems all who did the MIPS math are aware of that first you get a figure for average instructions, but second you have to look at examples that do something useful, as the actual number of instructions executed can be quite different, depending on the CPU architecture. It's also obvious from the code segment Lee showed above that there are quite a lot of DIV and MPY in the floating point multiply and divide instructions. In a computer like the TI 99/4A, you sometimes can't optimize for speed only, but also have to optimize memory usage. And, as everybody says, the environment the TMS 9900 is stuffed into in the TI 99/4A isn't the most efficient... I can measure a speed increase of about 110%, if I compare code running with both workspace and code in normal, 8-bit wide RAM expansion, compared to when it runs entirely in my own RAM expansion design, which has 16-bit wide, zero wait state memory. Now most code is designed to have at least the workspace in RAM at >8300, which means the speed up isn't that dramatic, but sometimes you want to use memory elsewhere, and then it's really important.
-
I used to lift them all one pixel, so I could get true descenders. They do hit a "tall" letter on the line below, but I live with that.
-
That could be. The floating point instructions in the TI 99/4A are optimized towards accuracy, not speed. It uses radix 100 coding instead of binary, to avoid rounding errors. Divide or multiply by 100 is frequently called for in such math routines, so maybe they do use that. I've not studied these routines in detail. I just know that the 99/4A computes floating point numbers to significantly better accuracy than most other computers. This is probably due to the influence from the calculator division, since TI calculators, like the TI-59, had good math accuracy. I didn't think about the block move instruction in the Z80. That for sure makes a difference in favor of the Z80. I've mainly used the Intel 8085 back then, not the Z80.
-
Where did the "4" come from in 99/4A?
apersson850 replied to matthew180's topic in TI-99/4A Computers
That's just because those who wrote them didn't know about the mini computer series. It's as simple as the fact that TI used to put an "A" Bafter the first remake of a product, a "B" after the next revision and so on. Both the computer and the VDP got an update, so they both got an "A". It's just a coincidence that they both got an "A" at the same tme. Look at the TI 990/10 for example. There's a TI 990/10A too, but just because they went from multi-chip CPU to the TMS 99105. No "A" in any chip there. The TI 990/4 uses a TMS 9900 CPU and has 56 K RAM, to start with, so it's in about the same level as the TI 99/4 and 99/4A. Thus selecting /4 makes sense, and gives room for both more powerful (TI 99/8) and cheaper (TI 99/2) models. -
Still, MPY and DIV are efficient, compared to doing it the shift and add/subtract way. The large amount of "registers" in the TMS 9900, plus the memory to memory architecture also implies that sometimes a single instruction in the 9900 do the same as several in an 8-bit CPU. Like Load byte to accumulator from memory 1L Add byte in memory 2L Store accumulator in register 2L Load byte to accumulator from memory 1H Add one if carry Add byte in memory 2H Store accumulator in memory 2H In the TMS 9900 this is done by A @memory1,@memory2 It doesn't matter if you can do twice the number of instructions in the same time if you need seven. Now some 8-bit CPU can do 16-bit arithmetic, which makes them more efficient, but the principle stands.
-
Which implies that it's fairly equal then, considering the more efficient instruction set in the TMS 9900.
-
Maybe that comes from that they used GROM chips to store the code. The same kind of chips used to store GPL. But on the p-code card for a different purpose.
