Page 2 of 8
Re: screen editor design
Posted: Tue May 06, 2014 8:00 am
by chitselb
EDITDEL (STOP-D) works, almost. There's a bug in the screen wrap logic when I delete the very top line, but otherwise it handles 40 and 80 columns well.
Code is here. Update: The most annoying fencepost condition in the world has been fixed. EDITPASTE is still a disaster.
Update: It's all good! Line copy/cut/paste is working as designed. woot!!
Re: screen editor design
Posted: Wed May 07, 2014 4:08 am
by chitselb
With the line editing working, I'm moving on to the paging up and down through screens. Moving to a screen that doesn't exist automagically creates it. Navigating away from a screen (or quitting the editor) automagically saves it to the buffer. Screens/Blocks are numbered sequentially, starting with 1. The block number is not stored anywhere, but is the number of blocksize hops down from the top of memory.
- STOP-HOME screen 1 (top)
- STOP-UP previous screen
- STOP-DOWN next screen
- STOP-INS insert a screen before current screen
- STOP-DEL delete current screen
- STOP-I index screen composed of the first 40 characters of block 1...block 25
- STOP-R restore current screen from last time we exited it
Here's the memory scheme for my "virtual memory with cassette tape" design. Sets of blocks are loaded, saved and verifed (STOP-L, STOP-S, STOP-V) to either disk or tape as standard Commodore PRG files. The load address of the PRG file would be the first byte used, and the end address will always be $7C00 (MEMSIZ minus 1K), or whatever the top of memory is on your PET. Each compressed screen in the buffer will be a packet, with the last two bytes being the size of the packet. Here's some bad ASCII art to illustrate what this area at the top of RAM will look like
Editor buffers build downward in RAM from the top, indicated by MEMSIZ (stored at $34-$35 after RESET)
Code: Select all
| video ram $8000 | <--- MEMSIZ (e.g. $8000 on 32K PET)
+===============================+
| static 1K block buffer | MEMSIZ-1024...MEMSIZ-1 ($7C00 on 32K)
| used by compression words ... |
+-------------------------------+
| current screen (editor start) | MEMSIZ-1024-2 ($7BFE)
+-------------------------------+
| block 1 size | MEMSIZ-1024-4 ($7BFC)
+-------------------------------+
|f e d c b a 9 8|7 6 5 4 3 2 1 0| linewrap table of 24 high bits $E1-$F8
+---------------+---------------+ bit 0 = line24, bit n = line1,
| RLE encoded |n m l k j i h g| line0 $E0 is always "1" ($7BF9)
| screen image +---------------+
| stored as screen codes |
| BLOCK 1 ... | MEMSIZE-1024-2-{block 1 size}
+-------------------------------+
| block 2 size | size of block 2
| . |
| . |
| . |
| BLOCK N ... |
+-------------------------------+
| 0 | a zero-sized block marks the bottom of the block buffer
+-------------------------------+ the address of this word is the load/save address
I'm adding new words to the USERAREA. Are these stupid names?
MEMSIZ = $8000
EDITBLK = $7BFE
Behavior question: I COLD start PETTIL. This puts a zero in EDITBLK (no blocks are loaded or created). I immediately enter the editor by typing EDIT. Since I came in cold, whatever is on the screen will remain, but I'll be in edit mode. I type a few lines, and then... STOP-HOME. Does the editor save the current screen as block 1, and then navigate to block 1? This would turn STOP-HOME into a handy place to anchor something for future STOP-R (restore) operations. I'm also not entirely confident about requiring the user to manually and explicitly clear the paste buffer with STOP-Z (zilch), because the other commands append to it.
Re: screen editor design
Posted: Wed May 07, 2014 7:26 pm
by chitselb
I present
RLENCODE and RLDECODE, in 22 bytes of header + a measly 88 bytes of code!
Re: screen editor design
Posted: Thu May 08, 2014 2:39 am
by barrym95838
I'm really enjoying your tumblr blog. I find it entertaining to be able to read about all of the little details on the bumpy road to a finished project, and I like the way that your 'voice' comes through in your posts ... it's an elusive quality that some of us struggle to maintain, with mixed success. I enjoy it more than github, which is still a confusing and awkward browsing experience for me.
Mike
Re: screen editor design
Posted: Thu May 08, 2014 5:03 pm
by White Flame
It might save you a few bytes of code footprint, and probably much more in your compressed buffers, to not do "full" RLE, but just record spans of spaces vs non-spaces. That's really where your compression ratio is going to come from anyway, unless somebody likes making horizontal lines of dashes and such. It saves a byte from each compressed tag, because the byte that's being repeated is implied to be a space character and no longer needs to be included.
However, reading from your code, do you RLE tag
everything? As in a line of "abcde " would be .byt 'a',1,'b',1,'c',1,'d',1,'e',1,' ',35 ?
Usually, RLE compression will use 1 bit of the length byte to flag if the next bytes are repeated or not. Thus, the above example would be .byte 5,"abcde",35|128,' '. This assumes that if the high bit is clear, then that many raw bytes follow. If it is set, the lower 7 bits say how many times to repeat the following byte. A repeated byte is only used when at least 2 are in succession, so "2 3 +" would end up as a span of 5 bytes, followed by a repeated span of 35 spaces, even if you're only RLE-ing spaces.
Code: Select all
Encoding " 3 2 +": (3 leading spaces, some text, then the rest of the line empty)
;; Always using RLE tags, even for single bytes
.byte " ",3, "3",1, " ",1, "2",1, " ",1, "+",1, " ",32
; = 14 bytes
;; Using a bit flag for compressed vs uncompressed
.byte 3|128," ", 5,"3 2 +", 32|128," "
; = 10 bytes
;; Implied spaces as the compressed character
.byte 3|128, 5,"3 2 +", 32|128
; = 8 bytes
Re: screen editor design
Posted: Fri May 09, 2014 4:43 am
by chitselb
Here's what I came up with during the 8 hour car ride between Washington DC and Syracuse NY:
When decompressing blocks, RLDECODE decompresses until {compressed size} bytes of source buffer are exhausted.
Before decompressing, target buffer will be initialized to 1024 spaces
The corollary to this is, compressed buffers never need to store any trailing spaces at the end of the buffer, RLE-encoded or otherwise
When RLENCODE does its thing, it will fail (and store the block uncompressed) if the compressed size exceeds 1024 bytes. This prevents the pessimal case "AABBAABBAABBAABBAABBAABB..." repeated to fill the block from being expanded to "AA",2,"BB",2,"AA",2,"BB",2,"AA",2,"BB",2... (a 50% expansion in the worst-case scenario)
BLOCKs are numbered starting at zero, as per the Forth-83 standard
The number of compressed blocks currently stored in high memory will be kept in the uservariabe VIRTSIZ
VIRTSIZ can expand and contract during the editing session
From a command line, saying "50 BLOCK" after cold start would create empty blocks 0..50
Each empty block so created will contain the word $0002. The length is 2. There are no spaces stored. There are no linewrap bits stored because it's a data block until the screen editor updates it.
I haven't really worked out how and when the three $FFFFFF bytes (representing lines 1..24 are each 40 characters) gets connected to the stored blocks, other than "the editor does it if it tries to edit a block that doesn't have them, and the editor always stores the linewrap table as three bytes after the RLE encoded screen codes
The buffer returned would contain 1024 spaces
EDIT will now require a block number at TOS as an argument
The 2-bytes of current screen currently stored at $7BFE will no longer be stored in the virtual memory buffer. Instead it will become the new uservariable SCR
SCR will be initialized to 0 on CASSLOAD
CASSLOAD is a word which loads the next file named "PETTIL BLOCKS" from cassette tape, or the file named "PETTIL BLOCKS" from disk
the new uservariable DRV will determine the device used for mass storage (initialiized at COLD to 1 for tape #1)
If DRV contains device #>3, the filename saved by SAVE-BUFFERS will be prepended with "@0:" becoming e.g. "@0:PETTIL BLOCKS",8 . Otherwise SAVE-BUFFERS will save to e.g. "PETTIL BLOCKS",1
The start address of SAVE-BUFFERS is calculated by hopscotching backward through block sizes (first one, 0 BLOCK, now has its length word stored at $7BFE) until a zero-sized block (end of virtual memory) is encountered at the bottom address
: EMPTY-BUFFERS VIRTSIZ OFF ;
I hope this is clear. This is scheme "a" Eventually two more virtual memory schemes will be implemented
a) virtual memory on cassette -- groups of blocks loaded/saved to high memory
b) virtual memory on floppy -- like Blazin' Forth does on the C=64. Multiple drives concatenated to increase VIRTSIZ, which is static (the total number of 1K blocks on all drives)
c) virtual memory on PETdisk -- bitfixer.com device which makes a microSD card pretend to be a disk. This will require some atmel coding on the PETdisk and some way of interfacing raw block storage between PET and PETdisk
when LOAD happens, I am very strongly considering treating the end-of-logical-line as a space. I can probably manipulate the beginning of line pointer at $C4-C5 and the line size, (I am pretty sure it's $D8, holding a 39 or 79) to trick the screen editor and kernel CHRIN routine to input from a fake screen at $7C00. How big a party foul is that?
I am also pretty sure I can get away with having only one 1K block buffer at $7C00-$7FFF to make all of this work.
Re: screen editor design
Posted: Sun May 11, 2014 4:40 pm
by chitselb
I'm really enjoying your tumblr blog...
Mike
Mike,
I appreciate that more than you know. Some days it feels like I'm the only guy in the world who wants to do the most kickass Forth ever for the Commodore PET. I'll try to keep the quality and quantity of my efforts up, at least until the job is done and I move on to some new curious obsession of the week. Here's a new one I'm calling
Virtual Memory on Cassette Tape
Re: screen editor design
Posted: Sun May 18, 2014 3:49 pm
by chitselb
If the user types, e.g.
they're in the editor, editing away on block 3, which was retrieved from a ramdisk and created if necessary.
Wouldn't it be pretty useful to say
(or any negative value) and have that just start the editor, without fetching a screen, so the current contents of the screen could be captured?
Okay so let's say that's how it works. When the user UPDATEs that captured screen, e.g. by going to another screen, where is it written in the ramdisk? Appended as the last block?? As the new block 0?? Something else?
Re: screen editor design
Posted: Sun May 18, 2014 4:02 pm
by barrym95838
Could you add 'virgin' flags to your screens? If so, another word, like CAPTURE (or maybe even a hot-key), could find the first virgin (or maybe just blank) screen and copy the current contents there before launching into EDIT.
I confess that I don't know how your interactive console 'screen' is implemented. Could it be morphed into an actual storage-class 'screen'? Or am I just spouting nonsense?
Mike
Re: screen editor design
Posted: Mon May 19, 2014 1:13 am
by chitselb
Here's the Forth-javadoc for it:
EDIT ( scr -- )
Sets the SCR user variable and launches the full screen block editor. If {scr} is negative, a new packet is appended to the virtual memory buffer and becomes the current SCR.
Hot keys aren't trivial on the PET, and are very un-Forth-like. I came across another Forth (Durex Forth) for the C=64 which I haven't really analyzed yet, but I was a little squicked when I learned that their screen editor is a vi clone.
Re: screen editor design
Posted: Wed May 21, 2014 4:25 am
by chitselb
top of dictionary now at 26c7 no headers, 2f8f with headers
difference = 8C8 or 2248 decimal bytes
As an experiment, I surrounded all the Link and Name fields in my Forth with "#ifdef HEADERS / #endif" and did a build. On a 32K machine, that's probably a big deal, because I'm going to need every byte I can get for this videogame application. I expected more, but it's still worth going after. If I detach the symbol table from the dictionary, and it looks like reserving 4K of symbol table space for "development" mode will more than suffice. I can also move all of the outer interpreter and compiler into a second dictionary, because that code becomes worthless once the name fields vanish and so won't be used in the target compiled environment. That might be another 2K of code for IF THEN ELSE BEGIN WHILE REPEAT UNTIL AGAIN DO LOOP +LOOP : ; CREATE VARIABLE CONSTANT QUIT ... I wonder if there's any other big chunks of low hanging fruit like that, before I start to go after individual words that aren't required?
Re: screen editor design
Posted: Wed May 28, 2014 9:12 pm
by chitselb
I feel bad about kicking vocabularies to the curb. If I were to split the headers off from the code, with the headers constructed like in the diagram below, is there enough information to put vocabulary back? Assume it's possible to slide words around at will. Perhaps the unused flag bit could be used to tag the tail end of a vocabulary?
for our example, here's ALLOT :
Code: Select all
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| $1467 | +0 code field
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
[0] | immediate bit +2 name field length/flags byte
| [0] | smudge bit
| [0]_________| unused, always a 0
| [ 5 ] name length 0..31
+-+-+-+-+-+-+-+-+
| A | +3 name field first letter
+-+-+-+-+-+-+-+-+
| L |
+-+-+-+-+-+-+-+-+
| L |
+-+-+-+-+-+-+-+-+
| O |
+-+-+-+-+-+-+-+-+
| T | +n name field last letter
+-+-+-+-+-+-+-+-+
Re: screen editor design
Posted: Thu May 29, 2014 10:30 am
by Brad R
I'd say it depends on what you want to use vocabularies for. There's no rule that says vocabularies have to be kept in multiple linked lists. You could, for example, add a single byte to the header of each word, indicating what vocabulary that word belongs to. (If you make that byte part of the name, the vocabulary match is done as part of the name comparison.) That would let you have 256 vocabularies, which is enough for any Forth application I've ever seen.
Of course, implementing it that way does not speed compilation in the slightest. I do know of people who have broken their dictionary into multiple vocabularies in order to speed dictionary search. But I tend to think of that as an incidental benefit of certain implementations. The purpose of vocabularies (IMHO) is to give you multiple, independent namespaces.
Re: screen editor design
Posted: Thu May 29, 2014 9:00 pm
by chitselb
new blog post, hopefully solving the riddles.
Re: screen editor design
Posted: Fri May 30, 2014 7:21 pm
by chitselb
Where I'm running into a snag is redefining an existing word. In the headless model, the new CFA replaces the existing CFA in the symbol table.
What FIG Forth CREATE does
- copy the name to HERE+1
- store the length at HERE
- set the smudge bit on the length byte
- FIND still locates the pre-existing word, because smudge hides the new one
- when closing the definition later, either ; ;CODE or END-CODE turns off smudge
Here's my not-really-sure-if-it-will-workaround in PETTIL CREATE. Make a new user variable (ugh) called REDEFINE. During the time between CREATE and sealing the vault, REDEFINE is either off ($0000) for new words or it's a copy of the old definition's CFA. Smudge will also be set on the header during this time.
- FIND the address of a pre-existing head, or add a new one. Duplicates (same name) are never created.
- if it's new, turn REDEFINE off ($0000), otherwise set REDEFINE = the CFA of the found word (found at 2- from the length byte)
- set the smudge bit
- during execution token compilation, FIND will check REDEFINE if the smudge bit is on
- - REDEFINE @ if REDEFINE is nonzero
- - HERE if REDEFINE is zero
- turn off SMUDGE (and maybe clear REDEFINE) when closing the definition
It feels inelegant and un-Forth-like. How do Forth optimizers sleep at night?