6502.org Forum  Projects  Code  Documents  Tools  Forum
It is currently Sat Nov 16, 2024 6:16 pm

All times are UTC




Post new topic Reply to topic  [ 4 posts ] 
Author Message
PostPosted: Thu Jun 04, 2020 8:55 pm 
Offline
User avatar

Joined: Tue Mar 02, 2004 8:55 am
Posts: 996
Location: Berkshire, UK
One of my many back burner projects is a portable forth compiler. I started it a few years ago and after a week of staring at the inefficient code generated by C compilers for a simple LED flashing program I wondered would it be be like in Forth.

The compiler reads in the definitions of standard words from the source like this
Code:
code false
  dex, dex, 1 stzzx, 2 stzzx,
end-code

-1 constant true

code lshift ( n1 n2 -- n3 )
  1 ldazx,
  16 cmpim,
  2 ldazx,
  ifeq
    ifcc
      tay,
      whilene
        3 aslzx, 4 rolzx, dey,
      endw
      inx, inx,
      rts,
    then
  then
  0 ldaim,
  2 stazx, 3 stazx,
  inx, inx,
end-code

I've defined W65C134's registers and some utility words like this:
Code:
hex

\ ==============================================================================
\ Hardware Peripheral Registers
\ ------------------------------------------------------------------------------

0000 constant PD0   inline
0001 constant PD1   inline
0002 constant PD2   inline
0003 constant PD3   inline
0004 constant PDD0  inline
0005 constant PDD1  inline
0006 constant PDD2  inline
0007 constant PCS3  inline
0008 constant IFR2  inline
0009 constant IER4  inline
000a constant TCR1  inline
000b constant TCR2  inline
000c constant T1LL  inline
000d constant T1LH  inline
000e constant T2LL  inline
000f constant T2LH  inline
0010 constant T1CL  inline
0011 constant T1CH  inline
0012 constant T2CL  inline
0013 constant T2CH  inline
0014 constant STATE inline
0015 constant SR0   inline
0016 constant SR1   inline
0017 constant SR2   inline
0018 constant SR3   inline
0019 constant SCSR  inline
001a constant BAR   inline
001b constant BCR   inline
001c constant PD4   inline
001d constant PD5   inline
001e constant PDD4  inline
001f constant PDD5  inline
0020 constant PD6   inline
0021 constant PDD6  inline
0022 constant ACSR  inline
0023 constant ARTD  inline
0024 constant TALL  inline
0025 constant TALH  inline
0026 constant TACL  inline
0027 constant TACH  inline
0028 constant TMLL  inline
0029 constant TMLH  inline
002a constant TMCL  inline
002b constant TMCH  inline
002c constant IFR1  inline
002d constant IER1  inline

\ ------------------------------------------------------------------------------

: INPUT ( -- pin-mode )
  false
; inline

: OUTPUT ( -- pin-mode )
  true
; inline

: LO ( -- pin-state )
  false
; inline

: HI ( -- pin-state )
  true
; inline

\ ==============================================================================
\ Pin Abstractions
\ ------------------------------------------------------------------------------

: pin50 ( -- pin-spec )
  PDD5 PD5 1 0 lshift
;

: pin51 ( -- pin-spec )
  PDD5 PD5 1 1 lshift
;

\ ------------------------------------------------------------------------------

code pinSetMode ( pin-spec pin-mode -- )
  1 ldazx, 3 eorix, 7 andzx, 3 eorix, 3 staix,
  txa, clc, 8 adcim, tax,
end-code

code pinSetState ( pin-spec pin-state -- )
  1 ldazx, 5 eorix, 7 andzx, 5 eorix, 5 staix,
  txa, clc, 8 adcim, tax,
end-code

And I have a simple test program like this
Code:
: led-pin ( -- pin-spec )
  pin50
; inline

: boot ( -- )
  led-pin OUTPUT pinSetMode
  begin
    led-pin HI pinSetState
    led-pin LO pinSetState
  again
; external

The compiler inlines words where directed and optimises both at the forth (e.g. 1 0 lshift in pin50 has become 1) and assembly levels. It only generates referenced words and internal functions (like __test which is used by IF). The result is this:
Code:
;===============================================================================
                             ; forthcc W65C02 [20.06] -- Generated Code - Do not edit.
                             ;-------------------------------------------------------------------------------

                                             .65C02

                                             .code
                             __test:                                 ; code __test
00:0000' B501              :                 lda     <1,x
00:0002' 1502              :                 ora     <2,x
00:0004' E8                :                 inx
00:0005' E8                :                 inx
00:0006' A8                :                 tay
00:0007' 60                :                 rts                     ; end-code

                                             .code
                             lshift:                                 ; code lshift
00:0008' B501              :                 lda     <1,x
00:000A' C910              :                 cmp     #16
00:000C' B502              :                 lda     <2,x
00:000E' D00F              :                 if      eq              ; ifeq
00:0010' B00D              :                 if      cc              ; ifcc
00:0012' A8                :                 tay
00:0013' F007              : .L1:            beq     .L2             ; whilene
00:0015' 1603              :                 asl     <3,x
00:0017' 3604              :                 rol     <4,x
00:0019' 88                :                 dey
00:001A' 80F7              :                 jpa     .L1             ; endw
00:001C' E8                : .L2:            inx
00:001D' E8                :                 inx
00:001E' 60                :                 rts
                                             endif                   ; then
                                             endif                   ; then
00:001F' A900              :                 lda     #0
00:0021' 9502              :                 sta     <2,x
00:0023' 9503              :                 sta     <3,x
00:0025' E8                :                 inx
00:0026' E8                :                 inx
00:0027' 60                :                 rts                     ; end-code

                                             .code
                             true:                                   ; : true
00:0028' A9FF              :                 lda     #<-1            ; -1
00:002A' A8                :                 tay
00:002B' 802B              :                 jpa     __push          ; ;

                                             .code
                             pin50:                                  ; : pin50
00:002D' A91F              :                 lda     #<31            ; 31
00:002F' A000              :                 ldy     #>31
00:0031' 20????            :                 jsr     __push
00:0034' A91D              :                 lda     #<29            ; 29
00:0036' A000              :                 ldy     #>29
00:0038' 20????            :                 jsr     __push
00:003B' A901              :                 lda     #<1             ; 1
00:003D' A000              :                 ldy     #>1
00:003F' 8017              :                 jpa     __push          ; ;

                                             .code
                             false:                                  ; code false
00:0041' CA                :                 dex
00:0042' CA                :                 dex
00:0043' 7401              :                 stz     <1,x
00:0045' 7402              :                 stz     <2,x
00:0047' 60                :                 rts                     ; end-code

                                             .code
                             pinSetState:                            ; code pinSetState
00:0048' B501              :                 lda     <1,x
00:004A' 4105              :                 eor     (5,x)
00:004C' 3507              :                 and     <7,x
00:004E' 4105              :                 eor     (5,x)
00:0050' 8105              :                 sta     (5,x)
00:0052' 8A                :                 txa
00:0053' 18                :                 clc
00:0054' 6908              :                 adc     #8
00:0056' AA                :                 tax
00:0057' 60                :                 rts                     ; end-code

                                             .code
                             __push:                                 ; code __push
00:0058' CA                :                 dex
00:0059' CA                :                 dex
00:005A' 9501              :                 sta     <1,x
00:005C' 9402              :                 sty     <2,x
00:005E' 60                :                 rts                     ; end-code

                                             .code
                             __pull:                                 ; code __pull
00:005F' B501              :                 lda     <1,x
00:0061' B402              :                 ldy     <2,x
00:0063' E8                :                 inx
00:0064' E8                :                 inx
00:0065' 60                :                 rts                     ; end-code

                                             .code
                                             .global boot
                             boot:                                   ; : boot
00:0066' 20????            :                 jsr     pin50           ; pin50
00:0069' 20????            :                 jsr     true            ; true
00:006C' 20????            :                 jsr     pinSetMode      ; pinSetMode
00:006F' 20????            : .L5:            jsr     pin50           ; pin50
00:0072' 20????            :                 jsr     true            ; true
00:0075' 20????            :                 jsr     pinSetState     ; pinSetState
00:0078' 20????            :                 jsr     pin50           ; pin50
00:007B' 20????            :                 jsr     false           ; false
00:007E' 20????            :                 jsr     pinSetState     ; pinSetState
00:0081' 80EC              :                 jpa     .L5             ; (branch) ;

                                             .code
                             pinSetMode:                             ; code pinSetMode
00:0083' B501              :                 lda     <1,x
00:0085' 4103              :                 eor     (3,x)
00:0087' 3507              :                 and     <7,x
00:0089' 4103              :                 eor     (3,x)
00:008B' 8103              :                 sta     (3,x)
00:008D' 8A                :                 txa
00:008E' 18                :                 clc
00:008F' 6908              :                 adc     #8
00:0091' AA                :                 tax
00:0092' 60                :                 rts                     ; end-code

                                             .end


It needs to be linked with a small routine to setup the hardware and forth stack pointers (X) before I can run it.

_________________
Andrew Jacobs
6502 & PIC Stuff - http://www.obelisk.me.uk/
Cross-Platform 6502/65C02/65816 Macro Assembler - http://www.obelisk.me.uk/dev65/
Open Source Projects - https://github.com/andrew-jacobs


Top
 Profile  
Reply with quote  
PostPosted: Thu Jun 04, 2020 11:32 pm 
Offline

Joined: Wed Jan 08, 2014 3:31 pm
Posts: 578
The assembler output looks really well formatted. Are you building on top of an existing tool chain, or is this completely home brew?

The idea of a compiled Forth like language has a lot of merit for eight bit CPU's, as lining definitions would be faster than threaded code, but still portable between CPU's like Forth.

Late last year I got bitten by the idea to write a simple compiler and used Python. The language itself used Lisp S-exprs, but honestly was very Forth like as it used two stacks (page zero, and the page one). I learned a lot, but it honestly needs a complete rewrite.


Top
 Profile  
Reply with quote  
PostPosted: Tue Jun 16, 2020 1:37 am 
Offline
User avatar

Joined: Mon May 12, 2014 6:18 pm
Posts: 365
Very interesting. How do the optimizations work?


Top
 Profile  
Reply with quote  
PostPosted: Tue Jun 16, 2020 8:32 am 
Offline
User avatar

Joined: Tue Mar 02, 2004 8:55 am
Posts: 996
Location: Berkshire, UK
The compiler generates code for my Dev65 portable assembler (Java based) which supports structured programming constructs (e.g. IF/ELSE/ENDIF) and short jump generation (e.g. JPA becomes JMP or BRA).

Internally Forth is compiled into a vectors of cells where each cell is a word reference, a literal or a branch. Alloted data areas are held separately from the code.

There are two levels of optimiser: the first works at the forth level and eliminates constant expressions, handles inline definitions, redundant stack operations and adjusts branches so they can work on the top stack value non destructively. The optimized forth is converted to assembler and then peep hole optimized.

I have backends for different processors including 6502, 65C02 and 65816 in various states of development.

_________________
Andrew Jacobs
6502 & PIC Stuff - http://www.obelisk.me.uk/
Cross-Platform 6502/65C02/65816 Macro Assembler - http://www.obelisk.me.uk/dev65/
Open Source Projects - https://github.com/andrew-jacobs


Top
 Profile  
Reply with quote  
Display posts from previous:  Sort by  
Post new topic Reply to topic  [ 4 posts ] 

All times are UTC


Who is online

Users browsing this forum: No registered users and 10 guests


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: