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

All times are UTC




Post new topic Reply to topic  [ 7 posts ] 
Author Message
PostPosted: Sat Jan 01, 2022 9:46 pm 
Offline

Joined: Sun Apr 26, 2020 3:08 am
Posts: 357
I am finding it much easier to convert Forth to machine language than I thought.

Let's go through an example. We will use ." as an example which will highlight STREAM's code routine as it is used multiple times.

These are my definitions of STREAM and .", but the idea works the same on any word definitions.

: stream ( -- adr ) blk @ ?dup if block else tib @ then in @ + ;

: ." ( -- ) stream c@ 22 = if 1 IN +! else 22 state @ if compile (.") then dup c@ 1+ over + c@ 22 = no ?stream state @ if c@ 1+ allot then count type then ;

Some simple rules are then, anytime we see a constant or variable we can convert them with simple mnemonics.

@ ( AT ) converts to LDA and ! ( STORE ) converts to STA. Therefore:

"BLK @" converts to LDA VBLK and "TIB @" converts to LDA VTIB, and "STATE @" convert to LDA VSTATE, where the V stands for Variable.


Next to convert are word definitions that are being called. They all just are converted to a sub-routine call using JSR and with the name prefixed with an "X".

"IF" is converted to BNE and "ELSE" is converted to BRA, and the branch offset of IF branches to just after ELSE, and ELSE's offset branches to just after THEN.

and to complete the conversion of STREAM, the "+" will convert to "CLC ADC".

Now let's follow "STREAM" through. The code will be worked out in Source format.

blk @ = LDA VBLK
?dup = JSR XQDUP
if = BNE else + 2
block = JSR XBLOCK
else = BRA then + 2
tib @ = LDA VTIB
THEN is just a place holder to calculate the offset at ELSE
"in @ +" because of the "+" sign after the @, we cannot do just LDA VIN, but we can do CLC and ADC VIN.
if we see a "-" sign instead, then we do a SEC and SBC VIN.
multiply and divide we will end up doing a JSR XMULTIPLY or JSR XDIVIDE.
and lastly the ";S" is converted to RTS. Although at this time just take note of the last word definition before ;S for later use.

We now have the sub-routine part of STREAM nailed down, but we also need it to be supported as a word definition. Which is also quite easy to do. We call the subroutine above, "XSTREAM" and create a semi-header for "STREAM" to be used as a word definition like so:

* We can leave out the main header, but the CFA of the STREAM header points to STREAM, and XSTREAM is called as a subroutine by other sub-routines. Only subroutines can call another subroutine.

STREAM JSR XSTREAM
JMP NEXT

XSTREAM LDA VBLK
JSR XQDUP
BNE ELSE + 2
JSR XBLOCK
ELSE BRA THEN +2
LDA VTIB
THEN EQU *
CLC
ADC VIN
RTS


I will let this sink in for a bit and give anyone a chance to convert ." for themselves. What I have stated here should be enough to convert most Forth definitions. I will list the result to ." in a day or two. But for now I have to go watch the Rose Bowl.


Last edited by IamRob on Sun Jan 02, 2022 6:31 am, edited 4 times in total.

Top
 Profile  
Reply with quote  
PostPosted: Sun Jan 02, 2022 2:35 am 
Offline
User avatar

Joined: Sun Jun 30, 2013 10:26 pm
Posts: 1929
Location: Sacramento, CA, USA
Your stack comment for stream is ( -- adr ). I may be wrong, but it looks like the first thing XSTREAM does is LDA VBLK, which has the side-effect of DROPping your initial TOS in A, something I didn't expect to see. A word that grows the stack is inevitably going to include a DEX DEX STA 0,X sooner or later.

I'm a big fan of TOS in A, but every time you LDA you need to spend a thought over what you may be discarding in A.

Turning bunches of secondaries into primitives is fine for the do-it-yourselfer looking for performance, but finding a point of equilibrium may not be obvious, and may vary widely between different applications and implementers.

_________________
Got a kilobyte lying fallow in your 65xx's memory map? Sprinkle some VTL02C on it and see how it grows on you!

Mike B. (about me) (learning how to github)


Top
 Profile  
Reply with quote  
PostPosted: Sun Jan 02, 2022 5:14 am 
Offline

Joined: Sun Apr 26, 2020 3:08 am
Posts: 357
barrym95838 wrote:
Your stack comment for stream is ( -- adr ). I may be wrong, but it looks like the first thing XSTREAM does is LDA VBLK, which has the side-effect of DROPping your initial TOS in A, something I didn't expect to see. A word that grows the stack is inevitably going to include a DEX DEX STA 0,X sooner or later.

I'm a big fan of TOS in A, but every time you LDA you need to spend a thought over what you may be discarding in A.

Turning bunches of secondaries into primitives is fine for the do-it-yourselfer looking for performance, but finding a point of equilibrium may not be obvious, and may vary widely between different applications and implementers.

Now I know how a real teacher feels. It is not even the first day of class and already the students are correcting the teacher. :P

I was hoping just to produce the theory behind converting Forth to Assembly before handing over my very efficient code. But like everything else in Forth, I see now that it might be better to work in reverse. Otherwise a lot of the routines may not make sense and I will get bombarded with people correcting my mistakes.

Here is one of my most powerful routines, and I might even argue, as powerful as the NEXT routine, as it will almost see as much usage as NEXT.

Code:
DUP DEX
    DEX

PUT  STY $00,X
   JMP NEXT

Now we can truly start to appreciate the reason for duplicating TOS in the Y-reg.
Preserving the Acc becomes as easy as:

Code:
STREAM JSR XSTREAM
       JMP DUP

The Acc is now the new TOS and the Y-reg being the old TOS, gets pushed onto the stack.

STREAM ( -- adr ) should now makes sense.

As we saw ( -- 1-output ) we can use the entry to JMP DUP
If we see ( -- ) as a comment following a definition, then instead of JMP DUP, we can use TYA and JMP NEXT as long as the Y-reg is not used within the sub-routine.
( 1-input -- ) we do JMP NIP as long as the Acc has not changed.
( 1-input -- 1-output ) TYA and JMP NEXT.
( 2-inputs -- 1-output ) we do a JMP POP, with the Acc holding the value that is in TOS on exit
( 2-inputs -- ) we can just do a JMP POPTWO.
( 3-inputs -- ) JMP POPTHREE
( 1-input -- 3 outputs ) DEX DEX STA $02,X LDA N JMP DUP ( N is used as a temp variable when both Acc and Y-reg are used)

This is all I will explain for now. After all the teacher cannot be doing the students homework.

One conversion I missed before the IF statement is if IF is preceded by
Code:
0=
, then the branch that follows the IF, could be either BEQ or BNE depending on the value you think is being loaded.


Last edited by IamRob on Sun Jan 02, 2022 6:09 pm, edited 3 times in total.

Top
 Profile  
Reply with quote  
PostPosted: Sun Jan 02, 2022 5:38 am 
Offline
User avatar

Joined: Sun Jun 30, 2013 10:26 pm
Posts: 1929
Location: Sacramento, CA, USA
Nicely done. Although I would suggest that you avoid confusion that may arise from naming NIP that way, because your stack effect looks something like NIP DUP ( x1 x2 -- x2 x2 ) and doesn't follow the FORTH tradition of NIP ( x1 x2 -- x2 ) which shrinks the stack with INX INX.

Your enthusiasm is contagious, but it looks like you might be chasing lots of bugs if you keep blasting full throttle. I know, I've been there.

_________________
Got a kilobyte lying fallow in your 65xx's memory map? Sprinkle some VTL02C on it and see how it grows on you!

Mike B. (about me) (learning how to github)


Top
 Profile  
Reply with quote  
PostPosted: Sun Jan 02, 2022 5:57 am 
Offline

Joined: Sun Apr 26, 2020 3:08 am
Posts: 357
barrym95838 wrote:
Nicely done. Although I would suggest that you avoid confusion that may arise from naming NIP that way, because your stack effect looks something like NIP DUP ( x1 x2 -- x2 x2 ) and doesn't follow the FORTH tradition of NIP ( x1 x2 -- x2 ) which shrinks the stack with INX INX.

I am going to decline your suggestion for now as the more code I show, will also actually break a few things. And the more things I break might show you that Forth (at least the 8-bit Forths) might have been broken from the very beginning, but is only fixed thanks due to the 16-bit instructions of the 65816. Thanks to LEEPIVONKA's source listing has given me a very good understanding of 65816 instructions. I will be crediting him much along the way.

I will leave that decision up to any followers, but all I ask is that keep suggestion to the very end as once the entire source is shown, I am sure there will be lots of suggestions, corrections and ways of improvement.

I will list as much as possible here as an open environment so anyone can participate, but I would like to lay down some general guide lines first before listing too much source.


Top
 Profile  
Reply with quote  
PostPosted: Sun Jan 02, 2022 6:28 pm 
Offline

Joined: Sun Apr 26, 2020 3:08 am
Posts: 357
Sorry about that Mike, about NIP. I thought I copy/pasted it from my working text, and here I typed in the wrong name. It should have been PUT. It is corrected in the listing above but I will re-list it here as well. Also should be noted that PUSH is also the same entry point as DUP.

Code:
PUSH EQU *
DUP DEX
    DEX

PUT  STY $00,X
   JMP NEXT

While we are on the topic of NIP, here is what I have with some words that go well with NIP. POP3 only takes 2 extra bytes and is well worth it to put here. This time I copy/pasted to prevent any mistakes.

Code:
* : POPTHREE
POP3   INX
   INX


* : POPTWO
POP2   INX
   INX
        HEX 24

* : >R ( tos -- )
TOR   PHA

* : DROP ( -- )
DROP EQU *
POP   LDA $0,X
   
* : NIP ( -- )
NIP   INX
   INX
   CPX #TOS
   BCS ERROR2
   JMP NEXT     ;  NEXT is in direct-page

ERROR2 LDA #UNDERFLOW
 JMP XERROR


Top
 Profile  
Reply with quote  
PostPosted: Sun Jan 02, 2022 11:28 pm 
Offline
User avatar

Joined: Sun Jun 30, 2013 10:26 pm
Posts: 1929
Location: Sacramento, CA, USA
I'm gonna ease up on you from over here in the peanut gallery. I don't want to harsh your mellow, so I'll read all your posts but wait until you ask for comments before offering.

_________________
Got a kilobyte lying fallow in your 65xx's memory map? Sprinkle some VTL02C on it and see how it grows on you!

Mike B. (about me) (learning how to github)


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

All times are UTC


Who is online

Users browsing this forum: No registered users and 3 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: