68k-mbc Userled in Pascal+/MT
Intro: 68k-mbc Userled in Pascal+/MT
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.
STEP 1: Some Basics, Start by Installing Pascal Compiler
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 addressmove.w (a7)+,d7 * byte to storemove.l (a7)+,a1 * address to pokemove.b d7,(a1) * poke bytejmp (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.
STEP 2: 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) * returnpoke: 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.
STEP 3: 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 insertWhen 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.3Serial No. 3170-0000-000088 All Rights ReservedCopyright (c) 1984 Digital Research, Inc.--------------------------------------------------PASTEMP.TOK ROUTED TO DISK: @++++SYNTAX SCAN COMPLETESYMBOL TABLE INITIALIZATIONAVAILABLE MEMORY: 357200USER TABLE SPACE: 350436RELEASE 3.3 -- PHASE 1###REMAINING MEMORY: 349212RELEASE 3.3 -- PHASE 2MT+68KBYE 16 SIZE: 82MYPOKE 98 SIZE: 52DELAY 150 SIZE: 126USERLED 276 SIZE: 198LINES : 74ERRORS: 0CODE : 474BSS : 4PASCAL/MT+68K COMPILATION COMPLETEG>
Next the linker
STEP 4: 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.fSerial No. XXXX-0000 All Rights ReservedCopyright (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>userledUser LED now blinking, press USR key to exit
See the next section for comments on the code.
STEP 5: 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;
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
STEP 6: 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.