Jump to content
IGNORED

GCHAR equivalent in UCSD Pascal?


Recommended Posts

Hello!

 

I've decided to start learning/coding in UCSD Pascal and figured my first project would be converting a TI BASIC game.  With that, I do not see an equivalent to CALL GCHAR in the compiler documentation.  Any ideas?

 

Thanks,

Larry

Edited by LarryFromBuffalo
Link to comment
Share on other sites

This is awesome! Welcome to the club! I suggest you post in the Pascal thread in this forum for continuity 

 

Regarding GCHAR, there isn't a direct equivalent in UCSD Pascal on the TI, but it should be pretty straightforward to code a small assembly procedure to achieve the same result. Let me look into it.

Link to comment
Share on other sites

Here's a test program that includes a GCHAR function. I know it seems a bit complicated but I'll make it into a precompiled UNIT which you can include in any program thar requires GCHAR and not worry about the details (at least for now as you get started in your UCSD Pascal journey). I made GCHAR's parameters exactly as TI BASIC expects them, but this could lead to potential confusion in UCSD Pascal where the screen is zero-based. Also GOTOXY has the parameters reversed compared to GCHAR. I'm thinking it would be better if GCHAR was made to take parameters like GOTOXY. 

 

{gchar test program}

program gchartest;
uses support;

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

procedure vsbr(vdpadr : integer; byteval : dual); external;

function gchar(y, x : integer) : integer;
{x : 1..32, y : 1..24}

var
 vdpadr : integer;
 byteval : dual;
 
begin
 vdpadr := 2048 + (((y - 1) * 32) + (x - 1));
 vsbr(vdpadr, byteval);
 gchar := byteval.bytes[1];
end; (* gchar *)

begin
 page(output);
 set_screen(2);
 gotoxy(15, 11);
 write('*');
 gotoxy(0, 2);
 write('character code: ',gchar(12, 16));
end. (* gchartest *)
 

 

PWORK.dsk

  • Like 4
Link to comment
Share on other sites

3 hours ago, Vorticon said:

Here's a test program that includes a GCHAR function. I know it seems a bit complicated but I'll make it into a precompiled UNIT which you can include in any program thar requires GCHAR and not worry about the details (at least for now as you get started in your UCSD Pascal journey). I made GCHAR's parameters exactly as TI BASIC expects them, but this could lead to potential confusion in UCSD Pascal where the screen is zero-based. Also GOTOXY has the parameters reversed compared to GCHAR. I'm thinking it would be better if GCHAR was made to take parameters like GOTOXY. 

 

{gchar test program}

program gchartest;
uses support;

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

procedure vsbr(vdpadr : integer; byteval : dual); external;

function gchar(y, x : integer) : integer;
{x : 1..32, y : 1..24}

var
 vdpadr : integer;
 byteval : dual;
 
begin
 vdpadr := 2048 + (((y - 1) * 32) + (x - 1));
 vsbr(vdpadr, byteval);
 gchar := byteval.bytes[1];
end; (* gchar *)

begin
 page(output);
 set_screen(2);
 gotoxy(15, 11);
 write('*');
 gotoxy(0, 2);
 write('character code: ',gchar(12, 16));
end. (* gchartest *)
 

 

PWORK.dsk 180 kB · 1 download

Curiosity question.

 

Is there a way to access the screen management variables in the P-code system?

Where I am going is; is there any point is seeing what mode the system thinks it is in?  (Text, graphics 1, as the two most common ones) 

That way your gchar function could maybe know the line length so that 32 would be replaced by a variable that "knows" the current line length. 

 

Asking for a friend :)

 

 

Link to comment
Share on other sites

7 hours ago, TheBF said:

Curiosity question.

 

Is there a way to access the screen management variables in the P-code system?

Where I am going is; is there any point is seeing what mode the system thinks it is in?  (Text, graphics 1, as the two most common ones) 

That way your gchar function could maybe know the line length so that 32 would be replaced by a variable that "knows" the current line length. 

 

Asking for a friend :)

 

 

Unlike the standard OS, the Pascal VDP registers can be read easily as they are located at >2810 in low memory, so it should be pretty simple to determine which screen mode the system is in by reading VR1 and then take that into account when calculating screen coordinates in the Screen Image Table. The question though is why would you need GCHAR in text mode?

Link to comment
Share on other sites

3 hours ago, Vorticon said:

 The question though is why would you need GCHAR in text mode?

Excellent.

Why?  For that time that you need it and it won't work as expected.

However if that is unthinkable then you are correct.

Don't write code for stuff that "might" happen. 

Link to comment
Share on other sites

The whole thing is further complicated by the fact that the p-system really is an 80 column screen system. All printout is on the 80 column screen, and then you select which part of that you want to show on your physical screen. So you have to decide if you want to read things from the physical or logical screen.

The operating system also has a data area with a pointer to the 80 column screen and the physical screen's height and width. You can also see which is the current window shown on the screen.

 

Here's an extract of some system data. The simplest way to see if you are in text or graphics mode is to check the value at 2CC6H. If it's 40 you are in text mode. If it's 32 it's graphics mode.

 

2CAC AA00    DATA DSR validation code
2CAE 0080    
2CB0 81F0    DATA VDP copy R1
2CB2 8202    DATA VDP copy R2
2CB4 832F  / DATA VDP copy R3
2CB6 8400    DATA VDP copy R4
2CB8 8518    DATA VDP copy R5
2CBA 8600    DATA VDP copy R6
2CBC 001F    DATA VDP copy R7
2CBE 2000    DATA Pointer 80 column screen
2CC0 0017    DATA Current line
2CC2 0000    DATA Current column
2CC4 2730 '0 DATA Current 80 column address
2CC6 0028  ( DATA Screen width
2CC8 0018    DATA Screen height
2CCA 0000    DATA Current screen window
2CCC 0730  0 DATA Number of bytes in 23 lines (for scroll)

 

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

Now I don't know what kind of game you are planning to convert, but if it's something that has some kind of action, then you should be aware of that if you put such a function as @Vorticon provided above in a pre-compiled unit, then you have a very convenient way of handling it. But you also have the absolutely slowest possible call command to use it. A global external inter-segment call is by far the most complex way there is to invoke a procedure.

So if you intend to call this gchar equivalent frequently I recommend you include it in your main program instead.

Link to comment
Share on other sites

5 hours ago, apersson850 said:

Now I don't know what kind of game you are planning to convert, but if it's something that has some kind of action, then you should be aware of that if you put such a function as @Vorticon provided above in a pre-compiled unit, then you have a very convenient way of handling it. But you also have the absolutely slowest possible call command to use it. A global external inter-segment call is by far the most complex way there is to invoke a procedure.

So if you intend to call this gchar equivalent frequently I recommend you include it in your main program instead.

Good point, with the understanding that including it in the main program will require linking the external vsbr procedure after each compilation. 

Link to comment
Share on other sites

4 hours ago, apersson850 said:

If you aren't going to use it too frequently you can write the whole thing in Pascal. Then you don't need the linking.

But otherwise you're correct.

How would you code it without assembly?

Link to comment
Share on other sites

You already know how to do poke and peek in Pascal, with the dual data type.

Poke the VDP read address port to the VDP, then peek the VDP read data port from the VDP.

If you want to write to VDP RAM or to VDP registers, you need to set the second or first bit in the address. Remember that UCSD Pascal can do bitwise logic functions by using the odd and ord functions.

So you can do VDP_address := ord(odd(16384) or odd(your_address)); where both VDP_address and your_address are integers. That will set the VDP address for a write (which you don't need for GCHAR, of course).

Also note that you'll need to create a swapbyte function, but there you can do a dual data type with an integer and a packed array[0..1] of byte, where byte is 0..255, to handle that.

 

A word of caution! In the example above the constant 4000H is represented by the 2's complement decimal number 16384. If you want to get the equivalent of 8000H into a decimal number, you can not use the constant -32768! Since that number can't be negated without an overflow (there is no +32768 in the range of 2's complement 16-bit numbers) it will be handled like a long integer constant, occupying two words, not one. But you can use 32767+1, as UCSD Pascal does no overflow detection on integer arithmetic. As long as you don't divide by zero, that is.

 

Note that for this direct VDP access to work you need to manually set the code type to M_9900, since if it's M_PSEUDO then the code usually will be loaded in VDP RAM. If you then change the read address the PME will get lost about where the code is. The PME will only set the VDP read address when performing a jump.

If you link in an assembly procedure into your Pascal program the linker will set the code type automatically, since assembly can't run from VDP RAM. But the compiler can't know which memory you'll access in a Pascal program, so there it's not automatic.

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

Got it. The trade off here then is having to run the SETLTYPE utility after each compilation to change the program type to M_9900. 

The trick about overflow is neat!

We should really move that conversation to the Pascal thread. Perhaps @OLD CS1 can do that?

Link to comment
Share on other sites

@LarryFromBuffalo Here's GCHAR included in a compiled unit called GRAPHCHAR in the GRAPHICS library. As you can see below you don't need to know how the unit itself works, just how to use it. Just make sure that you have a file called USERLIB.TEXT on your default drive which contains the name of the library, GRAPHICS.CODE, before compiling your program.

 

{gchar test program}

program gchartest;
uses support,
{$u graphics.code} graphchar;

begin
 page(output);
 set_screen(2);
 gotoxy(15, 11);
 write('*');
 gotoxy(0, 2);
 write('character code: ',gchar(12, 16));
end. (* gchartest *)

 

 

PWORK.dsk

  • Like 1
Link to comment
Share on other sites

5 hours ago, Vorticon said:

We should really move that conversation to the Pascal thread. Perhaps @OLD CS1 can do that?

Is there really any reason to not allow a Pascal-related question to have its own thread?  I figured combining this thread with another thread would clutter the other thread with various questions, and would attempt to use that thread as a sub-sub-forum.

Link to comment
Share on other sites

I worry about information fragmentation, but I'll leave it up to you. With such a limited number of Pascal users I feel it would be good to have all related knowledge in one place at the risk of unintentionally creating a rogue subforum 🏴‍☠️

Link to comment
Share on other sites

6 hours ago, Vorticon said:

Got it. The trade off here then is having to run the SETLTYPE utility after each compilation to change the program type to M_9900. 

Yes, but using Setltype (Set Language Type) is easier than doing the linking.

2 hours ago, Vorticon said:

@LarryFromBuffalo Here's GCHAR included in a compiled unit called GRAPHCHAR in the GRAPHICS library. As you can see below you don't need to know how the unit itself works, just how to use it. Just make sure that you have a file called USERLIB.TEXT on your default drive which contains the name of the library, GRAPHICS.CODE, before compiling your program.

As always (?) with the p-system, there are more to it than that. Let's recap how units work.

 

Separate compilation

One of the advantages of the unit concept in UCSD Pascal is that you can compile parts of your program separately. If the program is very large that's a necessity, since our little computer doesn't have memory enough to compile too large programs.

By compiling a unit instead of a program, you create a support file for your program. The unit comtains an interface part which defines to the main program the data and procedures available and an implementation part which contains the code to execute. The user of a unit doesn't need to know how the procedures are implemented, only how to call them. You can even change the implementation without having to re-compile the programs using the unit, as long as the interface is the same.

 

Compiling the unit

This is as easy as compiling a program. Just run the compiler and it creates the code file for you.

The basic structure of a unit is as below.

 

unit example;

interface

type
  extype = record
    this, that: integer;
  end;

procedure doexample(handle: extype);

implementation

procedure doexample;
begin
  (* Code *)
end;
end.

 

Compiling the program

When a program uses a unit, it must know where to find the unit's code file. Easiest is if it's included in the *SYSTEM.LIBRARY file. Then it's all automatic. (Note that the * in the filename represents the system volume, i.e. the boot disk that was in #4: when starting the system. It's not part of the file name, but equivalent to passys: if the system disk is called passys. In such a case, *SYSTEM.LIBRARY is equivalent to PASSYS:SYSTEM.LIBRARY or #4:SYSTEM.LIBRARY, provided pasys is actually mounted in drive #4:.)

In the case the unit is in a separate code file you need to tell the compiler where to find it.

In the example below the first unit is in *SYSTEM.LIBRARY but the second is not.

program unittest;

uses
  support;
  (*$U develop:exfile.code *) example;

 

Running the program

When you run the program the same is true: If the unit is in *SYSTEM.LIBRARY everything works automatically. The build_env procedure in the operating system will locate the units referred to by your program and build the run-time environment records and vectors so the system can locate the files needed when they are needed.

However, if the unit is still in a separate file, you need to tell the build_env where to locate it. This can be done by a text file listing the library files to search, just as @Vorticon showed above. In this example the content of the text file would simply be this:

develop:exfile.code

If you do store this in the text file called *USERLIB.TEXT (note that it must be on the system disk), then the program will find your unit. However, when you are designing your program you may not want to mess with a perhaps already existing *USERLIB.TEXT file. You can create a separate library reference file for your experiments. The content would still be like above, but you can store it in DEVELOP:EXLIBS.TEXT. But that's not where the run-time system will look by default. To make this work at runtime you have to use an execution option.

You set it up by pressing X (for eXectue) at the top system level. But instead of entering a file name of a program to execute, you type in L=develop:exlibs.text. Now you've told the system you have a user-defined library reference file, so it can find it and through the information in it find your unit. You can list more than one unit in such a file, if you have your units spread all over the place.

  • Like 3
Link to comment
Share on other sites

1 hour ago, Vorticon said:

I worry about information fragmentation, but I'll leave it up to you. With such a limited number of Pascal users I feel it would be good to have all related knowledge in one place at the risk of unintentionally creating a rogue subforum 🏴‍☠️

I will leave it up to you guys and the thread owner.  Fragmentation should not be a problem with a competent search.  ::shrugs::

  • Like 1
Link to comment
Share on other sites

THANK YOU for your responses!  The game I am re-writing is a game called Goblin, which originally appeared in COMPUTE! magazine.  Thankfully, I learned Pascal (Borland Turbo) in college, so that part is pretty straightforward.  It's the TMS9900 assembly integration that I am looking forward to learning as that was WELL above my head when I was 12! :-)  

 

 

Again, THANK YOU!

  • Like 4
Link to comment
Share on other sites

13 minutes ago, LarryFromBuffalo said:

The game I am re-writing is a game called Goblin, which originally appeared in COMPUTE! magazine.

Neat game from a neat genre of games.  Was one of the first type-ins I ever did. 🍿

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