6502.org Forum  Projects  Code  Documents  Tools  Forum
It is currently Thu Nov 21, 2024 4:56 pm

All times are UTC




Post new topic Reply to topic  [ 354 posts ]  Go to page Previous  1 ... 7, 8, 9, 10, 11, 12, 13 ... 24  Next
Author Message
PostPosted: Mon Nov 16, 2020 4:31 am 
Offline
User avatar

Joined: Sun Jun 30, 2013 10:26 pm
Posts: 1949
Location: Sacramento, CA, USA
My way of thinking FORTH is brutally incomplete, but I did think of ?EXIT briefly. It's kind of like a conditionally executed RTS in the middle of a subroutine, efficient but a bit lacking in what I perceive to be "structured" technique. Come to think of it, I often break those techniques when I'm doing my own thing, so ...

My (probably flawed) reasoning on the subject of where to place my IFs and ELSEs and THENs is that I feel weird needlessly inserting line breaks between the data or code and the word immediately responsible for consuming or executing it. In my example, the 3 <> is immediately consumed by the following IF, and the DROP KEY is within the execution scope of the following THEN ... kind of an "object verb verb" "noun noun verb" thing, like Yoda tends to use in his sentences. As my understanding hopefully improves, I may start to see things more the way you and Garth format your code, but many miles to go still have I ... :lol:

_________________
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: Mon Nov 16, 2020 5:16 am 
Offline
User avatar

Joined: Fri Aug 30, 2002 1:09 am
Posts: 8543
Location: Southern California
Nothin' wrong with ?EXIT, as there's nothing pending on either stack like there would be with DO...LOOP (where you can remove the exit address, loop limit, and loop index with UNLOOP). Any of the structures will have stuff on the stack during compilation, but not necessarily at run time.

Putting the condition and flow-control words this way makes if very clear, and there's no visual searching:
Code:
: FOOBAR    ( n1 -- n2 f )
   <do stuff>
   <condition?>
   IF    ┌─────────────────┐
         │ <do this stuff> │
         └─────────────────┘
   ELSE  ┌─────────────────┐
         │ <do this stuff> │
         └─────────────────┘
   THEN
   <do more stuff>    ;

But if you want the IF on the same line with the condition so the line is more stack-autonomous, you can re-arrange it this way:
Code:
: FOOBAR    ( n1 -- n2 f )
   <do stuff>
   <condition?>          IF
   ┌─────────────────┐
   │ <do this stuff> │
   └─────────────────┘   ELSE
   ┌─────────────────┐
   │ <do this stuff> │
   └─────────────────┘   THEN
   <do more stuff>     ;

The latter works out really well if you have a lot of nested IF's, and no visual searching is necessary. (Color syntax highlighting is not only unnecessary when you do this, but totally undesirable, IMO.) Then you can put them one above the other instead of indenting and indenting and indenting way over to ridiculous extremes. You might end with a line of
Code:
   THEN  THEN  THEN  THEN  THEN

Some may turn up their nose at this and say if you're having to do that, then you're taking the wrong approach; but then their "fix" usually ends up overfactoring or doing something else that's just as undesirable.

_________________
http://WilsonMinesCo.com/ lots of 6502 resources
The "second front page" is http://wilsonminesco.com/links.html .
What's an additional VIA among friends, anyhow?


Top
 Profile  
Reply with quote  
PostPosted: Mon Nov 16, 2020 7:49 pm 
Offline
User avatar

Joined: Fri Aug 30, 2002 1:09 am
Posts: 8543
Location: Southern California
I seem to remember the humorous book "Starting Forth" had the following logic example as part of a getting-dressed word, to show Forth's differing approach.

Are you lacking socks?
IF so, socks-install
THEN shoes-install

(The "shoes install" object-noun word order is natural for native Korean speakers.) The THEN is not part of the IF, but instead meas "After you've taken care of that, do this." It makes perfect sense this way, even though it's different from other programming languages which would take the approach,

process dress_feet:
IF your are lacking socks, THEN put on socks. ENDIF
Now put on shoes.

_________________
http://WilsonMinesCo.com/ lots of 6502 resources
The "second front page" is http://wilsonminesco.com/links.html .
What's an additional VIA among friends, anyhow?


Top
 Profile  
Reply with quote  
PostPosted: Tue Nov 17, 2020 10:48 pm 
Offline

Joined: Fri May 05, 2017 9:27 pm
Posts: 895
GARTHWILSON wrote:

The first sample follows what so many Forthers are dogmatic about, that definitions should be limited to two lines. This kind of dogma hurts readability. It tends to result in over-factoring too, taking more memory for all the unnecessary headers for factors that are only used once, more time to run because of the extra nesting, and it can be hard sometimes to come up with a descriptive name for the factor that is any shorter than the code it replaces! There was a regular contributor to Forth Dimensions magazine who defended this strongly, and said screens should be viewed as 3x5 cards. He kept pushing this as a plus, yet I just saw it as inadequate room to make things clear and add adequate comments. Then in a private mail exchange (which today we would do by email, but back then it was on paper), he sent me his code for something we were discussing, and he had a definition that was over 100 lines long! I didn't call out his hypocrisy.


I tend to view screens as a consequence of the limitations of the technology of the day, although blocks on the C64 can also be used as a form of virtual memory. On my Linux box, I use plain text files for source.
If I have a definition on the C64 that will not fit on a single block, I just let it span however many I need. -NUMBER takes two blocks but I load something like that with THRU rather than LOAD , so it's not a problem.
I think some of the over zealous factoring came about because some Forth programmers tried to minimize what they perceived as wasted space in a source screen. I hope this example makes it evident that the "wasted space" in a source screen is not so valuable a resource as the understanding that comes from well formatted source.
I made a copy of the source for -NUMBER and reformatted it to pack it into as few lines as possible. The only rules where:
1) Three space indentation for every line in a definition after the name.
2) two spaces around each control flow word unless it is the first or last word on a line.
To say it is highly unreadable is an understatement. When I deleted the white space, I may have accidentally deleted a word or two. From the compacted form it is not easy to tell.
Code:
: -NUMBER  ( ADR -- D FLAG )  RB DUP 1+ C@ ASCII # - DUP 3 U<
   IF  BASE.TABLE + C@ BASE ! 1+ DUP  THEN  DROP DPL ON  0 0 ROT
   DUP 1+ C@ ASCII - = DUP>R - DUP>R  BEGIN  CONVERT DUP C@
   VALID?  WHILE  DUP 1+ C@ VALID? 0=  WHILE  DPL OFF  REPEAT
   THEN  DUP C@ BL <> SWAP 2- DPL @ 0< - R> = OR R> 0EXIT >R
   DNEGATE R> ;

If this was somebody's ideal, I can see why they would want to aggressively factor it (over factor it). Personally, this form looks like software that went through a virtual trash compactor.
In this case, there is no need to refactor this word, just don't format the source like this.
Even if there are any useful factors in the definition of -NUMBER , it is not easy to find them in this mess.


Top
 Profile  
Reply with quote  
PostPosted: Tue Nov 24, 2020 12:42 am 
Offline

Joined: Fri May 05, 2017 9:27 pm
Posts: 895

I was just reading "Real Time Forth" by Dr. Tim Hendtlass and becoming reacquainted with some of F-PC's words. I had forgotten that F-PC has a word called NUMBER? which is similar to Fleet Forth's -NUMBER . The only apparent difference is NUMBER? returns a TRUE flag if successful. I use the word 'apparent' because without a copy of F-PC (which I had at one time, but it's been a while) I do not know what F-PC's NUMBER? rejects as an invalid number. Fleet Forth's -NUMBER requires a numeric string to:
1) Have at least one valid digit in the current base (or the temporary base specified by a leading # , $ , or % )
2) Have no more than one occurrence of valid punctuation in a row.
3) Have a trailing blank. When interpreting, this is automatically supplied by WORD .
For example, the following are valid numbers:
1.2
1.
.1
.1.
.1.2.3.4.
-1.
$-FF
$-DEAD.BEEF
the following are not:
1..2
.
..
...
.1..2
..1
123...
-.
-
Since there is already a word that does what -NUMBER does, but with a different return flag and a slightly different name, I changed -NUMBER to NUMBER? and changed the return flag.
-NUMBER had the following test to determine if the result was valid:
Code:
   DUP C@ BL <> SWAP
   2-  DPL @ 0< - R> = OR

FALSE = valid
Thanks to De Morgan’s laws, I know I can change this test to reverse the flag without just tacking a 0= on the end to flip the flag.
Code:
   DUP C@ BL = SWAP
   2-  DPL @ 0< - R> <> AND

TRUE = valid
I also had to change a few (very few) things in the kernel and saved two bytes overall.


Top
 Profile  
Reply with quote  
PostPosted: Fri Nov 27, 2020 10:51 pm 
Offline

Joined: Fri May 05, 2017 9:27 pm
Posts: 895
GARTHWILSON wrote:

As long as things don't conflict, it's nice when you can accommodate multiple standards. The only drawback is that it takes more memory.


I wasn't trying to accommodate multiple standards. I thought it would be useful if I could specify a number as hexadecimal, decimal, or binary regardless of the current number base. The Ansi Forth Standard specified that behaviour so I appropriated that part of it.
It's still early days, but for 44 extra bytes in the kernel, I think it will be worth it.


Top
 Profile  
Reply with quote  
PostPosted: Mon Dec 07, 2020 11:16 pm 
Offline

Joined: Fri May 05, 2017 9:27 pm
Posts: 895

Fleet Forth has a word, ?MEM , to check if the amount of memory to ALLOT is available. At one point I defined ?MEM like this:
Code:
: ?MEM  ( N -- N )
   MRU OVER PAD + U< 0EXIT
   ABORT" NO MEMORY" ;

0EXIT was used to exit the word immediately, if there was no error, to avoid the overhead of (ABORT") .
This was the source for (ABORT")
Code:
: (ABORT")  ( F -- )
   IF
      WHERE CR R@ S?
      ABORT
   THEN
   R> COUNT + >R ;

Including docolon, that is seven passes through NEXT just to check for an error and keep going. Rather than using code like the above code for ?MEM to avoid that overhead in some places, I decided to make (ABORT") faster. Here is the source for the new (ABORT") .
Code:
CODE (ABORT")  ( F -- )
   0 ,X LDA,  1 ,X ORA,
   0= IF,
      IP )Y LDA,  SEC,
      IP ADC,  IP STA,
      CS IF,  IP 1+ INC,  THEN,
      POP JMP,
   THEN,
   >FORTH
   WHERE CR R@ S?
   ABORT ;
   -2 ALLOT

The new version of (ABORT") is only nine bytes larger than the older version.


Top
 Profile  
Reply with quote  
PostPosted: Mon Dec 07, 2020 11:34 pm 
Offline
User avatar

Joined: Fri Aug 30, 2002 1:09 am
Posts: 8543
Location: Southern California
UNUSED is pretty standard for your ?MEM, and is even in ANS Forth. It can be defined as:

Code:
: UNUSED        ( -- #bytes )   \                                   ANS_CORE_EXT
   LIMIT  HERE  -       ;       \ Leaves room for a max-length comment line from
                                \ TIB , HLD , and a max-length string in PAD .

I did it as a primitive in my '816 Forth, this way:
Code:
        HEADER "UNUSED", NOT_IMMEDIATE   ; ( -- bytes )
UNUSED: PRIMITIVE           ; Tells how much RAM is left to expand the dictionary
        LDA     #LIMITdata  ; I omitted CLC since 1 byte either way won't matter.
        SBC     DPdata
        JMP     PUSH


_________________
http://WilsonMinesCo.com/ lots of 6502 resources
The "second front page" is http://wilsonminesco.com/links.html .
What's an additional VIA among friends, anyhow?


Top
 Profile  
Reply with quote  
PostPosted: Tue Dec 08, 2020 12:36 am 
Offline

Joined: Fri May 05, 2017 9:27 pm
Posts: 895

?MEM is different. It does not return the amount of bytes free. Given a number of bytes, it performs the test to see if there is enough memory available. It takes the number of bytes and if that number is available, it returns that same number. This is why, in a previous post, I called it "transparent".
For example, here is the source for Fleet Forth's ALLOT .
Code:
: ALLOT  ( N -- )
   ?MEM  DP +! ;

ALLOT was previously defined without memory checking:
Code:
: ALLOT  ( N -- )
   DP +! ;

This seems more convenient to me than picking a "reasonable" amount of memory that needs to be available in CREATE or the need to check memory if I need to ALLOT some. If there is not enough memory, HERE does not change and the system aborts.
So far ?MEM is only used in two other places:
" places a string at PAD while interpreting or compiles (") and inlines the string while compiling.
FBUF in the editor.
The new and improved version of (ABORT") wasn't just to remove 0EXIT from ?MEM , but to make all uses of (ABORT") faster when there is no error to report.


Top
 Profile  
Reply with quote  
PostPosted: Tue Dec 08, 2020 1:31 am 
Offline
User avatar

Joined: Fri Aug 30, 2002 1:09 am
Posts: 8543
Location: Southern California
My UNUSED is also used in , (comma). My ALLOT uses it too, but in looking at it again now, I think I could simplify it.

_________________
http://WilsonMinesCo.com/ lots of 6502 resources
The "second front page" is http://wilsonminesco.com/links.html .
What's an additional VIA among friends, anyhow?


Top
 Profile  
Reply with quote  
PostPosted: Tue Dec 08, 2020 1:44 am 
Offline

Joined: Fri May 05, 2017 9:27 pm
Posts: 895
GARTHWILSON wrote:
My UNUSED is also used in , (comma). My ALLOT uses it too, but in looking at it again now, I think I could simplify it.


Wouldn't your , (comma) use ALLOT ?
Code:
: ,  ( N -- )  HERE 2 ALLOT ! ;
: C,  ( C -- )  HERE 1 ALLOT C! ;

It's funny. I intended my post to be about (ABORT") with ?MEM as an example.


Top
 Profile  
Reply with quote  
PostPosted: Tue Dec 08, 2020 2:59 am 
Offline
User avatar

Joined: Fri Aug 30, 2002 1:09 am
Posts: 8543
Location: Southern California
, uses 2 DP +! instead of ALLOT. Yeah, 2 ALLOT would be more efficient.

_________________
http://WilsonMinesCo.com/ lots of 6502 resources
The "second front page" is http://wilsonminesco.com/links.html .
What's an additional VIA among friends, anyhow?


Top
 Profile  
Reply with quote  
PostPosted: Sat Dec 12, 2020 1:20 am 
Offline

Joined: Fri May 05, 2017 9:27 pm
Posts: 895

I've talked about Fleet Forth's words BLOCK and BUFFER in other posts and I thought it would be good to go into them in a bit more depth here.
First is a quick look at R/W , Fleet Forth's block read/write word. Depending on the block number requested, R/W will either access a disk drive (for lower numbered blocks) or access RAM (for higher numbered blocks). R/W is not a deferred word, but the word to access the disk, DR/W , and the word to access RAM, RR/W , are.
R/W , and therefore DR/W and RR/W , take the address of a buffer, a block number, the read/write flag, and a count. This differs from Blazin' Forth. Blazin' Forth's R/W only takes three parameters, the address of a buffer, a block number, and the read/write flag. Fleet Forth's R/W was written to require a count to support a general version of INDEX . INDEX is a handy utility word that, given a starting block number and an ending block number, displays the first line for each block in that range. Ideally, INDEX only reads in one line as well (64 bytes). Fleet Forth's INDEX does so it only needs 64 bytes of memory at PAD. Since it uses R/W directly, it will work with different drive access methods, for different drive types, just like BLOCK and BUFFER .

Next is a look at the buffer table. The inspiration for the table came from Blazin' Forth. Fleet Forth, like Blazin' Forth, can support a variable number of blocks, configurable on the fly. There should never be less than one buffer, unless there will be no block access. There can be as many buffers as will fit in memory. The table consists of one entry per buffer plus one, the phantom entry. The entries are arranged in order from the phantom entry then the most recently used to the least recently used.
There are two system values used to manage the table, MRU and LRU. MRU points to the phantom entry. LRU points to the entry for the least recently used block. LRU is also headerless, since it is only used in the Forth kernel. Each entry consists of six cells: the block number, the buffer address, and an update flag. R/W does not update the order of the entries in the table. BLOCK and BUFFER handle that. The following shows four blocks with the resulting 5 table entries.
Code:
MRU:   5 | $C800 | FLAG
       5 | $C800 | FLAG
      42 | $C000 | FLAG
     137 | $C400 | FLAG
LRU:   1 | $CC00 | FLAG

A quick reminder that Fleet Forth is an ITC Forth for the Commodore 64 and when a CODE word is executed, the Y-register is set to zero. BLOCK starts out as a CODE word.
Code:
CODE BLOCK  ( BLK -- ADR )
   DEY,
   BLK/BUF STY,
   ' MRU >BODY    LDA,  N    STA,
   ' MRU >BODY 1+ LDA,  N 1+ STA,
   6 # LDY,  N )Y LDA,  0 ,X CMP,
   0= IF,
      INY,  N )Y LDA,  1 ,X CMP,
      0= IF,
         INY,  N )Y LDA,  PHA,
         INY,  N )Y LDA,
         PUT JMP,
      THEN,
   THEN,

The first thing BLOCK does is decrement the Y-register and store it's value for (possible) later use. BUFFER is a bodiless CODE word with a code field that points one byte into the body of BLOCK.
BLOCK then checks to see if the requested block is the most recently used. If so, it replaces the block number with the address of the buffer used for that block. Up to this point BLOCK behaves like a CODE word and jumps back to NEXT by way of PUT and we're done.

If the requested block is not the most recently used, the jump to PUT is branched over.
Code:
   >FORTH
   #BUF 1+ 2
   ?DO
      DUP I >BT @ =
      IF
         DROP I UNLOOP
         AHEAD CS>A
      THEN
   LOOP

>FORTH causes a transition to high level code. The ?DO LOOP checks if the requested block is in the table. If it is, the index into the table is saved on the stack (not to be confused with Forth's word INDEX ), the loop parameters are discarded and AHEAD branches out of the loop to elsewhere in BLOCK .
Code:
      LRU 2+ 2+ @
      IF
         LRU 2@ 0 B/BUF R/W
         LRU 2+ 2+ OFF
      THEN

If the requested block is not in the table, the least recently used block is written to disk (or ram) if its update flag is true. The update flag is not cleared until after the block is written in case the write fails.
Code:
      BLK/BUF C@
      IF
         LRU ON
         LRU 2+ @ OVER 1 B/BUF R/W
      THEN
      LRU ! #BUF

The value of the Y-register that was saved earlier is fetched to see if this code was called as BLOCK or BUFFER . If this is BLOCK , if the read fails, the contents of the buffer will not be valid (most likely) so the entry for the block number of the least recently used block is set to TRUE (-1) to indicate an unused buffer. The new block is read in to the least recently used buffer.
Both BLOCK and BUFFER then store the requested block number in the block number entry for the least recently used block/buffer and leave the index to the entry for the least recently used block/buffer on the stack.
Code:
   A>CS THEN
   DUP >BT MRU 6 CMOVE
   MRU 1 >BT ROT 6 * CMOVE>
   MRU 2+ @ ;

The branch out of the ?DO LOOP branches to this section of code. At this point one of two index values will be on the data stack.
If the requested block number was found in the table the index to the table entry with the desired block number is on the data stack.
If it was not found in the table the index of the least recently used block/buffer is on the data stack.
This table entry is copied to the phantom entry. The phantom entry and all entries above the one with its index on the stack are now slid down by one entry.
If a block that was not in the table, such as 123, was requested, this is what it would look like:
Code:
MRU: 123 | $CC00 | 0
       5 | $C800 | FLAG
      42 | $C000 | FLAG
     137 | $C400 | FLAG
LRU: 123 | $CC00 | 0

Code:
MRU: 123 | $CC00 | 0
     123 | $CC00 | 0
       5 | $C800 | FLAG
      42 | $C000 | FLAG
LRU: 137 | $C400 | FLAG

If however a block that was in the table, such as 42, was requested, this is the result:
Code:
MRU:  42 | $C000 | FLAG
       5 | $C800 | FLAG
      42 | $C000 | FLAG
     137 | $C400 | FLAG
LRU:   1 | $CC00 | FLAG

Code:
MRU:  42 | $C000 | FLAG
      42 | $C000 | FLAG
       5 | $C800 | FLAG
     137 | $C400 | FLAG
LRU:   1 | $CC00 | FLAG




Top
 Profile  
Reply with quote  
PostPosted: Sat Dec 12, 2020 2:50 am 
Offline

Joined: Sun Apr 26, 2020 3:08 am
Posts: 357
Thanks for delving into BLOCK and BUFFER for Fleet Forth.

I was in the process of deciphering what they do and how they work in ProForth. ProForth is a Forth for the Prodos OS on an Apple II. ProForth also has 2 buffers laid out as blocks. A ProForth block is 1024 bytes which happens to also be 16 lines of 64 bytes. The same as Fleet Forth's line limit.

Because of the great similarities between ProForth and FleetForth, I have taken a great interest in a lot of your improvements for FleetForth, and hope to adapt many of them to ProForth.

Just a couple of quick questions.

How does FleetForth set a device # to know which device to read from or write to before using BUFFER and BLOCK?

Under ProDOS, the BLOCK parameter list contains a device #. So far this has been hard coded into ProForth. I would like to change that so I can load any text file from any slot/drive combination. Just seeing what else is out there for word primitives that may already exist. DR0 and DR1 were only meant to signify drives and were originally programmed for floppies. Under ProForth, they are not used at all but are still included in word definitions. What does the C64 have in the way of device types. I believe there was an add-on SD card for the C64 port? That would mean you would need a word under FleetForth to identify which Port to save to other than the floppy drive, would you not?

Is FleetForth reading text files as a sequential text file (one line at a time) or as a chunk of 1024 byte screen files? Proforth is set up to load 16 screens all at the same time and the text file is a fixed 16384 ($4000) = 16 screens of 1024 bytes. Then the screens are accessed with the LIST word from 0-15 and a screen is loaded using the LOAD word.

I hate this method and want to get away from it. Would simply like to use OPEN "textfile" and LOAD to load it.


Top
 Profile  
Reply with quote  
PostPosted: Mon Dec 14, 2020 11:13 pm 
Offline

Joined: Fri May 05, 2017 9:27 pm
Posts: 895

I do not recall an SD card for the C64. I do know that there were Ram Expansion Modules available for the C64 and C128. The module plugged into the expansion port (cartridge port). As for the disk drives, they were external units interfaced by a serial cable, a serial version of IEEE-488. The drives had two connectors for the serial cable so multiple units could be 'daisy chained' together. Device numbers 8 and up were typically for disk drives.

I think I've mentioned this elsewhere, Fleet Forth determines which drive to use for block access based on block number. Originally, only five drives were supported but it takes less code to support eight drives. Here a table showing which block numbers go to which drive.
Code:
Actual        | C64    | device
block range   | device | block range
              |        |
    0 - $0FFF |    8   | 0 - $0FFF
$1000 - $1FFF |    9   | 0 - $0FFF
$2000 - $2FFF |   10   | 0 - $0FFF
$3000 - $3FFF |   11   | 0 - $0FFF
$4000 - $4FFF |   12   | 0 - $0FFF
$5000 - $5FFF |   13   | 0 - $0FFF
$6000 - $6FFF |   14   | 0 - $0FFF
$7000 - $7FFF |   15   | 0 - $0FFF
$8000 - $FFFF |  REU   | 0 - $7FFF

Block numbers are unsigned. The code word R/W determines which device is used.
Code:
HEX
CODE R/W  ( ADR BLK# R/WF CNT -- )
   BEGIN,
      SEC,
      5 ,X LDA,  10 # SBC,
   CS WHILE,
      5 ,X STA,
      INY,  8 # CPY,
   0= UNTIL,
      ' RR/W SPLIT SWAP
      # LDA,  # LDY,
      (EXECUTE) 0= NOT BRAN,
   THEN,
   DRB STY,
   ' DR/W SPLIT SWAP
   # LDA,  # LDY,
   (EXECUTE) 0= NOT BRAN,
END-CODE

(EXECUTE) is a metacompiler label that points into EXECUTE . The two branches into the body of EXECUTE are always taken. A branch always instruction would have been nice but the C64 has the 6510 processor. No CMOS version.
Code:
HEX
CODE EXECUTE  ( ADR -- )
   0 ,X LDA,
   1 ,X LDY,
   INX,  INX,
   LABEL (EXECUTE)
   W 1+ STY,  W STA,
   0 # LDY,
   W 1- JMP,
END-CODE

Although the system will choose a specific drive for each range of 4096 blocks, not all blocks will be available.
For example, if device 8 is a Commodore 1541 drive, only 166 blocks are available. This means that the range of available blocks is not continuous. The range selected for the drives is a design consideration I made to facilitate copying blocks from one device to another while allowing a larger capacity drive to have more blocks available than what is possible with the 1541 drive.
The maximum REU size that is compatible with other C64 REU's is 16 megabytes or 16384 blocks. The 1764 REU had a capacity of 256 blocks and the 1750 REU had a capacity of 512 blocks.

For block numbers less than $8000, the deferred word DR/W is called otherwise the deferred word RR/W is called.

Fleet Forth has the helper words DR+ and RAM to help hide this, or at least make it so the exact numbers do not need to be known. RAM just adds $8000 to the number on the stack. One use for blocks in the REU is virtual memory.

DR+ takes two parameters, the block number and the drive to use. If I want to see an INDEX of the first 10 blocks on drive 9 I would type this:
Code:
0 9 DR+ 10 9 DR+ INDEX

Note: For drive 8, DR+ is not needed. This:
Code:
5 8 DR+ BLOCK

Is effectively the same as this:
Code:
5 BLOCK

I placed Fleet Forth's system loader in drive 9 and got an index while the base was 16 and then set it to decimal and got the index again. This is the log of the session. Both what I typed and the computer's responses are shown.
Code:
 OK
0 9 DR+ 10 9 DR+ INDEX
1000 ***********************************
1001 // LOAD BLOCK
1002 // 2ND LOAD BLOCK -- EMPTY
1003 // DONE? THRU
1004 // SYSTEM CONSTANTS
1005 // OPCODE OFFSET TABLE AND MODES
1006 // BOT SEC RP)
1007 // CPU0 & IMPLIED ADDRESSING
1008 // CPU1
1009 // W/OUT IMPLIED ADDRESSING MODE
100A // W/OUT IMPLIED ADDRESSING MODE
100B // ?EXEC NOT OFFSET BRANCHES
100C // THEN,
100D // AHEAD, IF, ELSE, WHILE, ELIF,
100E // BEGIN, AGAIN, UNTIL, REPEAT, BRAN,
100F // SUBR CODE ;CODE END-CODE
1010 // >FORTH >ASSEM
 OK
DECIMAL  OK
0 9 DR+ 16 9 DR+ INDEX
 4096 ***********************************
 4097 // LOAD BLOCK
 4098 // 2ND LOAD BLOCK -- EMPTY
 4099 // DONE? THRU
 4100 // SYSTEM CONSTANTS
 4101 // OPCODE OFFSET TABLE AND MODES
 4102 // BOT SEC RP)
 4103 // CPU0 & IMPLIED ADDRESSING
 4104 // CPU1
 4105 // W/OUT IMPLIED ADDRESSING MODE
 4106 // W/OUT IMPLIED ADDRESSING MODE
 4107 // ?EXEC NOT OFFSET BRANCHES
 4108 // THEN,
 4109 // AHEAD, IF, ELSE, WHILE, ELIF,
 4110 // BEGIN, AGAIN, UNTIL, REPEAT, BRAN,
 4111 // SUBR CODE ;CODE END-CODE
 4112 // >FORTH >ASSEM
 OK
CONSOLE

Fleet Forth does not store blocks in sequential files. Commodore sequential files are not random access. Fleet Forth, like Blazin' Forth, uses direct access to read four 256 byte disk sectors per Forth block. The sector mapping for the 1541 drive is compatible with Blazin' Forth. In other words, Fleet Forth can read blocks from a 1541 disk used by Blazin' Forth and vice versa.
I know that C64 Forth by Tom Zimmer used Commodore relative files for blocks and I may experiment with that approach one day.

To be able to access blocks on a disk, the drive must be opened with the word DOPEN . This word takes no parameters and leaves no parameters. It opens the disk on the current drive. The current drive is 8 upon startup or a cold start but can be changed with the word DRIVE . For example, if I wanted to access blocks on drives 8 and 9, I could do the following:
Code:
8 DRIVE DOPEN 9 DRIVE DOPEN

If I know that the current drive is drive 8, I could leave off the phrase '8 DRIVE'.
Before removing a disk from a drive, the drive must be closed with the word DCLOSE . DCLOSE also takes no parameters and leaves no parameters. As with opening a drive for block access, if I want to close a drive for block access, it needs to be the current drive. After the previous example drive 9 is the current drive.
Code:
8 DRIVE DCLOSE

Now 8 is the current drive again.

Fleet Forth maintains one or more buffers for blocks. I usually use four, but I sometimes use more when manipulating blocks. The buffers are right up against the memory limit specified by the system value LIMIT .
LIMIT can be lowered if the need arises. The buffer access table sits right up against the buffers. Although Fleet Forth reads blocks using direct access to read four Commodore disk sectors, it reads each block as a one kilobyte chunk. Fleet Forth only reads one block at a time and only writes one block at a time.
The following will change the number of block buffers.
Code:
n TO #BUF CONFIGURE

Since DR/W is a deferred word, it can be re-vectored to a new word to allow access to different hardware. It could even be re-vectored to a word that used Commodore relative files for blocks.

Since RR/W is also a deferred word, it can also be re-vectored. Instead of using a Commodore Ram Expansion Unit, it could be re-vectored to a word that uses a hard drive for the blocks in the range $8000 to $FFFF.
I hope this helps.


Top
 Profile  
Reply with quote  
Display posts from previous:  Sort by  
Post new topic Reply to topic  [ 354 posts ]  Go to page Previous  1 ... 7, 8, 9, 10, 11, 12, 13 ... 24  Next

All times are UTC


Who is online

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