6502.org Forum  Projects  Code  Documents  Tools  Forum
It is currently Sun Nov 10, 2024 10:36 pm

All times are UTC




Post new topic Reply to topic  [ 54 posts ]  Go to page 1, 2, 3, 4  Next
Author Message
 Post subject: LUCIDATA Pascal for 6502
PostPosted: Mon Oct 11, 2021 1:24 pm 
Offline

Joined: Thu Mar 12, 2020 10:04 pm
Posts: 704
Location: North Tejas
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:
Attachment:
BINARY.PNG
BINARY.PNG [ 7.3 KiB | Viewed 3670 times ]

from compiling this program:
Code:
PROGRAM EMPTY;

  BEGIN
  END.

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.


Top
 Profile  
Reply with quote  
PostPosted: Mon Oct 11, 2021 4:26 pm 
Offline
User avatar

Joined: Thu Dec 11, 2008 1:28 pm
Posts: 10977
Location: England
so, you're saying opcode 6 takes 6 bytes of argument?


Top
 Profile  
Reply with quote  
PostPosted: Tue Oct 12, 2021 3:46 am 
Offline

Joined: Thu Mar 12, 2020 10:04 pm
Posts: 704
Location: North Tejas
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.


Top
 Profile  
Reply with quote  
PostPosted: Tue Oct 12, 2021 5:43 am 
Offline
User avatar

Joined: Thu Dec 11, 2008 1:28 pm
Posts: 10977
Location: England
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.


Top
 Profile  
Reply with quote  
PostPosted: Tue Oct 12, 2021 7:35 am 
Offline

Joined: Thu Mar 12, 2020 10:04 pm
Posts: 704
Location: North Tejas
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:
                          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.


Top
 Profile  
Reply with quote  
PostPosted: Tue Oct 12, 2021 7:44 am 
Offline
User avatar

Joined: Wed Feb 14, 2018 2:33 pm
Posts: 1485
Location: Scotland
BillG wrote:
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

_________________
--
Gordon Henderson.
See my Ruby 6502 and 65816 SBC projects here: https://projects.drogon.net/ruby/


Top
 Profile  
Reply with quote  
PostPosted: Tue Oct 12, 2021 8:13 am 
Offline

Joined: Thu Mar 12, 2020 10:04 pm
Posts: 704
Location: North Tejas
drogon wrote:
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.

drogon wrote:
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.


Top
 Profile  
Reply with quote  
PostPosted: Tue Oct 12, 2021 8:20 am 
Offline

Joined: Thu Mar 12, 2020 10:04 pm
Posts: 704
Location: North Tejas
BigEd wrote:
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.


Top
 Profile  
Reply with quote  
PostPosted: Wed Oct 13, 2021 2:55 am 
Offline

Joined: Thu Mar 10, 2016 4:33 am
Posts: 181
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.


Top
 Profile  
Reply with quote  
PostPosted: Wed Oct 13, 2021 4:55 am 
Offline

Joined: Mon Feb 15, 2021 2:11 am
Posts: 100
jds wrote:
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.


Top
 Profile  
Reply with quote  
PostPosted: Wed Oct 13, 2021 5:01 am 
Offline

Joined: Thu Mar 12, 2020 10:04 pm
Posts: 704
Location: North Tejas
jds wrote:
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:
 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


Top
 Profile  
Reply with quote  
PostPosted: Wed Oct 13, 2021 11:39 am 
Offline

Joined: Thu Mar 12, 2020 10:04 pm
Posts: 704
Location: North Tejas
I am starting to "disassemble" programs as I figure out the instructions they use.

This program:
Code:
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:
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:
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


Top
 Profile  
Reply with quote  
PostPosted: Wed Oct 13, 2021 2:54 pm 
Offline

Joined: Mon Feb 15, 2021 2:11 am
Posts: 100
BillG wrote:
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.


Top
 Profile  
Reply with quote  
PostPosted: Wed Oct 13, 2021 4:38 pm 
Offline
User avatar

Joined: Wed Feb 14, 2018 2:33 pm
Posts: 1485
Location: Scotland
BillG wrote:
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

_________________
--
Gordon Henderson.
See my Ruby 6502 and 65816 SBC projects here: https://projects.drogon.net/ruby/


Top
 Profile  
Reply with quote  
PostPosted: Thu Oct 14, 2021 2:38 am 
Offline

Joined: Sat Dec 12, 2015 7:48 pm
Posts: 145
Location: Lake Tahoe
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.


Top
 Profile  
Reply with quote  
Display posts from previous:  Sort by  
Post new topic Reply to topic  [ 54 posts ]  Go to page 1, 2, 3, 4  Next

All times are UTC


Who is online

Users browsing this forum: No registered users and 1 guest


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot post attachments in this forum

Search for:
Jump to: