Jump to content
IGNORED

Pascal on the 99/4A


apersson850

Recommended Posts

2 minutes ago, Lee Stewart said:

 

In getbyte, if the MSB of R6 is ignored, this is not a problem, but, as far as I can tell, the MSB of R6 is indeterminate unless it has been cleared before invoking the procedure.

 

...lee

You got those eagle  eyes.

 

So this is similar to the case of C@ in our Forth systems where we would use:

 

SRA R6,8   rather than  SWPB R6 

 

The GCC team was fighting with this for a while too. 

  • Like 2
Link to comment
Share on other sites

3 hours ago, Lee Stewart said:

 

In getbyte, if the MSB of R6 is ignored, this is not a problem, but, as far as I can tell, the MSB of R6 is indeterminate unless it has been cleared before invoking the procedure.

 

...lee

I actually realized that I was not clearing R6 prior to getting a byte this morning :) However, this is not the issue because the MSB is indeed ignored. I should still clear it for proper form anyway.

  • Like 1
Link to comment
Share on other sites

With all this business with file transfers, I found I had an acute need for a HEX file viewer to check the integrity of received files. There is no such built-in utility in UCSD Pascal so I figured I'll put a quick and dirty one together. Below is the bulk of it. It's not yet finished but this is enough to test the functionality. Here it's only reading the first block of a file and display it in hex. Or at least that's what is supposed to happen, and guess what, it's not working...

(* hex file viewer *)

program hexview;
type
 byte = 0..255;

var
 infile : file;
 fname : string;
 cblock, blocksin : integer;
 buffer : array[0..511] of byte;

function dec2hex(n : integer) : char;
begin
 if n < 10 then
  dec2hex := chr(n + 48)
 else
  if n >= 10 then
   dec2hex := chr(n + 65);
   
 end; (* dec2hex *)
  
function getkey : integer; external;

procedure showblock;
var
 i, x, y : integer;

begin
 page(output);
 x := 1;
 y := 1;
 for i := 0 to 511 do
  begin
   gotoxy(x, y);
   write(dec2hex(buffer[i] div 256));
   write(dec2hex(buffer[i] mod 256));
   x := x + 3;
   if x > 71 then
    begin
     x := 1;
     y := succ(y);
    end;
  end;
end; (* showblock *)

begin
 page(output);
 writeln(chr(7),'file to view:');
 readln(fname);
 reset(infile, fname);

 cblock := 0;
 blocksin := blockread(infile, buffer, 1, cblock);
 showblock;
 repeat
 until getkey <> 255;
end. (* hexview *)

 

What it ends up displaying is non-Hex characters, which does not make sense since the buffer is supposed to fill up with 512 byte values. I'm sure I'm missing something basic here...

 

  • Like 3
Link to comment
Share on other sites

 

I'm biased by you know what, but you might try factoring this into some smaller pieces.

Here are the pieces used in a Forth system to chop an integer into a set of ascii digits. 

 

 TODIGIT converts a binary digit to ascii but handles the gap between 0 .. 9 and A .. F. 

 

Then you need a way to divide and modulo-divide an integer by the radix, in this case 16.

The quotient is retained,  the modulus is passed to TODIGIT and the ascii character is placed in a buffer starting from right to left. 

Lather rinse and repeat until there is nothing left in the quotient. 

 

In very bad pseudo  code: (I don't think like this any more) :) 

 

function TODIGIT  ( X : integer) 

           var out : char

           IF   X > 9 THEN  X=X+7  { handles ABCDEF } 

           out := x + 48  { ascii 0 } 

          return out

end;    

 

procedure MAKE_NUMBER ( inputinteger ) 

           var hp, quotient , modulus , length : integer 

           var hbuff()   6 bytes                 { should be global and big enough to hold a 16 bit number } 

           hp := 6                                   { called a hold pointer in Forth } 

           length:=0 

          repeat 

             quotient := input / 16            { if you made 16 a global variable you code convert to different bases } 

             modulus := input mod 16 

             hbuff(hp):=todigit(modulus)  { put the ascii digit in the buffer } 

             hp:=hp-1                             { move the pointer to the next digit location

             inc(length) 

          until quotient=0 

          return hbuff() , length    { length needs to global to make this easy to do in Pascal ? )

end;

           

Maybe that will give some helpful hints. ???   

      

Link to comment
Share on other sites

Oops... I'm dividing by 256 instead of 16.

write(dec2hex(buffer[i] div 16));
write(dec2hex(buffer[i] mod 16));

 

Also I forgot to subtract 10 from the A-F conversion

if n >= 10 then
   dec2hex := chr(n + 65 - 10);

 

Still not working though... What else am I missing???

Link to comment
Share on other sites

18 minutes ago, Vorticon said:

Oops... I'm dividing by 256 instead of 16.

write(dec2hex(buffer[i] div 16));
write(dec2hex(buffer[i] mod 16));

 

Also I forgot to subtract 10 from the A-F conversion

if n >= 10 then
   dec2hex := chr(n + 65 - 10);

 

Still not working though... What else am I missing???

Ok one bug down. :)

Only the results of the mod operation , passed through the digit convertor goes into the buffer. 

 

This name dec2hex is a bit misleading since a binary number you are processing is neither BASE 10 nor  BASE 16. 

This process of converting it to text, is what will determine if it decimal or hexadecimal or binary or whatever. 

 

This is should work and it's smaller!  

function todigit(n : integer) : char;
begin
 if n > 9 then
    n := n + 7
   todigit := chr(n + 48);
end; (*todigit *)
Link to comment
Share on other sites

And then remember that an HEX  integer can hold up to 4 digits, a HEX byte can be up to 2 digits.

So if you read the file as integers you need a 4 digit buffer, if as bytes a 2 byte buffer

You put the results of the characters into the buffer backwards. ie: right to left

 

So you need to read a byte, process 2 chars into the buffer. The leftmost chars could be a zero. 

That is why I recommended the other procedure to "make a number" which will be a little buffer of characters that you can print to the screen. 

 

Link to comment
Share on other sites

Thanks for the tips! It was a buffer issue. While it was declared as an array of bytes, it's still read as an integer, so each element is actually 2 bytes. This always gets me. There is no need however to invert the read order. 

Here's the functional version:

(* hex file viewer *)

program hexview;
type
 byte = 0..255;

 byteword = record
  case boolean of
   true : (value : integer);
   false : (bytes : packed array[0..1] of byte);
  end;

var
 infile : file;
 fname : string;
 cblock, blocksin : integer;
 buffer : array[0..255] of byteword;

function dec2hex(n : integer) : char;
begin
 if n >= 10 then
  n := n + 7;
 dec2hex := chr(n + 48);
end; (* dec2hex *)
  
function getkey : integer; external;

procedure showblock;
var
 i, x, y : integer;

begin
 page(output);
 x := 1;
 y := 1;
 for i := 0 to 255 do
  begin
   gotoxy(x, y);
   write(dec2hex(buffer[i].bytes[0] div 16));
   write(dec2hex(buffer[i].bytes[0] mod 16));
   x := x + 3;
   gotoxy(x, y);
   write(dec2hex(buffer[i].bytes[1] div 16));
   write(dec2hex(buffer[i].bytes[1] mod 16));
   x := x + 3;
   if x > 71 then
    begin
     x := 1;
     y := succ(y);
    end;
  end;
end; (* showblock *)

begin
 page(output);
 writeln(chr(7),'file to view:');
 readln(fname);
 reset(infile, fname);

 cblock := 0;
 blocksin := blockread(infile, buffer, 1, cblock);
 showblock;
 repeat
 until getkey <> 255;
end. (* hexview *)

 

  • Like 3
Link to comment
Share on other sites

Oh I see now that I actually read the rest of your code. You use the quotient as the 1st digit. 

OK. Very specific number conversion.

I will leave it with you as an exercise to to make an integer version. :) 

 

I am happy that I am able to help in some small way. 

 

Link to comment
Share on other sites

9 hours ago, Vorticon said:

With all this business with file transfers, I found I had an acute need for a HEX file viewer to check the integrity of received files. There is no such built-in utility in UCSD Pascal...

Sorry to ruin your party, but yes there is.

On the Utilities disk you can find the program PATCH. That can be used to view and change files of any type. It can display file content as hex or ASCII data. You can jump back and forth between blocks in the file, or inspect a unit, if you for example want to look at the disk directory.

  • Like 3
  • Thanks 1
Link to comment
Share on other sites

I checked out the PATCH utility and it's pretty comprehensive. I really like the way they formatted the screen display. I think there is no point in me duplicating that work now that I know how to do it. In a sense I'm glad you pointed out PATCH after I started Hexview :lol:

Now XMODEM is next...

PS: this is turning into quite the rabbit hole. It all started with me wanting to transfer a binary file from the PC to the pcode system and here we are still not quite there yet. Quite fun I might add (NEERDS! * which movie is that from? 😜*).

  • Like 6
Link to comment
Share on other sites

OK so I had to finish Hexview, so here it is. It's just a block viewer which allows you to go forward, backward, to the first block and the last one. Simpler to use that PATCH which I'll reserve for more involved file manipulation.

Spoiler
(* hex file viewer *)
(* By Walid Maalouli *)
(* March 7, 2024 *)

program hexview;
type
 byte = 0..255;
 
 byteword = record
  case boolean of
   true : (value : integer);
   false : (bytes : packed array[0..1] of byte);
  end;

var
 infile : file;
 fname : string;
 cblock, blocksin, lblock, key : integer;
 buffer : array[0..255] of byteword;

function dec2hex(n : integer) : char;
begin
 if n >= 10 then
  n := n + 7;
 dec2hex := chr(n + 48);
 end; (* dec2hex *)
  
function getkey : integer; external;

procedure showblock;
var
 i, x, y : integer;

begin
 page(output);
 i := 0;
 for x := 5 to 78 do
  begin
   gotoxy(x, 1);
   write(i, ':');
   i := succ(i);
   x := x + 4;
  end;
 i := 0;
 for y := 2 to 19 do
  begin
   gotoxy(1, y);
   write(i);
   gotoxy(4, y);
   write(':');
   i := i + 30;
  end;
  
  gotoxy(1, 22);
  write('block #: ',cblock);
  gotoxy(1, 23);
  write('(F)orward (B)ack (S)tart (E)nd (Q)uit');

 x := 5;
 y := 2;
 for i := 0 to 255 do
  begin
   gotoxy(x, y);
   write(dec2hex(buffer[i].bytes[0] div 16));
   write(dec2hex(buffer[i].bytes[0] mod 16));
   write(dec2hex(buffer[i].bytes[1] div 16));
   write(dec2hex(buffer[i].bytes[1] mod 16));
   x := x + 5;
   if x > 78 then
    begin
     x := 5;
     y := succ(y);
    end;
  end;
end; (* showblock *)

begin
 page(output);
 writeln(chr(7),'file to view:');
 readln(fname);
 reset(infile, fname);

 cblock := 0;
 lblock := 0;
 while not(eof(infile)) do
  begin
   blocksin := blockread(infile, buffer, 1, cblock);
   cblock := succ(cblock);
  end;
 lblock := pred(cblock);
 cblock := 0;
 
 repeat
  fillchar(buffer, 512, chr(0));
  blocksin := blockread(infile, buffer, 1, cblock);
  showblock;
  repeat
   key := getkey;
  until key in[66, 69, 70, 81, 83];
  case key of
   83 : cblock := 0;
   70 : if cblock < lblock then
         cblock := succ(cblock);
   66 : if cblock > 0 then
          cblock := pred(cblock);
   69 : cblock := lblock;
  end;
 until key = 81;
 close(infile);
end. (* hexview *)


 

 

 

HEXVIEW.jpg.78d60691edd256db68eebcc873f93943.jpg

My OCD has been appeased...

 

  • Like 4
  • Thanks 1
Link to comment
Share on other sites

I'm trying to set up a timer function using the TIME intrinsic and I'm getting weird results. TIME returns the system's clock into a 32 bit integer split into a high integer and low integer. The 32bit integer represents 1/60th of second intervals. TIME(HIINT, LOWINT).

 

program timetest;
type
 longint = record
  case boolean of
   true : (value : integer[10]);
   false : (intval : packed array[0..1] of integer);
  end;
  
var
 stime, etime : longint;
 stimestr, etimestr : string;
 
begin
 (* start time *)
 time(stime.intval[0], stime.intval[1]);
 writeln;
 (* end time *)
 time(etime.intval[0], etime.intval[1]);
 str(stime.value, stimestr);
 str(etime.value, etimestr);
 writeln('start time --- end time');
 writeln(stimestr, '---', etimestr);
end.

 

I'm getting ridiculously large differences in time which make no sense. Where am I going wrong here?

 

Link to comment
Share on other sites

It looks like changing the long integer definition from integer[10] to integer[4] works. I'm not clear as to why though because that index value is supposed to indicate the number of decimals that long integer can represent. I would love an explanation if anybody has one. Here's the modified test code which measures a 40 second delay. 

 

program timetest;
type
 longint = record
  case boolean of
   true : (value : integer[4]);
   false : (intval : packed array[0..1] of integer);
  end;
  
var
 stime, etime : longint;
 stimestr, etimestr : string;
 
begin
 page(output);
 writeln('starting now!');
 (* start time *)
 time(stime.intval[0], stime.intval[1]);
 repeat
  (* end time *)
  time(etime.intval[0], etime.intval[1])
 until (etime.value - stime.value) > 2400;;
 str(stime.value, stimestr);
 str(etime.value, etimestr);
 writeln('start time --- end time');
 writeln(stimestr, '---', etimestr);
end.

 

That said, that is not a reliable timer and it appears that it's heavily affected by what goes on inside the timer loop as additional statements make the timer run much slower. Below is a snippet of real life code where a value of 70 represents 40 seconds instead of the expected 2400!

 

 time(stime.intval[0], stime.intval[1]); {start time}
   repeat
    getbyte(comcode, flag); {wait for remote nak code}
    time(itime.intval[0], itime.intval[1]); {interval time}
    if (itime.value - stime.value) > 70 then {40 second timeout limit}
     begin
      timeout := true;
      exit(sendfile);
     end;
   until comcode = 21;

 

I should also point out that this is being tested out in emulation and I have the sneaky feeling that the results will be different on real hardware.

  • Sad 1
Link to comment
Share on other sites

Could it be that integer[4] is an integer of 4 bytes, ie: 32 bits.  This would be a typical "long" integer in C and other languages on a 16 bit CPU. 

 

Since you have access to an Assembler, you might want to try something I did.  You "install" a 32 bit timer that runs on the VDP interrupt. 

It runs continuously and can count up to 4.1 billion 16mS "ticks".

 

Then you read that 32bit integer anytime you want to, and subtract from the next time you read it.

Now if the PASCAL system is disabling interrupts a great deal it will mess with the timing.

I can't speak to that, but it will probably be better than a pure S/W solution. 

 

The timer is only 3 instructions and you "install" the address of the code in >83C4 and it runs every 16mS

Excuse the Forth in a Pascal thread but you can read it I'm sure.

 

(CELL+ below is just add 2 to the address T)

 HEX
 CREATE T 0 , 0 ,  \ 2 cells of memory for 32 bit number

: INSTALL ( isr-addr -- ) 83C4 ! ; \ put an address into >83C4

: RESET32  ( -- )  0 0 T 2! ; \ put zero in T 

HERE  \ headless code fragement begins at HERE 
   T CELL+ @@ INC,
   OC IF,
        T @@ INC,
   ENDIF,
   RT,
( address) INSTALL   \ put address in memory

 

  • Like 1
Link to comment
Share on other sites

12 minutes ago, TheBF said:

Could it be that integer[4] is an integer of 4 bytes, ie: 32 bits.  This would be a typical "long" integer in C and other languages on a 16 bit CPU.

That was my thinking as well, but that's not the definition quoted in the manual where the index can be as high as 36!

 

I like your interrupt driven timer although I'm not sure it will work on the p-system. Only way to find out is test it out. OC IF tests for overflow so as to know when to increment the high-word, correct? I need to find a safe place to stash that routine though.

Link to comment
Share on other sites

Posted (edited)

The p-systems time function runs on the VDP interrupt, so it counts 50/60 times per second.

It's reasonably accurate as long as you stay out of stuff which doesn't allow interrupts. Disk accesses are the main culprit. The p-system itself doesn't turn of interrupts for no good reason.

 

Long integers are a completely different structure compared to normal integers. The don't mix the way you tried, but within limits you can assign one to the other. Since the two parts should be handled as unsigned you also need to do some handling of that too.

I haven't tried it, but I think something like this should work.

time(high,low);
long1 := high;
long2 := low.
if long1<0 then long1 := long1+65536;
if long2<0 then long2 := long2+65536;
longtime := long1*65536+long2;

I haven't checked if long integers are BCD coded, but that could be. I've very rarely used them.

 

Anyway, this is the beginning of the code that runs on each VDP interrupt under the p-system. Now just like many other programs do their LIMI 2 LIMI 0 sequnce when they are ready to be interrupted, the p-system may not run the interrupt service right after the machine code instruction where it occurred, but it will run it frequently enough to serv all 50/60 of them under normal circumstances.

INTSRV	SBO	2
	MOVB	@>8802,@VDPSTCOPY
	INC	@LOWTIMER
	JNC	SKIPHI
	INC	@HIGHTIMER
SKIPHI	INCT	@SCRBLANK 

The code is at 32DC if you want to look at it. Branch vector at 2AAA so the workspace is 28B6.

Edited by apersson850
Link to comment
Share on other sites

3 hours ago, TheBF said:

The timer is only 3 instructions and you "install" the address of the code in >83C4 and it runs every 16mS

That will not work with the p-system, since it changes 83C4 itself. I looked at a printout of a disassembly of the p-system when it was running, a printout I did 40 years ago or so. At the time the program disassembled the 8300 memory area and hit 83C4, the value in 83C4 was 5AD8. That's the address of the RS232 write character routine in the p-system's BIOS. A bit too much of a coincidence that it points to the serial output code when the listing was printed.

Looking at the value of 83C4 in Classic 99 it's completely different and also changes all the time.

  • Thanks 1
Link to comment
Share on other sites

27 minutes ago, apersson850 said:

That will not work with the p-system, since it changes 83C4 itself. I looked at a printout of a disassembly of the p-system when it was running, a printout I did 40 years ago or so. At the time the program disassembled the 8300 memory area and hit 83C4, the value in 83C4 was 5AD8. That's the address of the RS232 write character routine in the p-system's BIOS. A bit too much of a coincidence that it points to the serial output code when the listing was printed.

Looking at the value of 83C4 in Classic 99 it's completely different and also changes all the time.

Well nuts... I had literally just finished writing an assembly timer routine based on @TheBF's suggestion...

intvect .equ    83c4h
        
        .proc   timer
        .org    0
        .def    htime, ltime
        inc     @ltime
        jnc     return
        inc     @htime
return  b       *r11
htime   .word
ltime   .word

        .proc   treset
        .ref    htime, ltime
        clr     @htime
        clr     @ltime
        b       *r11
        
        .proc   tinstall
        clr     @intvect
        b       *r11
        
        .proc   gettimer,2
        .ref    htime,ltime
        mov     *r10+,r1
        mov     *r10+,r2
        mov     @htime,*r2
        mov     @ltime,*r1
        b       *r11

        .end

 

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