Page 1 of 4
LUCIDATA Pascal for 6502
Posted: Mon Oct 11, 2021 1:24 pm
by BillG
LUCIDATA sold Pascal language systems for the 6800 FLEX and 6809 FLEX and UniFLEX environments. Their compiler generated programs in the form of P-code for a stack machine to be interpreted by their run-time package.
One of my projects is an effort to analyze the P-code structure and interpreter with the ultimate aim of eventually producing an interpreter running on the 6502. Because their compiler is supplied in P-code running on the same interpreter, it is hoped that when the interpreter is done, the compiler comes along for the the ride.
While I do not understand how the stack or their environment works yet, I have identified a number of the interpreted instructions. It is hoped that the instructions can provide insight into the particulars of the stack and environment.
A P-code binary file is structured as follows:

- BINARY.PNG (7.3 KiB) Viewed 4660 times
from compiling this program:
The number in red is the highest numbered instruction used by the program. The number in yellow is the size of the program image in bytes. The number in green is the number of entries in some kind of table. The number(s) in blue is that table of 4-bytes per entry. Finally, the numbers in brown is an array of P-code instructions.
Opcode $06 appears to be setting up the initial stack frame.
It is not known what the six bytes are. Some of them can be presumed to be for the standard input and output file streams.
Opcode $00 appears to be shutting down the program, but disassembly of the code for that instruction shows it potentially does much more than that.
Re: LUCIDATA Pascal for 6502
Posted: Mon Oct 11, 2021 4:26 pm
by BigEd
so, you're saying opcode 6 takes 6 bytes of argument?
Re: LUCIDATA Pascal for 6502
Posted: Tue Oct 12, 2021 3:46 am
by BillG
If I declare an integer global variable, the $06000006 changes to $06000008, so I believe it is definitely related to setting up the stack frame.
Re: LUCIDATA Pascal for 6502
Posted: Tue Oct 12, 2021 5:43 am
by BigEd
I see... is it perhaps likely that opcode 6 is encoded as two bytes? And the operand also two bytes, but big-endian? In which case the final 0 would also be two bytes, leaving just two more zero bytes which might again be an operand.
Re: LUCIDATA Pascal for 6502
Posted: Tue Oct 12, 2021 7:35 am
by BillG
P-code instructions are all four bytes in length with the exception of push constant (opcode $07) which tacks on additional multiples of four bytes as needed to fully convey the constant.
The instruction dispatcher uses byte 0 of the instruction to index a table and pass control to the appropriate handler. Before it does that, it copies byte 1 to address $DA and bytes 2 and 3 to addresses $DB and $DC.
This is a disassembly of the handler for opcode $06:
Code: Select all
01040 *-----------------------------------------------------------------------------
01041 ; 0: Opcode $06 - Manage stack pointer
01042 ; 1: Flag - if 0 then increment else decrement
01043 ; 2: High byte of offset
01044 ; 3: Low byte of offset
01045 *
06B3 01046 L_06B3
06B3 96 C2 [3] 01047 ldaa $C2
06B5 D6 C3 [3] 01048 ldab $C3
06B7 7D 00DA [6] 01049 tst $00DA
06BA 26 0E (06CA) [4] 01050 bne L_06CA
01051
06BC DB DC [3] 01052 addb $DC
06BE 99 DB [3] 01053 adca $DB
06C0 91 C8 [3] 01054 cmpa $C8
06C2 25 0A (06CE) [4] 01055 blo L_06CE
01056
06C4 01057 L_06C4
06C4 CE 06D5 [3] 01058 ldx #L_06D5 ; Stack overflow error
06C7 7E 0230 [3] 01059 jmp L_0230
01060
06CA 01061 L_06CA
06CA D0 DC [3] 01062 subb $DC
06CC 92 DB [3] 01063 sbca $DB
01064
06CE 01065 L_06CE
06CE 97 C2 [4] 01066 staa $C2
06D0 D7 C3 [4] 01067 stab $C3
01068
06D2 7E 01FA [3] 01069 jmp L_01FA ; Execute next instruction
01070
06D5 20535441434B204F 01071 L_06D5 fcc ' STACK OVERFLOW ***'
06DD 564552464C4F5720
06E5 2A2A2A
06E8 04 01072 fcb 4
$C2 and $C3 make up the stack pointer.
An instruction 06 00 00 00 has the single effect of checking for stack overflow.
Re: LUCIDATA Pascal for 6502
Posted: Tue Oct 12, 2021 7:44 am
by drogon
P-code instructions are all four bytes in length with the exception of push constant (opcode $07) which tacks on additional multiples of four bytes as needed to fully convey the constant.
Out of curiosity, I had a quick search online, but can't find a document with the opcodes described - is there one, or is this the results of your own investigations so-far?
I'd be interested in seeing if this might run on my Ruby system - but one big issue might well be what sort of underlying operating system it needs to handle e.g. files and so-on. If nothing else, it might save me writing a Pascal compiler in BCPL!
Cheers,
-Gordon
Re: LUCIDATA Pascal for 6502
Posted: Tue Oct 12, 2021 8:13 am
by BillG
Out of curiosity, I had a quick search online, but can't find a document with the opcodes described - is there one, or is this the results of your own investigations so-far?
I am making this stuff up as I hack and writing my own internals document.
I'd be interested in seeing if this might run on my Ruby system - but one big issue might well be what sort of underlying operating system it needs to handle e.g. files and so-on. If nothing else, it might save me writing a Pascal compiler in BCPL!
FLEX is the host operating system. The API is documented here:
http://www.flexusergroup.com/flexusergr ... lexapg.pdf
I have binaries for version 1 and 3 of the product. The manual I have is for version 1.
I chose to reverse engineer version 1 first because it is simpler meaning fewer details to clutter my view of the forest. It is not a particularly good dialect of the language. Keywords must be in upper case. Character strings are enclosed in quotation marks (") instead of apostrophes (') and there is no support for pointers, real numbers, records.
Re: LUCIDATA Pascal for 6502
Posted: Tue Oct 12, 2021 8:20 am
by BillG
I see... is it perhaps likely that opcode 6 is encoded as two bytes? And the operand also two bytes, but big-endian? In which case the final 0 would also be two bytes, leaving just two more zero bytes which might again be an operand.
I left out one small factoid which may be the source of your confusion. The OP shows a binary dump of the entire FLEX sector containing the compiled program. FLEX does not store the actual length of a file but just the number of sectors.
Any zeroes not enclosed in color rectangles are not a part of the program file.
Re: LUCIDATA Pascal for 6502
Posted: Wed Oct 13, 2021 2:55 am
by jds
Since this is Pascal, and p-code, what you probably have is an implementation of Wirth's p-code interpreter. It is probably based on the code in the book Algorithms + Data Structures = Programs, a PDF is
available on Han's Ottens site. Page 334 onwards describes the P-code interpreter, and might give you some hints on your version. My guess is that it would be very similar to others of the time, and perhaps even quite similar to the p-code interpreter in Apple Pascal.
That opcode $06 could be the INT instruction, which allocates space for local variables by incrementing something called the t register. This happens to be the 6th instruction in the set of instructions in the source code.
Re: LUCIDATA Pascal for 6502
Posted: Wed Oct 13, 2021 4:55 am
by Sean
Since this is Pascal, and p-code, what you probably have is an implementation of Wirth's p-code interpreter. It is probably based on the code in the book Algorithms + Data Structures = Programs, a PDF is
available on Han's Ottens site. Page 334 onwards describes the P-code interpreter, and might give you some hints on your version. My guess is that it would be very similar to others of the time, and perhaps even quite similar to the p-code interpreter in Apple Pascal.
That opcode $06 could be the INT instruction, which allocates space for local variables by incrementing something called the t register. This happens to be the 6th instruction in the set of instructions in the source code.
The compiler and interpreter at the end of Wirth's "Algorithms + Data Structures = Programs" is for PL/0, a very reduced subset of Pascal. It had integers, math operations, if, while, and procedures, but it no functions, characters, pointers, arrays, else, for, etc. I remember reading it in high school mumble mumble years ago and trying to implement it using Turbo Pascal on the high school computers. It was a real pain because some of the standard Pascal constructs Wirth used weren't supported.
You also might want to look at the
ISO 7185 Standard Pascal website. It has several versions of the portable Pascal-P compiler/interpreter. I believe Pascal-P2 served as the basis for UCSD Pascal.
Re: LUCIDATA Pascal for 6502
Posted: Wed Oct 13, 2021 5:01 am
by BillG
Since this is Pascal, and p-code, what you probably have is an implementation of Wirth's p-code interpreter. It is probably based on the code in the book Algorithms + Data Structures = Programs, a PDF is
available on Han's Ottens site. Page 334 onwards describes the P-code interpreter, and might give you some hints on your version. My guess is that it would be very similar to others of the time, and perhaps even quite similar to the p-code interpreter in Apple Pascal.
That opcode $06 could be the INT instruction, which allocates space for local variables by incrementing something called the t register. This happens to be the 6th instruction in the set of instructions in the source code.
Thanks for the link. I have that book, read it many trips around the sun ago and misplaced it.
The first thing I did when starting this project was to dig up documentation about the UCSD P-System and Apple Pascal. Very different from LUCIDATA.
This is the instruction dispatch table so far:
Code: Select all
0100 01C1 00263 fdb L_01C1 ; $00 - Halt + something else having to do with case of
0102 01E6 00264 fdb L_01E6 ; $01 - Branch
00265
0104 01 ED 00266 fcb $01,$ED ; 0100
00267
0106 0658 00268 fdb L_0658 ; $03 - Some kind of call
0108 067E 00269 fdb L_067E ; $04 - Some kind of call
010A 06A2 00270 fdb L_06A2 ; $05 - Return
010C 06B3 00271 fdb L_06B3 ; $06 - Manage stack pointer
010E 06E9 00272 fdb L_06E9 ; $07 - Push constant
0110 078E 00273 fdb L_078E ; $08 - Determine array index
0112 0828 00274 fdb L_0828 ; $09
00275
0114 08 45 08 5A 00276 fcb $08,$45,$08,$5A ; 0110
00277
0118 0883 00278 fdb L_0883 ; $0C - Eoln
011A 089C 00279 fdb L_089C ; $0D - Eof
011C 08B0 00280 fdb L_08B0 ; $0E - Rewrite
011E 08C9 00281 fdb L_08C9 ; $0F - Reset
0120 08DF 00282 fdb L_08DF ; $10 - Compare bytes?
0122 08FB 00283 fdb L_08FB ; $11
00284
0124 09 01 09 01 00285 fcb $09,$01,$09,$01 ; 0120
0128 09 01 09 01 00286 fcb $09,$01,$09,$01 ; 0128
00287
012C 0904 00288 fdb L_0904 ; $16 - Push character
012E 0909 00289 fdb L_0909 ; $17 - Pop character
00290
0130 09 0E 09 1C 00291 fcb $09,$0E,$09,$1C ; 0130
00292
0134 0950 00293 fdb L_0950 ; $1A
00294
0136 09 79 00295 fcb $09,$79 ; 0130
00296
0138 09BD 00297 fdb L_09BD ; $1C - Writeln
013A 09C9 00298 fdb L_09C9 ; $1D - Readln
013C 0A03 00299 fdb L_0A03 ; $1E - Write string
013E 0A6D 00300 fdb L_0A6D ; $1F - Read
0140 0A84 00301 fdb L_0A84 ; $20 - Compare for =
0142 0AAC 00302 fdb L_0AAC ; $21 - Compare for <>
0144 0AB6 00303 fdb L_0AB6 ; $22 - Compare for <
0146 0AC2 00304 fdb L_0AC2 ; $23 - Compare for >
0148 0ACE 00305 fdb L_0ACE ; $24 - Compare for <=
014A 0ADA 00306 fdb L_0ADA ; $25 - Compare for >=
014C 0AE6 00307 fdb L_0AE6 ; $26 - Push integer
014E 0AEB 00308 fdb L_0AEB ; $27 - Pop integer
0150 0B05 00309 fdb L_0B05 ; $28 - Add integer
0152 0B0D 00310 fdb L_0B0D ; $29 - Subtract integer
0154 0B15 00311 fdb L_0B15 ; $2A - Multiply integer
0156 0B1D 00312 fdb L_0B1D ; $2B - Divide integer
00313
0158 0B 25 00314 fcb $0B,$25 ; 0158
00315
015A 0B2E 00316 fdb L_0B2E ; $2D - Odd?
015C 0B43 00317 fdb L_0B43 ; $2E - Write integer
00318
015E 0B FC 00319 fcb $0B,$FC ; 0158
00320
0160 0CE7 00321 fdb L_0CE7 ; $30 - Push set
0162 0CEC 00322 fdb L_0CEC ; $31 - Pop set
0164 0D00 00323 fdb L_0D00 ; $32 - Manipulate set
0166 0D34 00324 fdb L_0D34 ; $33 - Combine sets
00325
0168 0D 53 00326 fcb $0D,$53 ; 0168
00327
016A 0D7D 00328 fdb L_0D7D ; $35 - Compare sets
00329
016C 0D 9E 0D C6 00330 fcb $0D,$9E,$0D,$C6 ; 0168
0170 0D D4 0D E4 00331 fcb $0D,$D4,$0D,$E4,$0D,$F4,$0E,$04 ; 0170
0174 0D F4 0E 04
0178 0E 14 0E 19 00332 fcb $0E,$14,$0E,$19,$0E,$1E,$0E,$E8 ; 0178
017C 0E 1E 0E E8
Re: LUCIDATA Pascal for 6502
Posted: Wed Oct 13, 2021 11:39 am
by BillG
I am starting to "disassemble" programs as I figure out the instructions they use.
This program:
Code: Select all
PROGRAM TEST;
VAR
I : INTEGER;
J : INTEGER;
BEGIN
CASE I OF
0: J := 0;
1: J := 1;
2: J := 2
END
END.
compiles to:
Code: Select all
00000000: 00 27 00 68 00 01 00 01-00 06 06 00 00 0A 01 00 |.'.h............|
00000010: 00 0C 01 00 00 60 26 00-00 08 19 00 00 00 07 01 |.....`&.........|
00000020: 00 00 10 01 00 28 07 02-00 00 27 00 00 0A 01 00 |.....(....'.....|
00000030: 00 08 06 01 00 01 07 01-00 01 10 01 00 40 07 02 |.............@..|
00000040: 00 01 27 00 00 0A 01 00-00 08 06 01 00 01 07 01 |..'.............|
00000050: 00 02 10 01 00 58 07 02-00 02 27 00 00 0A 01 00 |.....X....'.....|
00000060: 00 08 06 01 00 01 00 01-00 01 06 01 00 02 00 00 |................|
00000070: 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 |................|
And this is the P-code:
Code: Select all
0000: 06 00 00 0A ; SP = SP + 10
0004: 01 00 00 0C ; Jump 000C
0008: 01 00 00 60 ; Jump 0060
000C: 26 00 00 08 ; Push integer I
0010: 19 00 00 00 ; Convert integer to byte
0014: 07 01 00 00 ; Push byte $00
0018: 10 01 00 28 ; If bytes NE then 0028
001C: 07 02 00 00 ; Push integer $0000
0020: 27 00 00 0A ; Pop integer J
0024: 01 00 00 08 ; Jump 0008
0028: 06 01 00 01 ; SP := SP - 1
002C: 07 01 00 01 ; Push byte $01
0030: 10 01 00 40 ; If bytes NE then 0040
0034: 07 02 00 01 ; Push integer $0001
0038: 27 00 00 0A ; Pop integer J
003C: 01 00 00 08 ; Jump 0008
0040: 06 01 00 01 ; SP := SP - 1
0044: 07 01 00 02 ; Push byte $02
0048: 10 01 00 58 ; If bytes NE then 0058
004C: 07 02 00 02 ; Push integer $0002
0050: 27 00 00 0A ; Pop integer J
0054: 01 00 00 08 ; Jump 0008
0058: 06 01 00 01 ; SP := SP - 1
005C: 00 01 00 01 ; Case variable error
0060: 06 01 00 02 ; SP := SP - 2
0064: 00 00 00 00 ; Halt
Re: LUCIDATA Pascal for 6502
Posted: Wed Oct 13, 2021 2:54 pm
by Sean
The first thing I did when starting this project was to dig up documentation about the UCSD P-System and Apple Pascal. Very different from LUCIDATA.
IIRC, Apple Pascal was based upon UCSD, and UCSD was based upon Pascal-P2. However, there were very substantial changes made to the virtual machine/interpreter in the port from P2 to UCSD. Given the lack of similarity between UCSD and LUCIDATA I wonder if LUCIDATA was a port from P2, or perhaps an independent reimplementation based upon the language specs.
Nice work decoding what is going on. Thanks for sharing.
Re: LUCIDATA Pascal for 6502
Posted: Wed Oct 13, 2021 4:38 pm
by drogon
I am starting to "disassemble" programs as I figure out the instructions they use.
Looks like some sort of 32-bit word based machine. Not what I'd expect when the target is an 8-bit micro. (or, possibly, I've had my head stuck in the BCPL/Cintcode bytecode VM for a bit too long!)
However it might make it relatively easy to write the interpreter for.
Good stuff so-far though!
Cheers,
-Gordon
Re: LUCIDATA Pascal for 6502
Posted: Thu Oct 14, 2021 2:38 am
by resman
Randy Hyde wrote a great book about the Apple Pascal p-code interpreter:
https://archive.org/details/Hyde_P-Sour ... ystem_1983
Very useful if the LUCIDATA is based off of UCSD, but still a great read regardless.