Jump to content
IGNORED

Pascal on the 99/4A


apersson850

Recommended Posts

Sure you can. In the assembly part of the program, you can see the location of the 80 column screen (2000H - 27FFH). You have to scroll that screen, not the one in VDP RAM, then call BL @47D2H to invoke the operatings system's routine to update the current 40 column window with the content of the relevant part of the 80 column screen.

 

See, not even that is as simple as one may think! But scrolling a part of a screen in ordinary RAM is of course easier than doing it in VDP RAM.

 

You can write your scroll routine in a separate source file, and assemble it separately, then link both that and the file I uploaded into the Pascal host unit. The linker can handle multiple assembly files. I probably have my own scroll routine as a part of some other file. Just couldn't locate it.

 

I have another Pascal utility, which allows you to show a temporary message window on the screen. It then disappears by itself, since it's drawn in the VDP screen, not the 80 column screen. So as soon as you for example press FCTN- Screen right, it goes away again.

If you like, I can show that too.

Edited by apersson850
  • Like 1
Link to comment
Share on other sites

Meanwhile, for you who use your TI for programs with text output in a different language than English, here's my solution to get the special Swedish characters. It can be adapted to any language, of course.

 

To use it, you simply include uses swedish; in your program. Then, using the initialization and termination code sections of the unit, it will start by defining the Swedish character patterns, then clean up and restore the original ones on exit.

Note that the interface part of this unit is empty. There are no procedures that the user can call. It just automatically changes the characters.

 

unit swedish;
  (* Defines Swedish characters, and restores original characters *)
  (* A-DATA 860205 *)
  
interface

implementation

uses
  support;

var
  charsave:array[1..6] of string[16];
  
begin (* Swedish *)
  (* Save original characters *)
  get_pattern( 91,charsave[1]);
  get_pattern( 92,charsave[2]);
  get_pattern( 93,charsave[3]);
  get_pattern(123,charsave[4]);
  get_pattern(124,charsave[5]);
  get_pattern(125,charsave[6]);
  
  (* Swedish characters *)
  set_pattern( 91,'280038447C4444');
  set_pattern( 92,'28003844444438');
  set_pattern( 93,'100038447C4444');
  set_pattern(123,'0028003848483C');
  set_pattern(124,'00280038444438');
  set_pattern(125,'0010003848483C');
***;
  (* Restore characters *)
  set_pattern( 91,charsave[1]);
  set_pattern( 92,charsave[2]);
  set_pattern( 93,charsave[3]);
  set_pattern(123,charsave[4]);
  set_pattern(124,charsave[5]);
  set_pattern(125,charsave[6]);
end. (* Swedish *)

 

Link to comment
Share on other sites

Has anyone tried using the RS232 to transfer a binary file from a PC to the Pascal system? I need to transfer a couple of binary data files (pattern and color files for a picture) to a UCSD Pascal disk for a project of mine, but there seem to be no modern tools capable of doing this. Pcode tool looked promising as it has an option to transfer data files but when I tried it the files were corrupted and of the wrong size.

My thinking is to use the REM: device which is 300 baud by default, connect the PC to the TI RS232 via a straight serial cable, and use the Transfer command in the Filer to receive the file. The problem will be to figure out on the PC end how to send that file since all terminal emulators use some kind of file transfer protocol like Kermit or Zmodem. 

Link to comment
Share on other sites

This might too ambitious but this xfer program says it is an Xmodem transfer program in Pascal. 

Ureal what you can find on Github.

 

pascal_sources/comm/xfer10.zip at master · codersclub/pascal_sources (github.com)

 

Files in this archive:


	XFER.PAS	Main source code file.  Contains
			constant definitions, CRC calculation code
			and command line parameter processing.

	SXRX.INC	Include file for XFER.PAS.  Contains
			functions to send and receive Xmodem.

	XFER10.DOC	This documentation file.

	XFER.EXE	The complete compiled version, in case you
			don't have the IBMCOM unit or Turbo Pascal.</PRE>

 

Link to comment
Share on other sites

8 hours ago, Vorticon said:

Has anyone tried using the RS232 to transfer a binary file from a PC to the Pascal system?

I have limited my experiments of this kind to text files. One reason is that they are easier to inspect manually and correct if there is some issue with the transfer.

It's of course possible to write a program which converts a binary file to some text file version and then another that does that conversion backwards again at the receiving end.

Link to comment
Share on other sites

1 hour ago, apersson850 said:

I have limited my experiments of this kind to text files. One reason is that they are easier to inspect manually and correct if there is some issue with the transfer.

It's of course possible to write a program which converts a binary file to some text file version and then another that does that conversion backwards again at the receiving end.

I wonder if BLOCKREAD would work since it does not care about the kind of data it receives. Set up an array of bytes as the receiving buffer and REM:datafile as the input file and set the block number to transfer to 12. I suspect timing would be an issue though... It does not look like a proper terminal program capable of binary file transfers has been made for UCSD Pascal that I can find anyway. 

Link to comment
Share on other sites

19 hours ago, apersson850 said:

Sure you can. In the assembly part of the program, you can see the location of the 80 column screen (2000H - 27FFH). You have to scroll that screen, not the one in VDP RAM, then call BL @47D2H to invoke the operatings system's routine to update the current 40 column window with the content of the relevant part of the 80 column screen.

 

I have another Pascal utility, which allows you to show a temporary message window on the screen. It then disappears by itself, since it's drawn in the VDP screen, not the 80 column screen. So as soon as you for example press FCTN- Screen right, it goes away again.

If you like, I can show that too.

Scrolling the 80 col screen per your description should be fairly straightforward. Given that UCSD Pascal IV supports concurrency, I can imagine an application where 2 separate processes are running at the same time, each using half of the screen. The user can then switch instantly between the 2 halves to see updates. Pretty cool :)

I'd love to see the source for the message window. 

Link to comment
Share on other sites

I see that you are saving 6 registers in the interrupt workspace at >83C0 and then restoring them later. (SAVEPAD is >83C6 which is R3, and STDVDPR1 is >83D4 which is R10) In XB you can just ignore that workspace and let the interrupt routine take care of it. It sounds like Pascal is using that workspace for other things besides to the usual interrupt routines.

 

 LWPI    SCANWS        ;SAVE SOME OF THE RAM PAD LOCATIONS
          LI      R0,SAVEPAD
          MOV     *R0+,R1
          MOV     *R0+,R2
          MOV     *R0,R3
          LI      R0,STDVDPR1
          MOV     *R0+,R4
          MOV     *R0+,R5
          MOV     *R0,R6

 

 

 

Link to comment
Share on other sites

15 hours ago, Vorticon said:

Has anyone tried using the RS232 to transfer a binary file from a PC to the Pascal system? I need to transfer a couple of binary data files (pattern and color files for a picture) to a UCSD Pascal disk for a project of mine, but there seem to be no modern tools capable of doing this. Pcode tool looked promising as it has an option to transfer data files but when I tried it the files were corrupted and of the wrong size.

My thinking is to use the REM: device which is 300 baud by default, connect the PC to the TI RS232 via a straight serial cable, and use the Transfer command in the Filer to receive the file. The problem will be to figure out on the PC end how to send that file since all terminal emulators use some kind of file transfer protocol like Kermit or Zmodem. 

An alternative that might be work is to convert your binary files to Intel HEX format.

Intel Hex Format (interlog.com)

 

Intel HEX is a text version of a binary file.  I used to use it to send binary data FROM embedded boards to the terminal in the past. 

 

I might be able to make a tool for TI-99 to convert a binary file to intel hex.

Then you would need a converter in PASCAL to convert the other way. 

 

Edited by TheBF
corrected text
  • Like 2
Link to comment
Share on other sites

2 hours ago, senior_falcon said:

It sounds like Pascal is using that workspace for other things besides to the usual interrupt routines.

It's actually not besides but instead of. Pascal handles interrupts by polling the interrupt input as a part of the execution of the PME. The interrupts aren't triggered by the CPU's interrupt hardware, but by software polling.

There are several reasons for this.

One reason is sprite automotion. Pascal supports that, but in a different and more efficient way, specifically designed to fit Pascal's more advanced sprite handling.

Another reason is DSR handling efficiency. Just like the p-system pre-searches DSR routines at boot time, finds the proper CRU and entry addresses for the routines it's interested in and stores them in a PCB (Peripheral Control Block, an ordinary PAB with some extra data) in order to quicker run the proper DSR once the system has started, it also pre-scans interrupt routines. Thus it knows in advance that the RS232 card has an interrupt routine. It creates a list of the CRU addresses of cards that do have an interrupt routine, so it doesn't waste time scanning through the entire range of CRU base addresses each time an interrupt occurs.

A third reason is the p-system has the keyboard buffer I've discussed above. That's also running in the simulated interrupt routine.

Since some of these things must occur with the p-code card disabled, as some interrupt service routines are on other cards, the code for this is in 8 K RAM, and parts of that runs with a workspace in 8 K RAM too.

  • Like 3
Link to comment
Share on other sites

5 hours ago, Vorticon said:

I wonder if BLOCKREAD would work since it does not care about the kind of data it receives. Set up an array of bytes as the receiving buffer and REM:datafile as the input file and set the block number to transfer to 12. I suspect timing would be an issue though... It does not look like a proper terminal program capable of binary file transfers has been made for UCSD Pascal that I can find anyway. 

Blockread will read blocks from blocked devices. The REMIN: and REMOUT: devices aren't blocked. They are handled one character at a time.

 

There were terminal programs designed in the 1980's, like TEP, but I never bothered much getting any, since back then I didn't have anyone to communicate with. Everybody was further away, so exchange of files was via diskette transfer, not electronic communication.

  • Like 1
Link to comment
Share on other sites

2 hours ago, TheBF said:

An alternative that might be work is to convert your binary files to Intel HEX format.

Intel Hex Format (interlog.com)

 

Intel HEX is a text version of a binary file.  I used to use it to send binary data FROM embedded boards to the terminal in the past. 

 

I might be able to make a tool for TI-99 to convert a binary file to intel hex.

Then you would need a converter in PASCAL to convert the other way. 

 

I like that idea. I'll take a close look at that document and see if I can create a conversion program in Pascal. I'm sure there are many converters on the PC end of things so that should not be an issue. 

  • Like 2
Link to comment
Share on other sites

This picture from wikipedia tells nicely how to decode intelHEX. (I am gonna do one in Forth now for myself. ;) )

I just read that it this goes back to 1973 so it fits well with our hobby. 

File example[edit]

This example shows a file that has four data records followed by an end-of-file record:

:10010000214601360121470136007EFE09D2190140
:100110002146017E17C20001FF5F16002148011928
:10012000194E79234623965778239EDA3F01B2CAA7
:100130003F0156702B5E712B722B732146013421C7
:00000001FF

  Start code   Byte count   Address   Record type   Data   Checksum

  • Like 2
Link to comment
Share on other sites

19 hours ago, apersson850 said:

Blockread will read blocks from blocked devices. The REMIN: and REMOUT: devices aren't blocked. They are handled one character at a time.

 

There were terminal programs designed in the 1980's, like TEP, but I never bothered much getting any, since back then I didn't have anyone to communicate with. Everybody was further away, so exchange of files was via diskette transfer, not electronic communication.

UNITREAD should do the trick actually.

Link to comment
Share on other sites

I'm trying to write a small assembly function to do a 2's complement conversion of an integer. Here's the code:

 .RELFUNC TWOCOMP,1
        MOV     *R10+,R1        ;GET FUNCTION ARGUMENT
        INV     R1              ;DO 2'S COMPLEMENT CONVERSION
        INC     R1
        MOV     R1,*R10         ;PLACE RESULT ON STACK
        B       *R11
        .END

 

And here's a small test program:

PROGRAM TWOCOMPTEST;
VAR
 N : INTEGER;
 
FUNCTION TWOCOMP(N : INTEGER):INTEGER; EXTERNAL;

BEGIN
 PAGE(OUTPUT);
 WRITELN('ENTER NUMBER');
 READLN(N);
 N := TWOCOMP(N);
 WRITELN('2 COMPLEMENT: ',N);
END.

 

It's giving me the wrong result. If for example you enter 19, you get -19 instead of the expected 237 and that's because the sign bit is being set by the operation and so the computer is displaying the 2' complement of 237 with a negative sign (-19).

19 = 0001 0011

2'complement is 1110 1101 = 237 

 

Any way I can fix this?

 

Link to comment
Share on other sites

1 hour ago, Vorticon said:

I'm trying to write a small assembly function to do a 2's complement conversion of an integer. Here's the code:

 .RELFUNC TWOCOMP,1
        MOV     *R10+,R1        ;GET FUNCTION ARGUMENT
        INV     R1              ;DO 2'S COMPLEMENT CONVERSION
        INC     R1
        MOV     R1,*R10         ;PLACE RESULT ON STACK
        B       *R11
        .END

 

And here's a small test program:

PROGRAM TWOCOMPTEST;
VAR
 N : INTEGER;
 
FUNCTION TWOCOMP(N : INTEGER):INTEGER; EXTERNAL;

BEGIN
 PAGE(OUTPUT);
 WRITELN('ENTER NUMBER');
 READLN(N);
 N := TWOCOMP(N);
 WRITELN('2 COMPLEMENT: ',N);
END.

 

It's giving me the wrong result. If for example you enter 19, you get -19 instead of the expected 237 and that's because the sign bit is being set by the operation and so the computer is displaying the 2' complement of 237 with a negative sign (-19).

19 = 0001 0011

2'complement is 1110 1101 = 237 

 

Any way I can fix this?

 

The two’s complement of 19 is, in fact, -19. With your ALC, you are dealing with words, so 19 = 0000 0000 0001 0011. The ones’ complement is 1111 1111 1110 1100 (-20) and the two’s complement is 1111 1111 1110 1101 (-19). You can reduce your two instructions to simply one NEG, which saves an instruction.

 

If you are needing just the right byte (LSB), you can mask off the MSB, making your code

 .RELFUNC TWOCOMP,1
        MOV     *R10+,R1        ;GET FUNCTION ARGUMENT
        NEG     R1              ;DO 2'S COMPLEMENT CONVERSION
        ANDI    R1,>00FF        ;mask off MSB
        MOV     R1,*R10         ;PLACE RESULT ON STACK
        B       *R11
        .END

 

...lee

Edited by Lee Stewart
clarification
  • Like 2
Link to comment
Share on other sites

I went ahead and created an Intel Hex format converter which takes a text file with Intel Hex data and converts it to a binary data file. Seems to work well. 

{INTEL HEX FORMAT CONVERSION PROGRAM
 CONVERTS FILE FROM HEX TO BINARY}

PROGRAM HEXCONVERT;
TYPE
 BYTE = 0..255;

VAR
 CHECKSUM, DATACHECK, DATALEN, I, BYTEVAL : BYTE;
 INFILE : TEXT;
 OUTFILE : FILE OF BYTE;
 REC : STRING[255];
 FNAME : STRING[20];
 HEXBYTE : STRING[2];
 
 {FUNCTION TO CONVERT NUMBER TO 2'S COMPLEMENT}
FUNCTION TWOCOMP(N : INTEGER) : INTEGER; EXTERNAL;

FUNCTION HEX2DEC(HVAL : STRING) : BYTE;
{CONVERT HEX DIGIT PAIR TO INTEGER VALUE}

VAR
 CH : CHAR;
 DVAL, VAL : BYTE;
 
 BEGIN
  VAL := 0;
  CH := HVAL[1];
  IF (ORD(CH) > 47) AND (ORD(CH) < 58) THEN
   DVAL := (ORD(CH) - 48) * 16
  ELSE
   DVAL := (ORD(CH) - 55) * 16;

  VAL := VAL + DVAL;
  CH := HVAL[2];
  IF (ORD(CH) > 47) AND (ORD(CH) < 58) THEN
   DVAL := ORD(CH) - 48
  ELSE
   DVAL := ORD(CH) - 55;
   
  VAL := VAL + DVAL;
  HEX2DEC := VAL;
 END; {HEX2DEC}

BEGIN
 PAGE(OUTPUT);
 WRITELN('INPUT FILENAME? ');
 READLN(FNAME);
 RESET(INFILE,FNAME);
 WRITELN('OUTPUT FILENAME? ');
 READLN(FNAME);
 REWRITE(OUTFILE,FNAME);
 
 WHILE NOT EOF(INFILE) DO
  BEGIN
   {READ A RECORD}
   READLN(INFILE, REC);
   {EXTRACT THE DATA LENGTH}
   HEXBYTE := COPY(REC,2,2);
   DATALEN := HEX2DEC(HEXBYTE);
   IF DATALEN <> 0 THEN
    BEGIN
     DATACHECK := DATALEN;
     I := 10;
     {READ EACH HEX DIGIT PAIR AND CONVERT TO INTEGER}
     WHILE I <= (DATALEN + 8) DO
      BEGIN
       HEXBYTE := COPY(REC,I,2);
       BYTEVAL := HEX2DEC(HEXBYTE);
       DATACHECK := DATACHECK + BYTEVAL;
       OUTFILE^ := BYTEVAL;
       PUT(OUTFILE);
       I := I + 2;
      END;
      WRITELN;
     {COMPUTE RECORD CHECKSUM}
     DATACHECK := TWOCOMP(DATACHECK);
     {EXTRACT RECORD CHECKSUM}
     HEXBYTE := COPY(REC,10 + DATALEN,2);
     CHECKSUM := HEX2DEC(HEXBYTE);
     {COMPARE COMPUTED AND EXTRACTED CHECKSUMS. MUST MATCH}
     IF CHECKSUM <> DATACHECK THEN
      WRITELN('WARNING! CHECKSUM ERROR!');
    END;
  END;
 CLOSE(INFILE);
 CLOSE(OUTFILE,LOCK);
 WRITELN;
 WRITELN('FILE CONVERSION DONE');
END.

 

  • Like 3
Link to comment
Share on other sites

Cool!

Typically this program would load the file into memory, so you might be able to make a simpler version that doesn't need an output file. 

It just reads the address field  and puts each data word into sequential memory "cells". 

 

I had to travel today for business but I am doing a Forth version called HEXLOAD. 

 

 

  • Like 1
Link to comment
Share on other sites

2 hours ago, TheBF said:

Cool!

Typically this program would load the file into memory, so you might be able to make a simpler version that doesn't need an output file. 

It just reads the address field  and puts each data word into sequential memory "cells". 

 

I had to travel today for business but I am doing a Forth version called HEXLOAD. 

 

 

Doing it on the fly is slower than having a binary data file ready to go where you just read and push the data to sequential memory locations. I'm thinking specifically of pattern and color picture files to be transferred to the VDP during a program run.

I do seem to have an issue though. Upon inspection of the resulting binary file, it is writing 16-bit words for each hex digit pair instead of bytes even though I set the output file to be a file of bytes. As a test I used a hex format file as below which holds the digits 1 to 5 as data:

 

:0A0000010102030405E7
:000000FF

 

The resulting output file looks like this:

 

>0001,>0002,>0003,>0004,>0005

 

I really need it to look like this:

 

>0102,>0304,>0500

 

Any thoughts?

Link to comment
Share on other sites

The smallest normal storage unit is a word, i.e. 16 bits. Even a boolean is stored as 16 bits, and that happens for the file buffer too.

A packed array[0..1] of byte will squeeze in two 8-bit pieces in one 16-bit word.

 

When messing with values like this a type definition like this one comes in handy:

 

type
  byte = 0..255;
  dual = record
    case boolean of
      false:(single: integer);
      true: (dual: packed array[0..1] of byte);
  end; (* record *)

var
  buffer: dual;

With that in place, you can use code like this:

buffer.dual[0] := one_byte_val;
buffer.dual[1] := another_byte_val;
outfile^ := buffer.single;

There two different byte-sized data pieces are stuffed into two different elements of a packed byte-sized array, which shares the same memory as one 16-bit integer.

This type of handling is more efficient for combining two 8-bit values into one 16-bit (or the opposite) than using * (multiply), div or mod to accomplish the same thing.

 

There are other applications for variant records too.

One of the more conventional is to create a file type that allows for different content within the same memory space for different blocks in the file. If you have a data file, you can declare a record where one variant maps all the fields of whatever file header stuff you need. The other variant is just an array[0..255] of integer, or maybe a packed array[0.511] of char, or whatever you need. Then you write and read the first block in the file as the first type of record, the other blocks as the second. Makes it easy to map the file data areas.

A more odd application is to define a record as being either an integer or a pointer to an integer. Then you can store any 16-bit value into your variable as an integer, then access the same variable as a pointer and read or write data to any word in memory. You create the functions peek and poke that way.

A third use is as we did above, for data manipulation. The same dual type above can also be used to implement a Pascal version of SWPB (swap bytes).

 

Finally, when handling variable data in the same type, don't forget the intrinsics moveleft, moveright and fillchar. They don't care about the type declaration and are fast for larger amounts of data. You can move data of any size and anywhere in memory, but the intrinsics will check to see if both source and destination are on even addresses and the number of bytes to move is an even number, then they'll use MOV instead of MOVB internally. Only half as many moves in that case.

 

Now I do know you are learning how to combine assembly with Pascal, but UCSD Pascal does have support for a surprisingly large amount of things that can be done already at Pascal level, although it may not immediately be obvious.

To do a 2-complement, then keep only the lower 8 bits, can be done like this:

var
  n: integer;

n := ord(odd(-n) and odd(255));

You can use boolean operators like and only on booleans. The operator odd has the odd(!) functionality of changing the type of the data to boolean but keep the data content of the integer as it was. Thus you can do boolean operations bitwise on integers directly in Pascal. The operator ord converts the type back to integer again, so you can assign the result to an integer. The output of ord(integer_value) is integer_value with type integer. Unlike if you do ord(character), where the type is changed from char to integer and returns the ordinal value of the character. If you expose 'A' to this treatment you get 65.

 

Assembly's equivalen of NEG is - (minus sign). The equivalent of INV is not. So if you want to invert an integer, then keep the lower byte, you can do like this instead:

var
  n: integer;

n := ord(not(odd(n)) and odd(255));

This behavior is not according to standard Pascal! But it does work with UCSD Pascal.

Edited by apersson850
  • Like 3
Link to comment
Share on other sites

12 hours ago, Vorticon said:

That's where I was going wrong. Byte versus word. Works fine now. Thanks!

During my code reviews one of the things I have to always make sure of if am I doing a byte operation or a word op. Am I looking for a result as a byte or a word.. because if I don't come back and review that I usually get bit, not bited.

 

 

  • Like 1
  • Haha 1
Link to comment
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.
Note: Your post will require moderator approval before it will be visible.

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