apersson850
-
Content Count
1,063 -
Joined
-
Last visited
Posts posted by apersson850
-
-
Yes, you are right in that there are applications where you can get a result from each invocation of the function, like plotting a point or line on a screen. But in the cases when there is no result until the recursion has ended, the whole thing is pointless if there's no condition to exit the recursion. If there isn't, then you'll never get the result.
Technically, it's possible to end recursion on a non-predetermined condition. If you are for example using recursion to evaluate moves in games, you can use elapsed time or amount of free memory, or both, as conditions to end the recursion. This means that an application can be written in such a way that it will play a better game if it runs on a faster computer, and/or one with more memory.
-
So if the registers really do contain garbage, when the loop starts, then that implies that you will move a random number of words from one random place in memory to another one. If you are lucky, and the number of words aren't very many, you may get away with nothing happening at all, but most likely, the program will derail.
By "lucky" I mean if you happen to move 123 words from some place to monitor ROM, which can't be changed, then nothing bad happens. You'll simply try to write to memory that will not change. So nothing bad happens, since actually nothing at all happens.
In the worst case, you overwrite the code you are right now executing, doing the digital equivalent of suicide.
-
1
-
-
On 5/22/2020 at 4:52 PM, RXB said:But then TI Basic with Assembly Support is slow and very very limited compared to XB with Assembly support.
I agree. When I was using BASIC on the TI, it was Extended BASIC supported with assembly to do things not available, or too slow, in BASIC.
-
2
-
-
15 hours ago, jrhodes said:1 A=A+8
2 GOSUB 1
(RUN, then PRINT A for aprox. free memory)
Does that count as recursion in BASIC?
No. Recursion implies a function that calls itself, and, when some condition is true, just returns.
More like this (just note that I wrote this very quickly, so I'm not sure if there's any glitch in my thoughts).
ON ERROR GOTO 200 GOSUB 100 PRINT A STOP 100: A=A+8 IF NOT ENDFLAG THEN GOSUB 100 200: ENDFLAG = -1 :: RETURN -
You can do recursion in BASIC, using GOSUB, but you have to maintain a stack with local data yourself.
Repeating something, FOR - NEXT, isn't recursion. It's iteration.
Many recursive algorithms can be implemented in other ways, but frequently not that elegant. Like traversing a tree. That's as simple as can be with a recursive algorithm, but gets pretty complex using other methods.
The penalty for recursion is the overhead of each function call.
-
At least the built-in operating system on the p-code card's "GROM disk" can be upgraded. You can place an upgrade of a segment of the operating system on the root disk. If you do (this never happened), then the system will use that part instead of the fixed one in GROM on the card.
But if TI BASIC didn't occupy two GROMs in the console, you could have had higher capacity modules. Perhaps they designed a nice size for the modules, found they could put five GROMs in there and then put the other three in the console? Since they can address up to eight of them, in an easy way.
QI most certainly is an abbreviation for Cost Reduced, nothing else. But that doesn't sound so cool to marketing.
-
Yes, that's one of the weird things. Was that diode really that expensive not to be included in the first place?
-
Just remember that the longer the shift, the more clock cycles it consumes on the TMS 9900.
But it doesn't match MPY anyway.
-
2
-
-
12 hours ago, Lee Stewart said:TI Forth did something like this for Forth work disks in that the disk was set up with a single file (SCREENS) that occupied the entire disk. TI Forth did not, however, actually care whether this was done. It was merely a device to discourage using that disk for anything else. I believe, as long as the first sector (VIB) was not touched, TI Forth could use the disk to read/write screens with impunity. Of course, avoiding writing to the first screen (block) avoided the first four sectors, the first three of which included the VIB, FDIR and the SCREENS file’s FDR, but I digress ....
It's identical to how the p-system operates.
Unlike many other higher level languages, Pascal provides three different access methods to storage.
- Typed file handling. Similar to the file handling in BASIC, but with better data types. Uses commands like read, write, put, get and seek.
- Untyped file handling. You can read/write any p-system file, without knowing what the data represents. Access is within the area the file occupies on the storage media. Uses commands like blockread and blockwrite. Similar functionality for the native file system is available as subprograms on the disk controller card, but not directly accessible from BASIC.
- Direct access. Will read/write any p-system block on a disk, for example. The blocks are 512 bytes long, so two sectors each. The p-system's block 0 starts at physical sector 4, since it assumes the BIOS requires the first four sectors to be protected. The p-systems disk catalog starts at block 0 (sector 4). Uses commands like unitwrite and unitread. You can set a control bit which allows access to physical sectors as well, so you can read/write to sector 0. By using unitstatus, you can get information about the physical sector size, number of sectors per track and number of tracks on a disk.
Since these commands are available in Pascal, it was for example possible to write a program which reads, or writes, a DIS/VAR 80 file in the native format directly in Pascal, without the need for any assembly support. Thus a converter between Pascal's text files and DIS/VAR 80 files could be written. Both the file itself and the directory entries could be handled from Pascal.
That's of course possible from Forth too, since it's much closer to assembly from the beginning.
-
4 hours ago, Rossman said:well, I now know that is not true, it costs me 5 bytes.
Words, not bytes. So ten bytes.
I still think this isn't too complex a main program. It's slightly more complex than the original, but more realistic.
program sortfile; uses magic; var fName: file of integer; begin (* sortfile *) getFileName(fName); if openFile(fName) then readFile(fName); sortFile(fName); writeFile(fName); closeFile(fName) else writeln('ERROR - File not found'; end; end. (* sortfile *)Provided the unit "magic" contains what is needed, it even kind of works.
-
-
It creates a dummy file, which occupies the entire disk. The p-system dosen't use any file IO inside that file, but to the native operating system it looks like the disk is full. This file is called PASCAL.
So if the first normal file entry is a file with that name, then the disk is likely prepared for use by the p-system.
The p-system then creates its own directory and files inside the space occupied by the dummy file.
-
And in Pascal
begin doit; end.And something similar in all other languages. Which doesn't give you any idea of what's going on.
But something like this does.
begin getFileName; openFile; readFile; sortFile; writeFile; closeFile; end.Not too big to be confusing, not too small to tell nothing.
-
2
-
-
As could be seen from the code example I posted, I tend to make the main program easy to overview, so you can get an idea about what's being done. Then the details are in various subprograms. At least in more complex projects. If the whole program is a hundred lines, it doesn't matter much.
By the way, when a procedure or function is called, an activation record is created on the stack.
An activation record in the UCSD p-system consists of the following parts:
- Function value, if it is a function. Four words for a real result, otherwise one word.
- Space for parameters, if any.
- Space for local variables, if any.
- Five words of Mark Stack information.
So the absolute minimum amount of stack space that's allocated is five words, for a procedure with no parameters and no local variables.
<nerd mode>
The five mandatory words are these:
- MSSTAT points to the activation record of the lexical parent.
- MSDYN points to the activation record of the caller.
- MSIPC points to the place from where it was called. Segment relative byte pointer (since p-codes are bytes, not words).
- MSENV pointer to the calller's environment record (E_Rec).
- MSPROC procedure number of the caller.
An activation record is what I just described. A block of data that's local to a procedure.
An environment record contains information about a compilation unit's global data and segment link.
A compilation unit is the main program or any unit it uses.
A segment is a part of the program that can be removed from memory, and later reloaded from disk, to allow running programs that don't fit in memory all at the same time. A compilation unit is always a segment, but a compilation unit may be split in several segments.
</nerd mode>
-
1
-
The p-system uses only the read/write sector subprogram. It doesn't expect to see anything special on the disk, as long as the sectors can be read and written.
Getting a physical disk ready for use with the p-system is a two step process.
- Format the disk. This is something the p-system can't do by itself, since it's totally machine dependent. There's a special program, dformat, supplied for this. It's not very good, so I wrote a better myself. But this step is not needed for a RAM-disk, since you can always read a sector from it.
- Install the p-system's file directory. This is something the p-system's Filer (disk manager) utility does, using the Zero command. This will write the p-system's disk catalog for an empty disk on the drive. For a RAM-disk, it's the only thing needed.
Then there's a special patch that needs to be installed in the p-system. That's because the p-system on the TI is limited to three drives. That's of course because only three drives were supported by the original disk controller. The p-system itself can handle six, but the system's tables need to be patched with the proper values first.
Due to the p-systems pre-scan of available units, you can add a fourth physical drive just by copying the information from one of the first three to the location for the fourth. That works because all four physical disks are accessed via the same DSR, on the disk controller. To add a RAM-disk requires creating space for a new drive definition, since the RAM-disk will have a different CRU base address. You remember I wrote before that the system pre-scans and stores the CRU address as well as the entry address for the sector read/write subprogram in its data structures. Since with a RAM-disk, there will be multiple devices providing access to blocked data storage units, the system also need different content in the tables for them. But this is a patch to the p-system, and has nothing to do with the DSR on the RAM-disk.
Step 2 above is the one where the read/write sector subprogram is needed, and then for all subsequent disk accesses, of course.
When the Zero command is run, you tell the system how many blocks are available on the drive. One p-system block is two sectors. 16 Megabytes is the theoretical limit for how large a p-system volume can be (32767 blocks). But you never want them that large with the IV.0 version. If the RAM-disk is gigantic, then it would be an advantage to allow two virtual drives on it.
The p-system on the 99/4A is version IV.0. Later versions, from IV.1, allow for subfolders. They are called subsidiary volumes, and reside as files on the host volume. Thus if you have a hard drive, you can create a master volume with up to 77 subsidiary volumes on it. Subsidiary volumes can't be nested, though, but each can be 16 Megabytes. These versions also allow for more than six base volumes.
Remember that the p-system was created when drives were small (a few hundred kilobytes at the most), so already the limit of 16 Megabytes was pretty farsighted. That they would cater for today's Terabyte drives would be a bit too much to expect.
-
3
-
Most hardware actually does work with the p-system, if the hardware maps any function the p-system can use. But if you add a Triple-Tech card, you can't use the real time clock just like that, since the concept of a real time clock doesn't exist in the p-system.
I have understood that the ROS does it the way it does to save a few bytes. That's why I called it a clever trick.
The benefit of making the DSR compatible with the p-system is that you wouldn't have to exchange the DSR each time you switch between the operating systems.
I have no problem with that it's compatible with the standard OS only. I happen to know enough about the system to find out why it fails with the p-system, and fix it. But I have previously sent code to people to make their RAM-disk work with the p-system, but it failed. This was before I had one, so I couldn't understand why.
That's why I'm spreading this knowledge and insist on that the manual should mention this issue.
-
2
-
-
On 5/13/2020 at 6:01 AM, Rossman said:I've found it not too difficult to get a stack overflow in Pascal by having too many constants.
When I had the stack overflow problem a few years ago, I solved it by replacing my constants with values in arrays.
That's a bit surprising, since the compiler actually handles constants by creating an array with the constants, then indexing into that array when it references a constant.
-
I don't know how today's system do this. Today, I use computers to support my tasks and save time. Back with the TI 99/4A, I was interested in how everything worked under the hood, so I spent a lot of time investigating that.
-
2
-
-
On 4/14/2020 at 1:23 PM, Vorticon said:Have you tried a packed array of char just for kicks? I've never tried it but there is no specific mention against its use as a selector in UCSD Pascal.
Well, there is, at least implied, since only simple types can be used, and an array (of which strings are a special case) isn't a simple type. Neither is a real. A simple type consists of a limited number of elements with a well defined order. Like characters and integers, but also enumerated types.
In a computer like the 99/4A, there is of course a limitation of how many different real numbers you can represent. Resolution puts a limit on that. The same goes for strings, since they can't have an unlimited length. But these are implementation dependent across different p-systems. At the time of the p-system, integers were 16 bit though, so the number of possible values was known and well determined.
-
2
-
-
42 words, actually, since both the values 49 and 90 have to be included. This is done by the compiler to make case selections efficient, time-wise.
The requirement to use simple types, like integer and char, does extend to enumerated types. So this code is valid:
type statetype = (stopped, running, idle, feeding, disabled, unknown); var state: statetype; begin case state of stopped: stopCode; running: runCode; idle: coffeeTime; feeding: foodTime; disabled: doNothing; end; end;The jump table will occupy five words only. Had the state unknown been declared between stopped and disabled, there would have been six words. That's equivalent to using the integers 1, 2, 3, 4 and 5 versus using 1, 2, 4, 5 and 6.
-
You don't. It's normal for Pascal implementations of the time to allow only simple data types for the selection variable. This is because the compiler makes a jump table with the different outcomes, and that becomes too complex if strings can be used for the case variable.
You also need to be aware of this when you select your constants to compare with in the case statement.
Compare these two case statements.
case a of 1: bla; 2: blah; end; case a of 1: bla; 20: blah; end;They will do the same, but the first requires a case jump table using two words of memory. The second will require 20 words of memory, where 18 of the words will just be unused fillers! Thus if you enter the two constants 1 and 20000, you'll get the dreaded ERROR: STACK OVERFLOW*REBOOT
On the other hand, this overhead makes the case statement very fast.
The same principle applies if you use char constants, with 'A' and 'B' versus 'A' and 'Z'.
Now when you know how the compiler implements this statement, it should be obvious why an array can't be used. Neither can real numeric values, but only integers.
-
On 5/8/2020 at 6:38 PM, InsaneMultitasker said:I do not consider reliance upon this address to be a 'trick'. The addresses 83d0-83d3 are defined to be used for the ROM and GROM searches.
Well, I do. Now I haven't dug up the DSR interface manual now, today, and it was many years ago I read it. I did that since I've made some PEB cards myself, and had to write DSRs for them.
But if I remember correctly, the DSR manual does not specify this address to be used for searching ROM and GROM links. I think it's the operating system in the console that specifies that this address is used when searching for the DSR. Thus it's a "trick", because the DSR makes assumptions about which operating system is calling it. Which it shouldn't, of course.
Now, again, it was decades ago I read these documents, but from memory this is the reason for incompatibility with the p-system.
I'll try to help you with examples of how to make it work. As I wrote, I did make a simple DSR for the RAMdisk that does work with the p-system. I'll just have to find the time right now to get that code to you.
I can give you the background to why it doesn't work with the p-system, though.
"Normal" DSRLNK calls, from the standard operating system, are as stupid as possible. Each and every time, the machine assumes it has no knowledge about where to find a requested device, so it starts scanning through the entire CRU range devoted to DSRs in ROM, and looks for the device name's entry, to find the entry address. When they did the p-system, they realized that although this scheme is very flexible, its also inefficient. To keep the flexibility, but reduce the inefficiency, they made the reasonable assumption that DSR entries will not move around while the machine is running. Now there they made an assumption which could be dangerous now, since we have DSR memory space in RAM. So in theory, we could move a DSR entry from one call to another.
Anyway, at that time, all DSR code was in ROM, so it wouldn't move or change while the machine was running.
Since the p-system is an operating system by itself, it already has a list of devices it interfaces with. These devices are like CONSOLE:, PRINTER: and various blocked devices (disks). The blocked devices have hardware numbers (#4:, #9: etc.), but also get the names of the disks in them. Thus a blocked device could be SYSTEM:, DEVELOP: or RAMDISK:.
When the p-system creates its list of devices (or units, as they are called), the list look the same in all p-systems. The commands (input, output, reset and so on) are also the same. In the next layer down, there is the BIOS, which is different depending on the hardware. In the 99/4A, the BIOS reduces the overhead in calling DSR code by splitting it in two stages. At boot time, the system looks for the proper DSR to call for each unit, to find the mapping between a p-system unit and the current hardware's implementation of it. Once the device entry is found (for example RS232 or disk sector read routine), the CRU address and the memory entry address for that routine is stored in a table. This table is located in the SYSCOM area, which in the 99/4A is resident in the 8 K RAM. Note that at this time, the DSR is not executed. It's just located.
Then, when the system continues to boot, and eventually releases control to the user, the appropriate DSR to call when a certain p-system unit is referred to is already known. Now the CRU and memory addresses from the table are used to enable the DSR and jump directly to the DSR code.
This works fine when the DSR has a separate entry for each subprogram/operation. But in the ROS for this RAMdisk, all subprograms have a common entry. Then, in the code snippet I posted before, at TEST, the address in the RAM PAD is inspected to determine which of the subprograms was actually called, and then the correct routine runs from there. But since the p-system already knows about what to run, and uses the RAM pad in a different way (the main PME workspace is at >8380, not >83E0), that address is in the table in the SYSCOM area, not in the console RAM. So whatever the DSR finds at 83D0 has nothing to do with what to run from the DSR code.
-
1
-
-
On 4/6/2020 at 8:07 PM, InsaneMultitasker said:As we get closer to more hardware being released and more interest in the ramdisk, there is now a Github site in process for the source code and other materials. A Wiki has also been created to cover things at a high level. I posted this in the HRD4000B topic as well.
Wiki:
https://github.com/horizonramdisk/Horizon-Ramdisk-ti994a/wiki
Source:
https://github.com/horizonramdisk/Horizon-Ramdisk-ti994a
I had a look in the source code and noticed that this DSR will still not work with the p-code card. I understand that the user base for that system is too small to bother with, but it would be good if the wiki and/or code contains a note about that the ROS is compatible with the built-in operating system, but not with the p-system.
If anyone with a p-system would ever be interested in getting it to work, like I was, here's the reason for the incompatibility.
The p-system only uses the sector read/write in normal operation. This is the part of the ROS where such a function call is decoded.
TEST LWPI HIWS Load our workspace MOV @SRHADR,R1 Get pointer to CALL table MOV @6(R1),R11 Get address of CALL program MOVB @>834C,R2 Get drive # for this CALL CLR @VDPCPU Assume VDP transfers COC @ST0,R2 Check if MSBit is SET JNE WNUMB Nope, so use VDP transfer mode SETO @VDPCPU Yep, so switch to CPU transfers SZCB @ST0,R2 Clear flag to get correct drive #On the second line, the MOV @SRHADR,R1 is supposed to get the address of the found subprogram in the DSR. The address SRHADR is >83D2, which works with the console's operating system, when it uses its DSRLNK routine to find the proper DSR. This is a clever trick to make the DSR code smaller.
The p-system, however, uses a more efficient method to call a DSR than the regular DSRLNK routine does. With this method, the address of the desired program in the DSR program isn't available at >83D2, so this clever trick backfires.
It's not difficult to write a sector read/write program for the RAMdisk which doesn't make assumptions about the caller, but resolves the call in the "old way", so to speak. Then it works with the p-system. As the file handling is all built into the p-system, and a RAMdisk doesn't need formatting like a physical disk, that's all that's needed.
-
I can add that the p-system, which has its own file system, still need to combine that with how to access these files on the TI hardware. So they implement something they call PCB, Peripheral Control Block. It contains information that links the file control variable, visible in Pascal, to the file information block. A part of the PCB consists of the PAB, as we know it from the native system, since that's the layer where the p-system's file abstraction meets the hardware's storage device abstraction.
The p-system is heavily layered, in order to be as portable as possible. At the highest levels, you can't tell anything about the hardware, but the lower you come, more and more things become visible, until you hit the lowest level, the actual hardware.
-
2
-

Disassemble tms9995
in TI-99/4A Computers
Posted
And since they are opcode compatible, the 69 instructions that are common will be correctly disassembled by a program that can handle the TMS 9900 only.