Jump to content
apersson850

Pascal on the 99/4A

Recommended Posts

 

Run a program which inserts the necessary values in the system's table to access my fourth physical disk (simple) and my RAMdisks (a bit more tricky). It also executes the system call

 

I always enjoy your posts because you make me realize there is so much I am not doing that I could be, and therefore I have that much more to learn. UCSD Pascal is an operating system, and I do not know it very well in this regard.

 

I have started another small game project on Pascal. I want to harvest from last year's project (chemin-de-fer), but the combined codebases will exceed a single disk. At some point, it would be nice to have common code units for things like screen i/o so that they could be distilled to a single disk. But that is a big refactoring project and for now I want to work on a small refactoring project.

 

It would be a convenience to have a fourth floppy drive. I've never tried this before. Are you modifying SYSTEM.MISCINFO to do this? Is that in the p-code firmware? I'm curious to know anything you can share about modifying that (or other) OS files.

  • Like 1

Share this post


Link to post
Share on other sites

I have found that the main limitation of working within the p-code environment to be the editor. Indentation is inherent to good Pascal programming, and thus the 40 col screen is poorly suited for this, and for long programs, the 24 line screen makes it very difficult to follow the program logic.

Using emulation helps alleviate some of these limitations because, at least with Classic99, you can code using a modern editor like Notepad++ and paste the source directly into the p-code editor for compilation and testing. This does mess up your indentations however, but it wouldn't matter (there might be a way around this within Notepad++). Emulation also is far more reliable, and in my case I only use the real hardware for testing purposes. I just transfer the work disk to the TI using HDX.

That said, for shorter programs, the native editor is fine, and at least for me, I still very much enjoy the experience of using real hardware :)

  • Like 1

Share this post


Link to post
Share on other sites

 

Welcome to the small but dedicated p-code community.

 

There is a collection of useful p-code disk images on whtech. This directory contains Pascal programming disk images as well as TI-Pilot and the Freeform spreadsheet. There is another directory on whtech with some other Pascal related resources.

 

And don't forget to look at the TI Development Resources page in Atariage. If you didn't get the manuals, you can get them in PDF format here.

 

I use a combination of 705.dsk and 712a.dsk as my boot disk. It has both the Filer and Editor on it, as well as some other useful utilities such as a character set mapping for true lower case characters, and a utility for changing the default screen colors. These are modest but nice touches for customizing your programming environment.

 

If you have just the standard disk drives from TI, boot the Filer disk.

 

I do most of my development using Classic 99. I use a modified version of 712a in drive 1 (#4:), the standard Pascal compiler disk in drive 2 (#5:), and my work disk in drive 3 (#9:). This works very efficiently.

 

Again, welcome to the p-code community.

 

 

Thanks Rossman! I am looking forward to getting to use the P-Code system

Share this post


Link to post
Share on other sites

In a previous post, I outlined some files that can be useful on the system disk (the one in DSK1, or #4). Depending on the size of the disk, you can have more or less of these available at the same time.

 

SYSTEM.STARTUP // If you have one. This is like DSK1.LOAD for Extended BASIC. It will be executed on booting. Can for example be used to set up your PRINTER: device, set the date, change colors and modify the operating system to allow for more disks.

SYSTEM.CHARAC // If you want to change the system's default character definitions.

SYSTEM.PASCAL // Some modifications to the operating system. Delivered with the UCSD system's disks.

SYSTEM.FILER // The disk manager for the p-system. Invoked by pressing F at the main menu. Can be restarted by U (user restart).

SYSTEM.EDITOR // The text file/source code editor. Started by pressing E on the main menu. Can be restarted by U.

SYSTEM.COMPILER // The Pascal compiler. Runs when pressing C at the main menu. Can't be restarted.

SYSTEM.LIBRARY // A colleciton of resources you want available. The default one contains units for sprites, sound, speech etc. Can be expanded and modified by the user. Additional libraries can be added.

SYSTEM.MISCINFO // Setup for character codes, terminal width and other system resources. Modified by the Setup program on the Utilities disk.

9900.OPCODES // Used by the assembler.

9900.ERRORS // Used by the assembler.

 

As said above, insert a disk with the Filer, then press F, to start the system's disk manager. Insert a disk with the Editor and press E to run the text editor.

 

The p-system's text files contain a header block with tab definitions and other stuff. Indentation is compressed, with a DLE code and a number representing how many leading spaces a line should have.

 

To add a fourth disk drive isn't too difficult, if we look at a "standard system". The p-system reserves units 4, 5, 9, 10, 11 and 12 for this. The system has an array of data for the different system units available. Since the TI controller card only supports three drives, the unit table is only populated with values for units #4, #5 and #9.

In this file, you can see some of this data. There are I/O related data at 29DC, 2C20, 2C68 and 3696. Some of this information you have to fill in manually, by accessing memory directly. That can be done in different ways. The simlest is to write a poke procedure. Once you have activated disk unit #10 (which is the fourth disk), then some of the final data required can be created by the system itself, by calling the intrinsic unitclear(10).

 

Exactly what to write where I have to check in my old programs to find out.

 

Adding a RAMdisk is a bit more tricky, as it will have a different CRU address than the normal disk controller. Hence you can't just copy all data from a physical disk drive's table entries, but they have to be modified. The p-system also has a setup of PABs predefined in VDP RAM. You can't use them either, as they are dedicated for physical disk access. To avoid stealing space from the code pool in VDP RAM, I typically put these PABs for the RAMdisks either high up in the character definitions, or in the sprite attribute tables.

 

These tricks I perform in the SYSTEM.STARTUP program, so they are executed automatically when booting the system.

 

When the code base for a single project exceeds one disk, it's a good idea to split the program up into units. These are compiled separately, so once you are happy with their performance, you don't have to recompile them each time you change the main program. For larger projects, I'd typically have half a dozen units available, stored in a library dedicated for this particular project. The code files would then reference these units, as well as standard units in SYSTEM.LIBRARY, as needed.

This means these units can easily be stored on a different disk than the one you are currently working with for development.

Edited by apersson850
  • Like 2

Share this post


Link to post
Share on other sites

Wow best explanation! Thanks! I will start with 2 floppy drives and start with the files on the disk as you have mentioned. I will let you know!

Share this post


Link to post
Share on other sites

can anyone recommend any books to learn UCSD Pascal on the TI99?

 

Thanks

 

Hi, Videofx.

 

I set out to re-learn Pascal a little over a year ago. I broke the problem down into two parts.

 

The first part of the problem are Pascal basics: conventions, structures, etc. To do that, I bought a used copy of the text I used at university, Oh! Pascal! It's quirky, but I liked it in the 1980s, and I like it today. You can also look through the USUS library disks for example code. I also found it useful to explore the turtlegraphics code. apersson850 created a package a long time ago that largely works, and I posted about my experience getting it to work somewhere in Atariage. You may find the same a useful exercise as you get familiar with the environment.

 

The second part of the problem are the TI extensions, things like Sprites and what not. The TI documentation (in the PDFs in the TI development resources) is pretty good. Supplement that with your own trial and error, and the occasional question to the Atariage forum, and you should be just fine.

 

I find Pascal to be a fun and satisfying language. I hope you find it so as well.

Share this post


Link to post
Share on other sites

 

Hi, Videofx.

 

I set out to re-learn Pascal a little over a year ago. I broke the problem down into two parts.

 

The first part of the problem are Pascal basics: conventions, structures, etc. To do that, I bought a used copy of the text I used at university, Oh! Pascal! It's quirky, but I liked it in the 1980s, and I like it today. You can also look through the USUS library disks for example code. I also found it useful to explore the turtlegraphics code. apersson850 created a package a long time ago that largely works, and I posted about my experience getting it to work somewhere in Atariage. You may find the same a useful exercise as you get familiar with the environment.

 

The second part of the problem are the TI extensions, things like Sprites and what not. The TI documentation (in the PDFs in the TI development resources) is pretty good. Supplement that with your own trial and error, and the occasional question to the Atariage forum, and you should be just fine.

 

I find Pascal to be a fun and satisfying language. I hope you find it so as well.

 

 

Thanks! I do remember in the 80's when I was playing with pascal I do remember the book Oh Pascal. Buy are the memories flooding back!

Share this post


Link to post
Share on other sites

I put a lot of the USUS newsletters up onto WHT as well--and I have most of the others, I just haven't had time to scan them in yet. ISTR that one of them has the procedure to add a fourth disk and also the values to add an HRD to the system, but Anders may be the absolute best source for that data. I will try and get back to that particular scanning project soon, as it is important to me that the data doesn't disappear.

Share this post


Link to post
Share on other sites

I put a lot of the USUS newsletters up onto WHT as well--and I have most of the others, I just haven't had time to scan them in yet. ISTR that one of them has the procedure to add a fourth disk and also the values to add an HRD to the system, but Anders may be the absolute best source for that data. I will try and get back to that particular scanning project soon, as it is important to me that the data doesn't disappear.

 

 

Thanks! I will get the newsletters

Share this post


Link to post
Share on other sites

Yes, I know there were a few other people, who also acquired a good knowledge of the p-system on the 99/4A. The problem back then was to learn about them. No internet means you frequently never got in touch.

Share this post


Link to post
Share on other sites

I was reading in the manuals about 2 disk drives and it tell me what I need to do. I am still trying to figure out the command line options for the compiler :)

Share this post


Link to post
Share on other sites

Yes, I know there were a few other people, who also acquired a good knowledge of the p-system on the 99/4A. The problem back then was to learn about them. No internet means you frequently never got in touch.

True--but oddly enough, your name was one of the ones that always popped up back in the day when it came to the p-System. You've been a famous TI p-System user for about as long as the TI has HAD a p-System! :) :) :) That's a good thing! :)

  • Like 1

Share this post


Link to post
Share on other sites

Well, then at least I'm famous for something! When I was at university (or Institute of Technology, as it was called), we were four people with p-code cards within a mile or so. Quite a concentration, I think, in the TI world.

Once I got the p-code card, almost all of my software development was either in Pascal, frequently with assembly support, or in assembly for use by Extended BASIC or stand alone. Since the majority of what I did was for the p-system, that's why I learned quite a lot about it.

Since I was interested in system programming too, some projects reflected that.

 

I did quite a lot of funny projects with the p-system.

  • I did a window support unit. You gave it a screen position, a window width and a text string. It then created a pop-up window, with a frame, where it automatically word-wrapped your text within the window. This was overlaying the text on the screen, and as soon as the screen content changed, the window disappeared and the original text was still there. All done in assembly, so the window appeared and disappeared in the blink of an eye (literally).
  • I did a clock display in the corner of the screen, interrupt driven, with time read from my own hardware real time clock.
  • I did the copier, which could copy disks like Advanced Diagnostics and some other stuff.
  • I did a turtlegraphics unit, which was never fully completed, but could write text,draw lines etc.
  • I expanded the concurrency in the p-system to real-time multitasking with preemptive task switching. Unfortunately it turned out the system's dinamic memory allocation procedures couldn't properly protect themselves from being switched out. Without dynamic memory available, multitasking is of limited use, so I had to abandon that.
  • I did a disk catalog program, where the idea was that you gave every disk (non p-system disk) a number. You then let this Pascal program read it. It would read all file names on the disk and store them. You then read a new disk (different number) and the procedure repeated itself. When you had read any number of disks, you could ask the program to display/print a list of all file names in alphabetical order, with the name and number of the disk they resided on. The disk number was stored by the program in an unused part of the normal file catalog, so if you read the same disk again, the program would update the stored data for that disk into the database. Unfortunately, with the memory my computer had at that time, handling the database was too slow to make it practically useful, when the number of disks read reached somewhere between 50 and 100. I had more than 100 disks. If I at that time had the RAMdisks I have now, the project would have been completed. The program set up an array of file information that was handled according to the same principle as the PDP-11 VAX/VMS manages memory. We had a lot of PDP-11 VAX at school, so we learned how they worked. This was OK for file data, where virtual memory was handled in pages that were read and written by a support unit, transparent to the main program. But when the short index arrays also grew outside the available memory, so they had to be paged too, it simply became too slow.
  • I implemented a RAMdisk using a 56 K Maximem module, the 32 K RAM expansion card and the remaining 32 K RAM out of the 64 K RAM I built into the console, before RAMdisks started to become commercially available.

There aren't really any command line options for the compiler, or for anything else, for that matter, since the p-system is menu driven. The closest you can come is some of the options you can set after pressing X (for eXecute). For example, pressing X, then writing p=#5, will set the default file name prefix (the disk name) to the physical unit #5 (DSK2). Handy if that's where your text and code files are.

 

So basically, to compile a text file called ANDERS on the second drive, now assuming you have set the prefix to #5 and you don't have any workfile specified in the system (I never use that feature), you do this:

Press C to start the compiler.

Enter ANDERS to compile the file #5:ANDERS.TEXT.

Press $ to send the output to the file #5:ANDERS.CODE.

Assuming compilation was successful, you then press X (for eXecute), followed by ANDERS. This will now load and run the file #5:ANDERS.CODE, which was just created by the compiler.

 

There are some compile time options you can use in the compiler, by entering these options into your source file. But nothing you really need to worry about to begin with.

 

If you want to do something similar to a LIST command in BASIC, you can use the filer. Press F to start the Filer, then press T for Transfer. Enter ANDERS.TEXT,CONSOLE: and the text file is displayed on your screen. If you instead press ANDERS.TEXT, PRINTER:, the file will be sent to your printer. Provided one is defined properly, that is. Nowadays, I usually set up the printer: port to use a serial port, which is connected to a computer, running Hyper Terminal in text capture mode.

If you want to study the file in detail on the monitor, you open the Editor and load the file there instead, of course.

 

If you are emulating the system, then you can use other procedures for some tasks.

  • Like 3

Share this post


Link to post
Share on other sites

I just read it. I don't think I've seen it before, but it's relevant. No major errors.

I noticed I'm mentioned there too.

 

Myself, I never use the workfile concept, though. I found my projects usually involved several source code files, for main program, inclucde files, separately compiled units and assembly support. In such cases, I found it easier to just enter the file name of the part I was working on each time, instead of re-assigning a different file as a work file each time.

Once I got RAMdisks included in the system, I also typically used that as a storage place for the file during compilation, to speed up that process. But even if you keep the source file on physcial disk, but have the compiler on the RAMdisk, you reduce compile time to half, due to the frequent swapping of code segments in the compiler.

Edited by apersson850
  • Like 2

Share this post


Link to post
Share on other sites

Yes, the tutorial is succinct and simple enough to get you started, because if one first starts with the manuals, it can be very intimidating, akin to trying to learn assembly just by reading the EA manual... A stark contrast to Turbo Pascal (I started with version 3.0)...

Share this post


Link to post
Share on other sites

That's correct. The manuals for the TI p-system implementation all assume you know what you want to accomplish, and only tell you exactly what's supported in the TI implementation. I think the only clue to how to operate the system is some note about how to distribute the files on the disk drives. Unless I've dreamed about that...

Share this post


Link to post
Share on other sites

Here are two programs, which among other things insert the fourth physical disk drive into the system's tables and adds RAMdisks, if present.

It's a bit too much to explain Everything from the beginning, so I suggest those interested look at the code, ask questions and we'll take it from there.

 

 

 

 

 

(*$R- *) (* Turn off range checking *)
(*$C Startup with login time bookkeeping, A-DATA 170824 *)
program autostart;
(* This program is intended for autostarting.
* It gives a welcome message together with the actual time.
* It also administrates the bookkeeping of login date and time on the root
* volume. Sector 3 on the disk is used for this purpose. See the dirtype
* declaration (in the main program).
* The system date is also changed, if necessary.
*
* Installs unit #10 in the system
* Redirects the printer port for virtual printing to a PC.
* Can also load multitasking support.
*
* IMPORTANT! Code type MUST be M_9900, because of direct VDP RAM access.
*
* A-DATA 170824 *)
uses commandio, (* redirection *)
screenops, (* date setting *)
realtime, (* clock reading *)
support; (* Screen color *)

const
rddata = -30720; (* VDPRD *)
rdstat = -30718; (* VDPST *)
wrtdata = -29696; (* VDPWD *)
wrtaddr = -29694; (* VDPWA *)
wrtenab = 16384; (* hex 4000, setting VDP address to write *)
pabtbl = 10716; (* hex 29dc *)
maxlen = 43;
valider = 'Has logtime '; (* Indicates valid login time on disk *)
sysdate = 13840; (* Address of system date in memory *)
type
byte = 0..255;
window = record
case boolean of
true: (int: integer);
false:(ptr:^integer);
end;
convdate = record
case boolean of
true :(int:integer);
false:(dat:sc_date_rec);
end;

chartype=string[40];
dirtype=record
fill1:packed array[0..255] of char; (* sector 0 *)
fill2:packed array[0..255] of char; (* sector 1 *)
fill3:packed array[0..255] of char; (* sector 2 *)
logvalid:packed array[0..11] of char; (* sector 3 *)
logtime :string;
fill4:packed array[0..161] of char;
fill5:packed array[0..5] of char; (* sector 4, Pascal directory *)
dvid:string[7];
fill6:packed array [0..5] of char;
dlastboot:sc_date_rec;
fill7:packed array[0..489] of char; (* sector 4 and 5 *)
end;

texttab=array[1..12] of string[3];

var
cpuaddr: window;
curlen,period,vdpaddr: integer;
savevdpaddr: integer;
ch: char;
unitno: integer;
i: integer;
pabname:string[43];
info:sc_info_type;
currdate,newdate:sc_date_rec;
twindate:convdate;
dateout:chartype;
directory:dirtype;
monthtab:texttab; (* For text display of month *)
printer:text;
actdate:tdate;
datestr :string;
rtc_ok :boolean;

procedure swapbyte(var x:integer);

(* This procedure takes a word and reverses the order of the bytes *)
type
byteword = record
case boolean of
true: (addr:integer);
false:(bytes: packed array[1..2] of byte);
end;

var
word: byteword;
tbyte: byte;

begin
with word do
begin
addr := x;
tbyte := bytes[1];
bytes[1] := bytes[2];
bytes[2] := tbyte;
x := addr;
end; (* with statement *)
end; (* procedure *)

procedure wrtvdpaddr (vdpaddr : integer);

(* This procedure initializes the VDP ram chip to read/write from the *
* address passed in the parameter vdpaddr. *)

begin
cpuaddr.int := wrtaddr;
swapbyte( vdpaddr );
cpuaddr.ptr^ := vdpaddr;
swapbyte( vdpaddr );
cpuaddr.ptr^ := vdpaddr;
end; (* procedure *)

function rdvdp (var vdpaddr : integer) : integer;

(* This function reads a byte of data from the VDP ram address specified *
* in the parameter vdpaddr. *)

begin
wrtvdpaddr( vdpaddr );
cpuaddr.int := rddata;
rdvdp := cpuaddr.ptr^ div 256; (* Right justify byte in word *)
vdpaddr := vdpaddr + 1;
end; (* procedure *)

procedure wrtvdp( var vdpaddr : integer;
data : integer);

(* This procedure writes the byte of data passed in the parameter *
* data to the VDP ram address specified in vdpaddr. *)

var
temp : integer;

begin
temp := vdpaddr + wrtenab; (* Write enable the address *)
wrtvdpaddr(temp);
cpuaddr.int := wrtdata;
cpuaddr.ptr^ := data * 256; (* Left justify byte in word and write *)
vdpaddr := vdpaddr + 1;
end; (* procedure *)
procedure change_color;
(* Changes screen color to black on white *)
type
chrptr = ^char;

var
temp: integer;
chpt: chrptr;

begin
temp := 10269;
moveleft(temp,chpt,2);
chpt^ := chr(31);
temp := 11453;
moveleft(temp,chpt,2);
chpt^ := chr(31);
set_scr_color(1,15);
end; (* change color *)

procedure poke(addr,value: integer);
(* Stores value at addr in CPU RAM *)

var memaddr: window;
begin
memaddr.int := addr;
memaddr.ptr^ := value;
end; (* poke *)
procedure datetostring(thedate:sc_date_rec; var dateout:chartype);
(* Datetostring converts the internal date format to the display format.
The array monthtab is not used, because the month is displayed lower-case
but may be entered as lower- or upper-case. *)
var yearchar,monthchar,datechar:chartype;
begin
with thedate do
begin
str(year,yearchar);
str(day,datechar);
case month of
1:monthchar := 'Jan';
2:monthchar := 'Feb';
3:monthchar := 'Mar';
4:monthchar := 'Apr';
5:monthchar := 'May';
6:monthchar := 'Jun';
7:monthchar := 'Jul';
8:monthchar := 'Aug';
9:monthchar := 'Sep';
10:monthchar := 'Oct';
11:monthchar := 'Nov';
12:monthchar := 'Dec';
end;
dateout := concat(datechar,'-',monthchar,'-',yearchar);
end;
end;

procedure unit_10;
(* Installes unit #10 in the system *)
const
pointaddr = 11316; (* Entry in unit type pointer table *)
disk_kind = 11376; (* Pointer to code for blocked units *)
descraddr = 14094; (* Address of unit descriptor *)
type
dvidtype = string[7];
dirtype = packed record
fill1,
dlastblk,
dfirstblk :integer;
dvid :dvidtype;
deovblk :integer;
end; (* dirtype *)

unittype = packed record
volume :dvidtype;
is_blocked :boolean;
blocks :integer;
end; (* unittype *)

dual = record
case boolean of
true :(int :integer);
false :(ptr :^unittype);
end; (* dual *)

var
window :dual;
dir :dirtype;
begin (* unit 10 *)
(* Insert blocked unit pointer in pointer table *)
poke(pointaddr,disk_kind);
(* Load something to begin with *)
window.int := descraddr;
unitclear(10);
unitread(10,dir,sizeof(dir),2,0); (* Fetch actual values *)
with window.ptr^ do
begin
if (ord(dir.dvid[0])<=7) and (ioresult=0) then
begin
volume := dir.dvid;
blocks := dir.deovblk;
end
else
begin
volume := '';
blocks := 0;
end;
end; (* with *)
end; (* unit 10 *)

begin (* main program *)

(* Write the welcome text *)
change_color;
page(output);
gotoxy(0,2);
writeln('Welcome to the UCSD p-system IV.0 at');
(* Use actual time *)
engtext(datestr);
(* writeln(pos('Jan 01,',datestr));
writeln(pos('00:0',datestr));
writeln(datestr[18]); *)
rtc_ok := not ((pos('Jan 01,',datestr)=1) and
(pos('00:0',datestr)=14) and
(datestr[18] in ['0','1']));
if rtc_ok then
writeln(datestr)
else
writeln('RTC battery low. Clock restarted.');
writeln;

(* Read and change login and system date in directory *)
unitread(4,directory,sizeof(directory),0);
with directory do
begin
(* Write last login time *)
write('Last ',dvid,' login ');
(* Use best date available *)
if logvalid=valider then
begin
writeln(logtime);
end
else
begin
datetostring(dlastboot,dateout);
writeln(dateout);
logvalid := valider;
end;

if rtc_ok then
logtime := datestr; (* Set new login time *)

(* Set the new system date, if a better one is available *)
if rtc_ok then
begin
readdate(actdate);
with dlastboot do
begin
if month>actdate.month then
year := (year+1) mod 100;
month := actdate.month;
day := actdate.date;
end; (* with dlastboot *)
sc_use_info(sc_get,info);
info.sc_date := dlastboot;
sc_use_info(sc_give,info);
twindate.dat := dlastboot;
poke(sysdate,twindate.int);
end; (* if rtc_ok *)
end; (* with directory *)

(* Write information back to directory *)
unitwrite(4,directory,sizeof(directory),0);

unit_10; (* Install unit #10 *)

(* Start modification of the RS232 port *)
gotoxy(0,10);
writeln('Modifying the PRINTER:-port');
writeln;
writeln('RS232/2:');
writeln(' Baudrate : 9600');
writeln(' Data bits: 8');
writeln(' Parity : none');
unitno := 6;
(* Find the address of the PAB entry in VDP ram. Read the current *
* device definition and display it to the user. If no device *
* currently defined, exit the program. *)
cpuaddr.int := pabtbl + unitno * 2;
vdpaddr := cpuaddr.ptr^ + 17;
savevdpaddr := vdpaddr;
curlen := rdvdp(vdpaddr);
if curlen > 0 then
begin
pabname[0] := chr(curlen);
for i := 1 to curlen do
pabname := chr(rdvdp(vdpaddr));
pabname := 'RS232/2.BA=9600.DA=8.PA=N';
(* Automatic setting of the type above *)
period := 0;
for i := 1 to length(pabname) do
begin
if (period = 0) and (pabname = '.') then
period := i - 1;
if pabname in ['a'..'z'] then
pabname := chr(ord(pabname)-32);
end; (* for loop *)

if period = 0 then
period := length(pabname);
vdpaddr := savevdpaddr-17;
for i := 1 to 2 do
wrtvdp(vdpaddr,0);
wrtvdp(vdpaddr, period);
for i := 1 to 3 do
wrtvdp(vdpaddr, 0);
vdpaddr := savevdpaddr;
for i := 0 to length(pabname) do
wrtvdp(vdpaddr, ord(pabname));
unitclear(unitno);
end;

(* Enable multitasking if required *)
gotoxy(0,18);
write('Enable multitasking? ');
read(ch);
writeln;

if ch in ['Y','y'] then
begin
chain('*multitask');
writeln('Loading multitasking support');
end;

(* Sets default prefix to drive #5 *)
if not redirect('P=#5') then
exception(true);
end.

program ram_disk_installation;
(* Used to install one of two RAMdisks for use with the p-system *)
(* A-DATA 880128 *)

uses
extrascreen,
misc;

const
disk_kind = 11376; (* Address to unit I/O procedure pointer table for
* blocked devices. *)
type
loc_type = record
occupied: boolean;
addr: integer;
end; (* loc_type *)
loc_arr_type = array[1..2,1..2] of loc_type;
addr_arr = array[0..32] of integer;
addr_type = ^addr_arr;
var
pcb_address, (* Address in VDP RAM of Peripheral Control Block *)
base, (* CRU base of RAM disk *)
device_no, (* I/O unit number *)
loc: integer;
continue: boolean;
pcb_pointer,
kind_pointer: addr_type;
locations: loc_arr_type;

function find_entry_addr(cru_base: integer): integer; external;
(* Returns the entry address of the sector R/W subprogram on the RAM disk in
question. This requires bit I/O (CRU access), which must be done in
assembly language. *)

function baseaddress: integer;
(* Lets the user input the CRU base address of the RAMdisk being installed *)

var
good: boolean;
ch: char;
line: string;

begin (* baseaddress *)
gotoxy(0,5);
writeln('Give the CRU base address of your');
write('RAMdisk (in hex, please).');
repeat
gotoxy(0,7);
write('Address? ',chr(141));
validstring(line,4,['0'..'9','A'..'F','a'..'f']);
(* Check the address for validity. All valid CRU base addresses look
like 1x00, where x is a hex digit in the range 0..F. *)
if length(line)=4 then
good := (line[1]='1') and (line[3]='0') and (line[4]='0')
else
good := false;
until good;

(* Convert the characters to an integer *)
upper_case(line,line);
ch := line[2];
if ch>'9' then
ch := chr(ord(ch)-7);
baseaddress := 4096+(ord(ch)-48)*256;
end; (* baseaddress *)
procedure heading;
(* Writes the heading when the program starts *)

begin
page(output);
gotoxy(5,0);
write('RAMdisk installation');
gotoxy(0,2);
writeln('A tool for the TI 99/4A p-system by');
write('Anders Persson, Sweden.');
end; (* heading *)
function in_as_unit: integer;
(* Asks the user about the I/O unit number to use for the RAMdisk. *)

var
temp: integer;

begin (* in as unit *)
gotoxy(0,9);
writeln('The RAMdisk may be installed as unit');
write('#11: or #12:.');

repeat
gotoxy(0,11);
write('Unit #? ',chr(141));
temp := validint(2,['1','2']);
until temp in [11,12];
in_as_unit := temp;
end; (* in as unit *)
function pcb_location: integer;
(* Lets the user select the character pattern table or the sprite motion
table as the destination for the peripheral control block. *)
var
choice: integer;

begin (* pcb_location *)
gotoxy(0,13);
writeln('The peripheral control block can be');
writeln('placed in the character pattern table or');
writeln('in the sprite motion table.');
writeln('Select one alternative.');
writeln('1 - Character pattern table');
writeln('2 - Sprite motion table');

repeat
repeat
choice := bufscan;
until choice<>-1;
choice := choice-48;
until choice in [1,2];
pcb_location := choice;
end; (* pcb_location *)

function overwrite(unitno: integer): boolean;
(* Asks for permission to reload the same logical device number *)
var
ch: char;

begin (* overwrite *)
gotoxy(0,13);
writeln('Unit #',unitno,' is already installed.');
write('Overwrite? ');

repeat
gotoxy(11,14);
write(chr(141));
read(ch);
until ch in ['Y','y','N','n'];
overwrite := ch in ['Y','y'];
end; (* overwrite *)
procedure init_tables
(* Initiates tables. Determines already occupied pcb locations to avoid
collisions if this program is used to install two RAMdisks. *)
(var pcb_pointer,
kind_pointer: addr_type;
var locations: loc_arr_type);
const
addr1 = 2010; (* PCB address in character pattern table *)
addr2 = 2029;
addr3 = 3418; (* PCB address in sprite motion table *)
addr4 = 3437;
(* Addresses of unit description tables in the BIOS data area. *)
base_device_kind = 11296;
base_pcb_loc = 10716;
var
i,
temp: integer;

procedure mark_occupied
(* Determines occupied pcb locations *)
( used: integer;
var locations: loc_arr_type);

var
i,
j: integer;

begin (* mark occupied *)
for i := 1 to 2 do
for j := 1 to 2 do
with locations[i,j] do
if addr=used then
occupied := true;
end; (* mark occupied *)

begin (* init tables *)
(* Fix to load pointers with desired values *)
temp := base_device_kind;
moveleft(temp,kind_pointer,2);
temp := base_pcb_loc;
moveleft(temp,pcb_pointer,2);

fillchar(locations,sizeof(locations),chr(0));
locations[1,1].addr := addr2;
locations[1,2].addr := addr1;
locations[2,1].addr := addr4;
locations[2,2].addr := addr3;

(* Mark occupied locations if the I/O devices are active *)
for i := 1 to 2 do
if kind_pointer^<>0 then
mark_occupied(pcb_pointer^,locations);
end; (* init tables *)
function pcb_addr_in_vdp
(* Determines the address in VDP RAM where the pcb should be loaded. *)
( loc: integer; (* Pattern vs. motion table *)
pcb_pointer: addr_type;
var locations: loc_arr_type): integer;
var
i: integer;
found: boolean;
begin (* pcb addr in vdp *)
(* If the device was already active, use the previous PCB location. *)
if loc=0 then
pcb_addr_in_vdp := pcb_pointer^[device_no]
else
begin
(* This is somewhat overkill for the current job, but easy to expand on.*)
found := false;
i := 1;
while (i<=2) and not found do
with locations[loc,i] do
if not occupied then
begin
found := true;
pcb_addr_in_vdp := addr;
occupied := true;
end
else
i := i+1;
end; (* if else *)
end; (* pcb addr in vdp *)

procedure make_pcb
(* Makes a peripheral control block, and loads it into VDP RAM *)
( cru_base,
pcb_addr: integer);

const
(* This procedure requires access to VDP RAM, which in turn requires
knowledge of the addresses used for mapping in the VDP itself. *)
rddata = -30720;
rdstat = -30718;
wrtdata = -29696;
wrtaddr = -29694;
wrtenab = 16384;

type
byte = 0..255;
window = record
case boolean of
true: (int: integer);
false: (ptr: ^integer);
end; (* window *)

buf_type = packed array[0..19] of char;

var
temp,
i: integer;
pcb: buf_type;

procedure swapbyte(var x: integer);
(* Reverses bytes in a word *)

type
byteword = record
case boolean of
true: (addr: integer);
false: (bytes: packed array[1..2] of byte);
end; (* byteword *)

var
word: byteword;
tbyte: byte;

begin
with word do
begin
addr := x;
tbyte := bytes[1];
bytes[1] := bytes[2];
bytes[2] := tbyte;
x := addr;
end; (* with *)
end; (* swapbyte *)

procedure wrtvdpaddr(vdpaddr: integer);
(* This procedure initalizes the VDP chip to read/write from the address
passed in the paramter vdpaddr. *)

var
cpuaddr: window;

begin (* wrtvdpaddr *)
cpuaddr.int := wrtaddr;
swapbyte(vdpaddr);
cpuaddr.ptr^ := vdpaddr;
swapbyte(vdpaddr);
cpuaddr.ptr^ := vdpaddr;
end; (* wrtvdpaddr *)

procedure wrtvdp(var vdpaddr: integer; data: integer);
(* Writes byte of data to VDP RAM at vdpaddr. *)

var
cpuaddr: window;
temp: integer;

begin
temp := vdpaddr+wrtenab; (* Write enable address *)
wrtvdpaddr(temp);
cpuaddr.int := wrtdata;
cpuaddr.ptr^ := data*256; (* Left justify and write *)
vdpaddr := vdpaddr+1;
end; (* wrtvdp *)

begin (* make pcb *)
(* Set up peripheral control block *)
fillchar(pcb,sizeof(pcb),chr(0));
pcb[2] := chr(1); (* Character count (to first period, if any) *)
pcb[6] := chr(1); (* Version number *)
pcb[7] := chr(10); (* Link offset *)
pcb[10] := chr(63); (* Buffer address in PAB (MSByte) *)
pcb[17] := chr(1); (* Name length byte in PAB *)
pcb[18] := chr(18); (* Name of sector R/W subprogram *)
moveleft(cru_base,pcb[4],2); (* Load CRU base *)
temp := find_entry_addr(cru_base);
moveleft(temp,pcb[0],2); (* Load entry address *)

(* Now move the whole thing to VDP RAM *)
for i := 0 to 18 do
wrtvdp(pcb_addr,ord(pcb));
end; (* make pcb *)
procedure sys_software
(* Tells the system that all system software (compilier, editor etc.) is
located on the RAMdisk. Usually it is, and if not, the system locates
the files on next invocation anyway. *)
( device_no: integer);
const
sys_info_address = 14370; (* Address of system file info block *)
sys_name = ':SYSTEM.';
link_name = 'LINKER';
filer_name = 'FILER';
ed_name = 'EDITOR';
comp_name = 'COMPILER';
assm_name = 'ASSMBLER';
type
name_type = string[23];
sys_rec_type = record
linker, (* Order reversed due to compiler allocation *)
filer,
editor,
compiler,
assembler: name_type;
end; (* sys rec type *)
info_type = ^sys_rec_type;
var
sys_soft_info: info_type;
temp: integer;
str_no,
sys_prefix: string;
begin (* sys software *)
str(device_no,str_no);
sys_prefix := concat('#',str_no,sys_name);
temp := sys_info_address;
moveleft(temp,sys_soft_info,2);

with sys_soft_info^ do
begin
linker := concat(sys_prefix,link_name);
filer := concat(sys_prefix,filer_name);
editor := concat(sys_prefix,ed_name);
compiler := concat(sys_prefix,comp_name);
assembler := concat(sys_prefix,assm_name);
end; (* with *)
end; (* sys software *)
begin
heading;
init_tables(pcb_pointer,kind_pointer,locations);
base := baseaddress;
device_no := in_as_unit;
(* See if the device already exists *)
if kind_pointer^[device_no]=0 then
begin
continue := true;
loc := pcb_location;
end
else
begin
continue := overwrite(device_no);
loc := 0;
end;

if continue then
begin
pcb_address := pcb_addr_in_vdp(loc,pcb_pointer,locations);
make_pcb(base,pcb_address);
pcb_pointer^[device_no] := pcb_address;
kind_pointer^[device_no] := disk_kind;
(*$i-*)
unitclear(device_no);
if ioresult=0 then
begin
sys_software(device_no);
gotoxy(0,20);
writeln('RAMdisk installed');
writeln('If system software isn''t on the RAMdisk');
writeln('now, run this program again when it is.');
end;
(*$i+*)
end;
end.

 

 

  • Like 5

Share this post


Link to post
Share on other sites

Here are two programs, which among other things insert the fourth physical disk drive into the system's tables

 

 

 

 

type

window = record

case boolean of

true: (int: integer);

false:(ptr:^integer);

end;

 

procedure poke(addr,value: integer);
...
end; (* poke *)

 

procedure unit_10;
...

unit_10; (* Install unit #10 *)

 

 

 

This is awesome. Thank you for posting your code. I was able to harvest the unit_10 code and it works. I now have 4 disk drives on my p-system. This will help me with developing my new project and refactoring my old project.

 

Thanks again!

 

Edit to the original post:

 

Made a simple copy of my work disk. Seeing "Warning units 9 & 10 have the same name" in the filer after an Extended Directory operation was very satisfying!

Edited by Rossman

Share this post


Link to post
Share on other sites

I used this document to get started with the pcode system on the TI, with supplemental reading from the manuals as needed. It's a tutorial by Ron Williams from the Boston Computer Society TI 99/4A User Group.

 

Introduction to UCSD Pascal System.pdf

I would like to add the document to the development resources thread. Could someone who is a lot more UCSD Pascal savvy as I am check the devres thread and tell me what is missing in terms of pascal documents, disk images and the like. Planning on doing a thread update by the end of the year. Would be cool if we could get a UCSD Pascal starter bundle. Thanks

Share this post


Link to post
Share on other sites

I have found that the main limitation of working within the p-code environment to be the editor. Indentation is inherent to good Pascal programming, and thus the 40 col screen is poorly suited for this, and for long programs, the 24 line screen makes it very difficult to follow the program logic.

Using emulation helps alleviate some of these limitations because, at least with Classic99, you can code using a modern editor like Notepad++ and paste the source directly into the p-code editor for compilation and testing. This does mess up your indentations however, but it wouldn't matter (there might be a way around this within Notepad++). Emulation also is far more reliable, and in my case I only use the real hardware for testing purposes. I just transfer the work disk to the TI using HDX.

That said, for shorter programs, the native editor is fine, and at least for me, I still very much enjoy the experience of using real hardware :)

 

Your comment about indentation is also aprapo to c99.. I remember Mike Maksimik of the Chicago TI Users group had hacked an old 80 column terminal in to the P-System. So that might be one route, serial cable, PC and a terminal emulator?

 

Dano

Share this post


Link to post
Share on other sites

The p-system is more or less prepared for a remote terminal out of the box. Most changes required are done in the SYSTEM.MISCINFO file by the SETUP program. You may have to write a new system unit GOTOXY and integrate that into the system as well.

Apart from that, the p-system does emulate an 80 character wide screen, similar to the EA editor.

 

Nice to hear you found the code useful, Rossman. The Volumes command in the filer will show you all available units, and also update the list of disk names.

Share this post


Link to post
Share on other sites

My phone started locking up, but it seems it did post replies anyway. And more than one of them.

 

I can add that the precompiled unit realtime, which is referenced from the program, contains support for reading the real time clock on my clock card. It's used to set the system date on startup, so you don't have to go into the Filer to do that.

Edited by apersson850

Share this post


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

Guest
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.

Loading...

  • Recently Browsing   0 members

    No registered users viewing this page.

×
×
  • Create New...