68k-mbc Userled in Pascal+/MT

by coopzone in Circuits > Electronics

535 Views, 0 Favorites, 0 Comments

68k-mbc Userled in Pascal+/MT

1-Untitled.png

This pascal program, userled, is used here as an example of how to compile a pascal program under CP/m-68k in this case on a 68k-mbc.

It's not a very well written program, it's my first pascal program in over 30 years. But it was an interesting visit to the past. As you will see, it hi-lighted one one of the things I remember from back then - don't trust the manual!

I hope it serves as an example for others, it covers some fundamental points such as linking external libraries. Small assembler routines. How to use the I/O ports (memory address's) on a 68k system.

My apologies to anyone fluent in pascal! Have fun.

Supplies

Before you can compile this pascal program your will need a working 68k-setup and you will need to install the pascal compiler, I installed in on Drive G: as per this instructable:

https://www.instructables.com/68k-mbc-Installing-a...

You will need to do the same.

You should also be familiar with ED, It seems to be the only editor on cp/m-68k at present.

Some Basics, Start by Installing Pascal Compiler

2.png

You should install the Pascal compiler by following the steps found here:

https://www.instructables.com/68k-mbc-Installing-a...

Then you should browse through the manuals for pascal, ED and General programming under CP/M-68k. You can find copies here:

Pascal+/MT: https://drive.google.com/file/d/1gr7TGt9h3lv-MA3xK...

ED and General guide to programming under CP/m-68k: https://drive.google.com/file/d/1dWkDc3giKkeKGX_Ky...

Having read through the info, the first thing to point out is an error in the Pascal+/MT book. I needed to use POKE and PEEK equivalents from BASIC programming since the 68k-mbc uses memory mapped I/O. The example in the manual uses an an external library you create using assembly code. Then it links to it from pascal by using parameters passed on the stack (Reg A7on the 68008). But they proceeded to mix up the order of these parameters. The pascal program has:

 POKE(bbb, ppp.p)

This according to the description, passes the following, return address, longint memory location, then the byte to poke. However the Peek/Poke assemble language routines retrieve the parameters like this:

poke:
move.l (a7)+,a0 * pop return address
move.w (a7)+,d7 * byte to store
move.l (a7)+,a1 * address to poke
move.b d7,(a1) * poke byte
jmp (a0) * return

Which gives: return address, byte to store and then memory location. The byte and location being reversed! Just goes to show how cutting edge this was back in the day!

In step 2 you will see how to create the external library and get the parameters in the right order.

External Libraries

Since the native install of pascal+/MT does not have a procedure/function for PEEK and POKE we first need to create our own versions. Bellow is the corrected version of the assembler code used as an EXTERNAL in our pascal program, it's pretty simple code and is very small I choose to call the library PP.S the S is what the AS68 program expects source code to be called. The steps on how to create an object file are below the code:

* PEEK and POKE Routines*
*.globl peek
* Entry point for peek routine.globl poke
* Entry point for poke routine.text
* Tell assembler we are writing code
*
peek:
move.l (a7)+,a0 * pop return address
move.l (a7)+,a1 * pop address to peek
moveq #0,d7 * clear function return
move.b (a1),d7 * get byte from memory
jmp (a0) * return

poke:
move.l (a7)+,a0 * pop return address
move.l (a7)+,a1 * address to poke
move.w (a7)+,d7 *get word/byte to stor
move.b d7,(a1) * poke byte
jmp (a0) * return.end

Note: The order of the parameters off the stack in the poke routine compared to the original.

To assemble it:

(I assume here that you booted/installed from the distribution SD-CARD image, in this case you will have the assembler on C: as part of the C installation.)

Use ED to create a new file called PP.S:

G:ED pp.s(once created, you will be at a * prompt),
enter insert mode with i (enter)
Then Cut/Paste the assembler code in(do it in small chunks, no hardware flow control sucks!)
or type it in.
Use CTRL-Z to exit insert mode.
Use e (return) to end and save your file.

You can use DIR and TYPE commands to confirm the file and contents.

Next, switch to Drive C: and use the AS68 assembler to assemble the code:

c:as68 -l -s 0: G:PP.S

When complete you can switch back to G: and you should now have an object file called PP.O. You only need to create this once, it can be re-used at the linker stage without re-assembling it every time.

Next the Pascal program.

The Userled.pas Program

In the same way you created your PP.s source program, you can use ED to either cut /paste this program or type it in line by line. If you cut/paste do it in small amounts as the terminal does not have hardware flow control it sometimes screws up.

Using ED

A:ED userled.pasAt the *,
use i(return) to insert
When done use CTRL-Z to exit insert mode.
e(return) to save and end your ED session.

Note: on several occasions I had issues when exiting ED, I suspect it was due to Cut/Paste messing ED around - on exit you may end up at the wrong default drive or worse no drive at all. You may need to reset the 68k-mbc, good news is your file will be saved ok.

PROGRAM userled;
    CONST EXWRPT = #1048572;
 STRPT = #1048573;
 EXRDPT = #1048572;
 LED = 0;
 KEY = $80;
TIME = 3000;

TYPE 
byte_ptr = ^BYTE;
pointerkludge = RECORD
     CASE BOOLEAN OF 
TRUE : (p : byte_ptr);
FALSE: (q : LONGINT)
END;

VAR
ppp : pointerkludge;  i,LEDstate : integer;

EXTERNAL PROCEDURE poke(b : BYTE; p : byte_ptr);
EXTERNAL FUNCTION peek(p : byte_ptr) : BYTE;
EXTERNAL PROCEDURE _HLT;

procedure ledst(b : BYTE);
begin
ppp.q := STRPT;
poke(LED,ppp.p);
ppp.q := EXWRPT;
poke(b,ppp.p);
end;

function userkey:BYTE;
begin
ppp.q := STRPT;
poke(KEY,ppp.p);
ppp.q := EXRDPT;
userkey := peek(ppp.p); (* read the key *)
end;

BEGIN (* Main Program *)
writeln('User LED now blinking, press USR key to exit');
while true do
begin
for LEDstate:= 0 to 1 do
begin
ledst(LEDstate);
for i:=1 to TIME do
begin
if userkey > 0 then
begin
writeln('User key pressed, bye.');
ledst(0); (*clean up *)
_HLT;
end;
end;
end;
end;
end.

It's worth a quick study of the program, it's not well written or anything, But it does have some point of note - comments in the "About the program" Step.

To compile the program:

g:mt68 userled

This should give no errors and information about the code produced, for example:

G>mt68 userled
--------------------------------------------------
Pascal MT+68 Version 3.3
Serial No. 3170-0000-000088 All Rights Reserved
Copyright (c) 1984 Digital Research, Inc.
--------------------------------------------------
PASTEMP.TOK ROUTED TO DISK:
@++++SYNTAX SCAN COMPLETE
SYMBOL TABLE INITIALIZATION
AVAILABLE MEMORY: 357200
USER TABLE SPACE: 350436
RELEASE 3.3 -- PHASE 1
###REMAINING MEMORY: 349212
RELEASE 3.3 -- PHASE 2
MT+68K
BYE 16
SIZE: 82
MYPOKE 98
SIZE: 52
DELAY 150
SIZE: 126
USERLED 276
SIZE: 198
LINES : 74
ERRORS: 0
CODE : 474
BSS : 4
PASCAL/MT+68K COMPILATION COMPLETEG>

Next the linker

Linking the Final Program

I assume you followed the how to install Pascal+/MT instructions. So you should have the link68 program on your drive G:

To link the userled.o and pp.o files together into an executable userled.68k you need to follow this step. Note the paslib.l68 file is the standard pascal library file and is required for every pascal program you write. (see manual)

link68 userled,pp,paslib.l68

The output will be like this:

G>link68 userled,pp,paslib.l68
--------------------------------------------------
LINK68 Overlay Linker Release 0.f
Serial No. XXXX-0000 All Rights Reserved
Copyright (c) 1983 Digital Research, Inc.
--------------------------------------------------
userled,pp,paslib.l68
G>

You should now have an executable program called userled.68k, run it by typing it's name. To exit use the USR button on the 68k-mbc:

G>userled
User LED now blinking, press USR key to exit

See the next section for comments on the code.

About the Program

Comments on the code

The program defines:

 EXWRPT = #1048572;
STRPT = #1048573;
EXRDPT = #1048572;
KEY = $80;

The special symbols $ and #, tell the compiler that the numbers are $=HEX and #=LONGINT

The section:

pointerkludge = RECORD
CASE BOOLEAN OF
TRUE : (p : byte_ptr);
FALSE: (q : LONGINT)
END;

declares that any variable of type pointerkludge can be either a LONGINT or a pointer. The pointer type .p is used when calling the external assembler code and the longint type .q is used when assigning a value of memory address.

How to declare external code:

EXTERNAL PROCEDURE poke(b : BYTE; p : byte_ptr);
EXTERNAL FUNCTION peek(p : byte_ptr) : BYTE;
EXTERNAL PROCEDURE _HLT;

Here we define three external routines, one is a function and gets a vale passed back. The peek and poke routines can be seen in step 1. The _HLT is a reserved function in the PASLIB.L68 library and not normally called directly - it allows for a quick halt of the program. Note the order of the parameters must line up with your library code.

This is an example of the two types of data defined above being used:

procedure mypoke(b : BYTE; a : LONGINT);
begin
ppp.q := a;
poke(b,ppp.p);
end;

First the ppp.q is used to assign a longint, then when poke is called we use the ppp.p as a pointer.

The rest of the program does not do anything special, but i have left a deliberate BUG in it for you to

have a go at fixing. It's simple enough, when the USR key is pressed no cleanup occurs so if it's pressed when the LED is ON it stays on. It would be far better to clean up and turn of the LED when exiting. Have a play ad see if you can add code to do that.

Have fun

Alternative Pascal Program UserLed.pas

This version performs the same job as the previous one, but it's more stream lined:

PROGRAM userled;                                                                         CONST                                                                                   EXWRPT = #1048572;        STRPT = #1048573;        EXRDPT = #1048572;        LED = 0;        KEY = $80;        TIME = 3000;TYPE                                                                              byte_ptr = ^BYTE;                                                                                                                                               pointerkludge = RECORD                                                                            CASE BOOLEAN OF                                                                     TRUE : (p : byte_ptr);                      FALSE: (q : LONGINT)                                                        END;VAR                                                                               ppp : pointerkludge;  i,LEDstate : integer;        EXTERNAL PROCEDURE poke(b : BYTE; p : byte_ptr);EXTERNAL FUNCTION peek(p : byte_ptr) : BYTE;EXTERNAL PROCEDURE _HLT;        procedure ledst(b : BYTE);begin           ppp.q := STRPT;        poke(LED,ppp.p);                ppp.q := EXWRPT;        poke(b,ppp.p);end;            function userkey:BYTE;begin        ppp.q := STRPT;     poke(KEY,ppp.p);             ppp.q := EXRDPT;     userkey := peek(ppp.p); (* read the key *)end;BEGIN (* Main Program *)                                                        writeln('User LED now blinking, press USR key to exit');while true do begin      for LEDstate:= 0 to 1 do    begin        ledst(LEDstate);        for i:=1 to TIME do        begin                if userkey > 0 then                begin                   writeln('User key pressed, bye.');                   ledst(0); (*clean up *)                   _HLT;                end;        end;    end;  end;  end.