Translating fig-forth source to CA65

Discuss technical or other issues relating to programming the Nintendo Entertainment System, Famicom, or compatible systems. See the NESdev wiki for more information.

Moderator: Moderators

Post Reply
russellsprouts
Posts: 53
Joined: Sun Jan 31, 2016 9:55 pm

Translating fig-forth source to CA65

Post by russellsprouts »

Just to try it out I've been trying to translate the source code of fig-forth for 6502 to CA65. I've created a 6502 emulator with a NES-like memory map, but with RAM everywhere instead of ROM, and an extra IO port in the $4xxx area for reading from the keyboard and outputting to the console. The idea is that this emulator is the "developer cart", and after the forth code is finished, I will freeze the RAM into a ROM file with a tiny bit of reset code. I'm working on writing my own forth which uses a mix of subroutine threading and inline code, but I want to get fig forth running first.

The original source is here.

I don't know what assembler it was created for, but it's pretty straightforward. I've just had to add : for labels and convert ' into ". There are a couple lines I'm not sure how to handle:

Line 66:

Code: Select all

* = * + 2
I assume that this is basically

Code: Select all

.res 2
?

Line 93:

Code: Select all

;    The following offset adjusts all code fields to avoid an
;    address ending $XXFF. This must be checked and altered on
;    any alteration , for the indirect jump at W-1 to operate !
;
          .ORIGIN *+2
I understand the intent: the remaining code must be aligned correctly, because the indirect threaded interpreter invokes jump indirect on each address in the byte code. Does this mean that another .res 2 will give the right padding?
User avatar
thefox
Posts: 3134
Joined: Mon Jan 03, 2005 10:36 am
Location: 🇫🇮
Contact:

Re: Translating fig-forth source to CA65

Post by thefox »

Yeah, I'd figure .origin *+2 is the exact same thing as *=*+2.

Code is kind of hard to follow so it's difficult to check whether it does what it promises, though.
Download STREEMERZ for NES from fauxgame.com! — Some other stuff I've done: fo.aspekt.fi
Garth
Posts: 246
Joined: Wed Nov 30, 2016 4:45 pm
Location: Southern California
Contact:

Re: Translating fig-forth source to CA65

Post by Garth »

russellsprouts wrote:Just to try it out I've been trying to translate the source code of fig-forth for 6502 to CA65. I've created a 6502 emulator with a NES-like memory map, but with RAM everywhere instead of ROM, and an extra IO port in the $4xxx area for reading from the keyboard and outputting to the console. The idea is that this emulator is the "developer cart", and after the forth code is finished, I will freeze the RAM into a ROM file
I don't remember for sure without looking back through it, but there may be places where variable data is mixed in with the code, so you'll have to separate that out to RAM.
I'm working on writing my own forth which uses a mix of subroutine threading and inline code
Bruce Clark has a good post on 6502.org about how that's actually more memory-efficient than one would think. He gives 9 reasons, starting in the middle of his long post in the middle of the page.
The original source is here.

I don't know what assembler it was created for
It's the Rockwell AIM-65.
I understand the intent: the remaining code must be aligned correctly, because the indirect threaded interpreter invokes jump indirect on each address in the byte code. Does this mean that another .res 2 will give the right padding?
The alignment was necessary because of the NMOS's JMP xxFF bug. That (and all bugs) was corrected in the CMOS version, but I still kept the alignment in spite of the small memory penalty for other reasons I don't remember for sure. I still use my '02 Forth frequently on the workbench, but the innards have been in place for 20+ years and I have not paid attention to those details since I got settled on them. What comes to mind is that the alignment made it easier to decompile (with the ANS word SEE).

I would caution you about some bugs in the common 6502 Forths: Someone is pushing me just a little to publish my 65816 Forth. I'd like to clean that up, and my '02 Forth as well, and put them on my website. The '02 will be especially a lot of work. These are much more full-featured than fig-Forth though. My '816 Forth runs about 2.5 times as fast as my '02 Forth at a given clock speed, due to the 816's much more graceful handling of the 16-bit quantities. Both these Forths are ITC; but the '816 made it practical and memory-efficient to have many hundreds of primitives (ie, code definitions), so that many of the words that were secondaries in '02 Forth now as primitives can dramatically reduce the incidence of NEXT, nest, and unnest in the process of getting a job done. So there's even less overhead than STC's JSR-RTS combination during the running of the word.
http://WilsonMinesCo.com/ lots of 6502 resources
russellsprouts
Posts: 53
Joined: Sun Jan 31, 2016 9:55 pm

Re: Translating fig-forth source to CA65

Post by russellsprouts »

Thanks Garth. I suspected there might be issues with variables and things assuming dictionary space will stay in RAM. I don't really feel like rewriting a bunch of those. I'll probably just use the fig-forth code as a reference and implement mine to handle those issues.
Garth
Posts: 246
Joined: Wed Nov 30, 2016 4:45 pm
Location: Southern California
Contact:

Re: Translating fig-forth source to CA65

Post by Garth »

The '02 Forth I started with, for ROM, using a metacompiler, used HERE in the normal way, but used THERE to keep track of where the next available RAM space was as the variables' NFAs, LFAs, and CFAs were built up in ROM as the kernel was compiled. IOW, only the PFAs were in RAM. It wouldn't take much to modify a source code to do this. You only need to modify the definition of the one word, VARIABLE. Then when the kernel runs from ROM, THERE is made to be the same as HERE, so there's no longer a difference.

Edit: If you're using an assembler to build it and not a metacompiler, it will be a little different. I'll try to look up what I did in later years for an assembly source and post again.
http://WilsonMinesCo.com/ lots of 6502 resources
Garth
Posts: 246
Joined: Wed Nov 30, 2016 4:45 pm
Location: Southern California
Contact:

Re: Translating fig-forth source to CA65

Post by Garth »

Garth wrote:I'll try to look up what I did in later years for an assembly source and post again.
This is from my 65816 Forth, with A in 16-bit mode and index registers in 8-bit mode. Things are pretty intertwined; but if you've been working with '02 ITC Forth, you probably have enough of a handle on it to figure things out from what's below. The '816 is actually easier than 6502, since you don't have to handle the low and high bytes separately and watch for carries in between. The following five snippets of code are separated by a lot of other material in my main 816FORTH.ASM file.

Code: Select all

DPdata: EQU     THERE
 HEADER "DP", NOT_IMMEDIATE  ; HERE returns the contents of DP .
DP:     VARIABLE             ; DP is init'd by COLD, which stores the INIT_DP assembler constant in it.
                             ; INIT_DP is in turn given the final value of THERE at the end of assembly.


const:  LDY    #<2           ; runtime routine pointed to by the CFA of words created by CONSTANT
        LDA    (W),Y
PUSH:   DEX2
PUT:    PUT_TOS
 ;-------------------

                             ; VARIABLE below is for use by the assembler while it's creating the Forth
                             ; kernel.  But when the kernel is running, it uses target_VARIABLE below.
VARIABLE:      MACRO         ; VARIABLE is like a constant that points to the variable's data space.
        DWL    const, THERE
 THERE: SETL   THERE + 2     ; Increment that address by two.
        ENDM
 ;-------------------


        HEADER "VARIABLE", NOT_IMMEDIATE     ; ( <name> -- )
target_VARIABLE:   DWL   nest, CREATE, ZERO, COMMA, unnest
 ;-------------------


        HEADER "HERE", NOT_IMMEDIATE         ; ( -- addr )
HERE:   PRIMITIVE
        LDA     DPdata
        JMP     PUSH
 ;-------------------
  • DWL is the C32 assembler's "Define Word, Low byte first," which I think is like other assemblers' .WORD.
  • SETL is the C32 assembler's "SET Label," like EQU but it can be changed as many times as you like.
  • DEX2 is just the macro that lays down DEX DEX, to shorten the source-code file since this in needed so many times in Forth.
  • PUT_TOS is the macro that lays down the two instructions to put A in the cell at the top of the data stack (with STA 0,X) and jumps to NEXT. I did it in a macro because there are a couple of choices for how to do it, and you only have to set the choice in one place and then all the macro invocations will do it the same way. It's to allow interrupt service in high-level Forth, and to use a different NEXT like if you want to do a trace, or something like that.
  • HEADER is the macro that lays down the name and link fields.
  • NOT_IMMEDIATE and IMMEDIATE are assembler constants (EQUates) given as a parameter to the HEADER macro to tell it what smudge bit value to put in the name field.
  • PRIMITIVE is the macro that just lays down a two-byte code field pointing to the parameter field, with DWL $ + WSIZE, to indicate that the machine-language code for the word starts immediately following.
If that's all as clear as mud, ask your questions and I'll try to clarify it.
http://WilsonMinesCo.com/ lots of 6502 resources
Garth
Posts: 246
Joined: Wed Nov 30, 2016 4:45 pm
Location: Southern California
Contact:

Re: Translating fig-forth source to CA65

Post by Garth »

I'll add another note. My 6502 stacks treatise has Appendix A which is a big start on a STC Forth, intended for someone who wants to be able to do assembly language in a very Forth-like way without necessarily using Forth. (I say "big start" because although the stacks treatise was finished and posted a couple of years ago, I never finished this appendix. It should still save you a substantial amount of work though.)

The following paragraphs are a slight tweak of the text at the top of that file.

The material there is much of what you would need to do a subroutine-threaded code (STC) '02 Forth. It has the stack operations for simple math, logic, memory, strings, etc., and if I ever get around to finishing it, it will also have things like trig, log, square-root functions. I've added another major feature to the site after I posted the stacks treatise, and the next major one will probably be my '816 Forth. Your work on your '02 STC Forth, if you share it russellsprouts, may help me finish up this Appendix A.

What it is primarily missing for doing a STC Forth is the components for doing its own compiling and for interpreting command lines (since there are no headers here for WORD to find the addresses of various routines), and compiling the control structures.

ZP data stack cell size throughout is 16 bits, ie, two bytes. Double-precision numbers take 32 bits, or two cells of two bytes each, ie, four bytes total. Characters or bytes still take 16 bits, but the high byte is zeroed.

For individual cells, byte order is the same as 6502: low byte first. For double-precision numbers, the low cell is pushed onto the stack, followed by the high cell. So if you have a double-precision number that's small enough to fit in a single cell (without changing the sign), you can convert the double to a single with just DROP (in fact, StoD uses the same code).

Most of these get much shorter on the 65816 since it is far more efficient at handling 16-bit quantities. 816StackOps.ASM (Appendix B) is planned for that. Right now there's just a placeholder file for it.

Have an 8-byte section of ZP called N, for routines' temporary storage. N-1 is also used, so don't have something else coming right up to N. N is introduced 80% of the way down the page in section 4 of the stack treatise on virtual stacks.
http://WilsonMinesCo.com/ lots of 6502 resources
Post Reply