as Wikipedia says, "PL/M was the first higher level programming language for microprocessor-based computers and was the original implementation language for those parts of the CP/M operating system which were not written in assembler." it was designed and developed by Gary Kildall, and used to write a lot of software for 8-bit CPUs (mostly running CP/M).
currently, there is no small and usable PL/M cross-compiler out there. there are several PL/M->C converters, and some translations of the old fortran PL/M compiler. converters are not really interesting, and fortran port is… not very easy to use/understand. so i decided to develop my own PL/M cross-compiler from the scratch.
i created this topic to force myself to finish the project (or at least make something useable ;-), so there is no ready to use software here yet. but i'm working on the thing, and i already wrote parser and semantic analyser. what it means? it means that the compiler can parse PL/M code, build and analyze AST. basically, everything is ready for code generation at this stage.
i am planning to support most PL/M-80 features, but the target platform will be ZX Spectrum, not some CP/M machine. the compiler itself is written in GNU C, and the sources will be opened under GPL license.
i hope to release Tech Preview version at the end of this month (i REALLY DO! ;-).
for now, you can download PL/M-80 manual, and check if you like the language. i think that PL/M is more beginner-friendly than C, and programming retro computers with The First Language For Microprocessors is much funnier than using C for that. ;-)
sorry for teasing you and not giving you something to play with. i will try to fix this ASAP. ;-)
PL/M cross-compiler for ZX Spectrum
Re: PL/M cross-compiler for ZX Spectrum
interesting idea. isis-2 setup is too complicated, besides, the generated code uses bdos calls
Re: PL/M cross-compiler for ZX Spectrum
also, i must tell you that the initial version will prolly have the worst codegen possible, because i want to get the thing running first. yet i know how to make it better (but no, i don't want to write yet another SSA compiler this time), so please, don't judge the thing by the upcoming Tech Preview. ;-) while the thing will prolly never be a speed demon, i hope to teach it to generate decent code.
Re: PL/M cross-compiler for ZX Spectrum
sorry, i have to use this thread as a kind of "developer diary", because working on the project is quite hard. not because i don't know how to implement it (it's not even a tenth compiler i am writing; writing compilers is my hobby), but because of some external… conditions. so i will drop some progress posts here from time to time, and this should force me to continue. ;-)
so i taught the parser to understand the code better, and rewrote semantic analyser several times (this is perfectly normal thing at this stage of development). also implemented very simple constant folding and dead code elimination, and wrote very simple stack-like codegen; and now replacing it with a slightly better one.
also, i made a mistake of adding signed integers as a built-in complier type. PL/M doesn't have signed ints, and i won't support them too. maybe later. the problem is that automatic type conversions with signed types is a mess, and it doesn't worth the efforts (at least for now). so i removed all traces of signed ints: let's stick to the original PL/M specs!
for those interested: the compiler is a classical multipass one. the parser builds very-very abstract AST, the semantic analyzer rewrites it to lower level one (and sets expression types), and the codegen is using delayed loads as described by Wirth. i am also planning to implement saving register info at block boundaries in codegen, and simple live range analysis to help register allocation (much) later.
i don't want to write SSA-based compiler this time, because i want some PAIN AND SUFFERING. but maybe i'll switch to SSA later anyway.
so i taught the parser to understand the code better, and rewrote semantic analyser several times (this is perfectly normal thing at this stage of development). also implemented very simple constant folding and dead code elimination, and wrote very simple stack-like codegen; and now replacing it with a slightly better one.
also, i made a mistake of adding signed integers as a built-in complier type. PL/M doesn't have signed ints, and i won't support them too. maybe later. the problem is that automatic type conversions with signed types is a mess, and it doesn't worth the efforts (at least for now). so i removed all traces of signed ints: let's stick to the original PL/M specs!
for those interested: the compiler is a classical multipass one. the parser builds very-very abstract AST, the semantic analyzer rewrites it to lower level one (and sets expression types), and the codegen is using delayed loads as described by Wirth. i am also planning to implement saving register info at block boundaries in codegen, and simple live range analysis to help register allocation (much) later.
i don't want to write SSA-based compiler this time, because i want some PAIN AND SUFFERING. but maybe i'll switch to SSA later anyway.
Spoiler
p.s.: if somebody wants to track my work, drop me a PM to get a link to Fossil repo. i feel that making it "fully public" now may create some wrong impressions (early development work is usually doesn't look good ;-).
Re: PL/M cross-compiler for ZX Spectrum
first (stupid) milestone!
this:
not only generates some awful asm code, but prints both numbers properly!
for those interested, here's the generated asm. currently, zero attempts were made on data flow analysis or register allocation, so the code is very ugly and slow:
this:
Spoiler
Code: Select all
PRINT$STRING: PROCEDURE(ADDR,LEN) EXTERNAL;
PRINT$NUMBER: PROCEDURE(NUMBER,BASE,CHARS,ZERO$SUPPRESS);
DECLARE NUMBER ADDRESS, (BASE,CHARS,ZERO$SUPPRESS,I,J) BYTE;
DECLARE TEMP(16) BYTE;
IF CHARS > LAST(TEMP) THEN CHARS = LAST(TEMP);
DO I = 1 TO CHARS;
J=NUMBER MOD BASE + '0';
IF J > '9' THEN J = J + 7;
IF ZERO$SUPPRESS AND I <> 1 AND NUMBER = 0 THEN
J = ' ';
TEMP(LENGTH(TEMP)-I) = J;
NUMBER = NUMBER / BASE;
END;
CALL PRINT$STRING(.TEMP + LENGTH(TEMP) - CHARS,CHARS);
END PRINT$NUMBER;
MAIN: PROCEDURE;
CALL PRINT$NUMBER(9679, 10, 5, 1);
CALL PRINT$NUMBER(9679, 10, 5, 0);
END MAIN;
for those interested, here's the generated asm. currently, zero attempts were made on data flow analysis or register allocation, so the code is very ugly and slow:
Spoiler
Code: Select all
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; PROC: PRINTNUMBER
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
_loc_PRINTNUMBER__NUMBER: defw 0
_loc_PRINTNUMBER__BASE: defb 0
_loc_PRINTNUMBER__CHARS: defb 0
_loc_PRINTNUMBER__ZEROSUPPRESS: defb 0
_loc_PRINTNUMBER__I: defb 0
_loc_PRINTNUMBER__J: defb 0
_loc_PRINTNUMBER__TEMP: defs 16,0
_PRINTNUMBER:
;; LINE #4
;; LINE #4
;; LINE #4
;; LINE #4
;; LINE #4
;; LINE #4
;; LINE #5
;; LINE #6
ld e,15
ld a,(_loc_PRINTNUMBER__CHARS)
sub a,e
jr z,$+4
ccf
sbc a,a
rra
jp nc,._label_1
;; LINE #6
ld a,15
ld (_loc_PRINTNUMBER__CHARS),a
._label_1:
;; LINE #7
;; LINE #7
ld a,1
ld (_loc_PRINTNUMBER__I),a
._label_3:
ld de,(_loc_PRINTNUMBER__CHARS) ; 8-bit cheat (lo)
ld a,(_loc_PRINTNUMBER__I)
sub a,e
ld a,0xff
jr z,$+3
sbc a,a
rra
jp nc,._label_2
;; LINE #8
;; LINE #8
ld de,(_loc_PRINTNUMBER__BASE)
ld d,0 ; 16-bit cheat
ld hl,(_loc_PRINTNUMBER__NUMBER)
call _mod_hl_de
ld de,48
add hl,de
ld a,l
ld (_loc_PRINTNUMBER__J),a
;; LINE #9
ld e,57
ld a,(_loc_PRINTNUMBER__J)
sub a,e
jr z,$+4
ccf
sbc a,a
rra
jp nc,._label_4
;; LINE #9
ld e,7
ld a,(_loc_PRINTNUMBER__J)
add a,e
ld (_loc_PRINTNUMBER__J),a
._label_4:
;; LINE #10
ld e,1
ld a,(_loc_PRINTNUMBER__I)
cp e
ld a,0
jr z,$+3
dec a
;; 1 : a
ld e,a
ld a,(_loc_PRINTNUMBER__ZEROSUPPRESS)
and a,e
push af
ld e,0
ld hl,(_loc_PRINTNUMBER__NUMBER)
dec h ; 16/8 (n)equ
jr nz,$+5 ; 16/8 (n)equ
xor a ; 8/16 (n)equ
jr ._label_6 ; 8/16 (n)equ
ld a,l
cp e
ld a,0
jr nz,$+3
dec a
._label_6:
;; 1 : a (op0: 1 : a)
ld e,a ; bothreg
pop af
and a,e
rra
jp nc,._label_5
;; LINE #11
ld a,32
ld (_loc_PRINTNUMBER__J),a
._label_5:
;; LINE #12
ld a,(_loc_PRINTNUMBER__J)
push af
ld de,(_loc_PRINTNUMBER__I) ; 8-bit cheat (lo)
ld a,16
sub a,e
ld l,a
ld h,0
ld de,_loc_PRINTNUMBER__TEMP
add hl,de
pop af
ld (hl),a
;; LINE #13
ld de,(_loc_PRINTNUMBER__BASE)
ld d,0 ; 16-bit cheat
ld hl,(_loc_PRINTNUMBER__NUMBER)
call _div_hl_de
ld (_loc_PRINTNUMBER__NUMBER),hl
;; LINE #7
ld e,1
ld a,(_loc_PRINTNUMBER__I)
add a,e
ld (_loc_PRINTNUMBER__I),a
jp ._label_3
._label_2:
;; LINE #15
ld de,16
ld hl,_loc_PRINTNUMBER__TEMP
add hl,de
ld de,(_loc_PRINTNUMBER__CHARS)
ld d,0 ; 16-bit cheat
or a
sbc hl,de
ld (_loc_PRINTSTRING__ADDR),hl
ld hl,(_loc_PRINTNUMBER__CHARS)
ld h,0 ; 16-bit cheat
ld (_loc_PRINTSTRING__LEN),hl
call _PRINTSTRING
;; LINE #3
ret
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; PROC: MAIN
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
_MAIN:
;; LINE #29
ld hl,9679
ld (_loc_PRINTNUMBER__NUMBER),hl
ld a,10
ld (_loc_PRINTNUMBER__BASE),a
ld a,5
ld (_loc_PRINTNUMBER__CHARS),a
ld a,1
ld (_loc_PRINTNUMBER__ZEROSUPPRESS),a
call _PRINTNUMBER
;; LINE #30
ld hl,9679
ld (_loc_PRINTNUMBER__NUMBER),hl
ld a,10
ld (_loc_PRINTNUMBER__BASE),a
ld a,5
ld (_loc_PRINTNUMBER__CHARS),a
ld a,0
ld (_loc_PRINTNUMBER__ZEROSUPPRESS),a
call _PRINTNUMBER
;; LINE #28
ret
Re: PL/M cross-compiler for ZX Spectrum
kool, as I see you avoid 8080 opcodes
Re: PL/M cross-compiler for ZX Spectrum
Oh boo, I thought this meant there might actually be some software to run on CP/M on my +3 one day
Re: PL/M cross-compiler for ZX Spectrum
i don't even remember 8080 mnemonics anymore. ;-) anyway, this codegen will be completely rewritten later, for now i simply made something that allowed me to write "it works!" post here. ;-)
it won't be a problem to add CP/M support later. but my primary goal is writing games for 48K Speccy. PL/M is slightly "closer to the 8-bit metal" than C, and if my ideas for codegen will work as i expect them to work, the generated code will be slightly smaller and faster than that of z88dk.
actually, the compiler itself doesn't assume anything about the machine at all (except that it is using Z80 CPU), so adding CP/M support should be as easy as writing a runtime library for it.
Re: PL/M cross-compiler for ZX Spectrum
got distracted by sudden game engine, sorry. scroll link up a little to read some tech description of it. maybe i'll include it into compiler distribution, so people will be able to create platformers almost out-of-the-box. ;-)
p.s.: version published there isn't final by any means. current one is better optimised (one t-state here, two t-states there… ;-), i'm finishing collision detection implementation, and cleaning the code. so there will be new demo soon (i hope). maybe i'll create a separate thead for it later.
p.s.: version published there isn't final by any means. current one is better optimised (one t-state here, two t-states there… ;-), i'm finishing collision detection implementation, and cleaning the code. so there will be new demo soon (i hope). maybe i'll create a separate thead for it later.