Text editor
Posted: Wed Sep 02, 2020 11:52 pm
16 hours ago, Stefan said:
That's an interesting subject.
The editor might not work as you think. It is essentially a model-view design. ...
When designing this, I calculated the memory usage. 16 bits are needed to represent the bank of the previous and the next memory page. ...
Aha, this is "prior window" and "next window" into the text buffer, with each window containing its own linking and text length information. Yes, the only relatively full featured editor I've ever written was a line buffer editor, so I fell into habitual assumptions.
So to do an insert, you can look if the next window has enough space to store the text after the insert. If it does, slide its text up, copy the text in the next window into the space, and continue with the insert. If it doesn't, grab a new "empty" window and link it in, and copy the text after the insert point into the new window.
You can go through and do small local memory collect at regular intervals, like when moving multiple windows up the chain, go back to the starting point and see how many windows is required to hold the first four buffers, and if its less than four, go through and compact them, releasing buffers from the "used" linked list and putting them in the "empty" linked list.
Anytime on the 6502 that you go beyond a page, both complexity and size of code expands. With a page sized window, keep the address of the window we are at, CURRENT, in zero page and everything can be done with (zp),Y operations. For windows into a buffer stuffed with text, with each window containing two way linked list pointers and number of text characters, page size windows are ideal, the largest size buffer that allows all copies to be simple loops like:
LDA SBNK: CMP DBNK: BNE +
- LDA (SRC),Y : STA (DEST),Y, : DEY : BNE -
-- LDA SBNK :+ STA BANK : LDA (SRC),Y : LDX DBNK : STX BANK : STA (DEST),Y : DEY : BNE --
One idea that reduces overhead by two bytes per page is have 64K buffers, in 8 consecutive High RAM segments. Then you keep a byte somewhere that has the bank of the first segment, you can use the high three bits of the LINK address to be the offset from the base. Then the link to page / bank converter, passing logical address in A and returning bank in A and page in X, is:
LNK2PG: PHA : AND #%00011111 : ORA #$A0 : TAX : PLA : LSR : LSR : LSR : LSR : CLC : ADC BUFF0 : RTS
There is an advantage to using an offset from a base bank even if you DON'T limit yourself to 64K files, because then you can have multiple BUFFERS, and the same operations can be directed to distinct buffers by just putting a different byte into BUFF0. But if you don't compact it, then "Link To Page" with the logical bank in A and logical page in X is just:
LNK2PG: PHA : TXA : AND #$1F : ORA #$A0 : ORA #$A0 TAX : PLA CLC : ADC BUFF0 : RTS
Even if you don't have an allocated use for the three redundant high bits in the logical page address, there's no harm in building in the ability to use them for some purpose later. For example, you might use them to indicate what KIND of text ... PETSCII, ASCII Latin-1, UTF-8 encoding of Unicode, etc. Having that encoded in logical address of each window makes it less likely that type of text information will be lost, and it makes sense to keep different types of text in different pages, even if there is free space available to merge them.
_____________________________________________________________
Another way to reduce to three bytes overhead per page-window, while retaining the two byte logical address, is with an XOR double-linked list.
In an XOR linked list, the "LINK ADDRESS" is the XOR of the LOGICAL ADDRESS OF "NEXT" and "PRIOR". I am going to assume for simplicity that the logical address is just the high byte of the page address with an assumed low byte of 0. If the above is desired, it still can be, with JSR LNK2PG at strategic intervals, but I'll set that aside.
We store the ACTUAL ADDRESSES of PRIOR, CURRENT, and NEXT in memory in zero page vectors, since this lets us do "LDA (PRIOR),Y : STA ( CURRENT),Y" type operations with less setup. We also have PRBANK, CRBANK, NXBANK.
To move UP the chain, we copy CURRENT to PRIOR and NEXT to CURRENT. And then for the physical address and the bank number of the new NEXT, we XOR the LINK in CURRENT with the ADDRESS stored in zero page. Note that you only access data in the CURRENT window, so whether the prior or next link is in the current bank doesn't matter.
NEXTWIN: LDA CRBANK : STA PRBANK : LDA NXBANK : STA CRBANK : STA BANK
L1: LDA PRBANK : EOR (CURRENT) : STA NXBANK
L2: LDA CURRENT+1 : STA PRIOR+1 : LDA NEXT+1 : STA CURRENT+1 : STZ PRIOR : STZ CURRENT
L3: LDY #1 : LDA PRIOR+1 : EOR (CURRENT),Y : STA NEXT+1 : STZ NEXT : RTS
To move down the chain, we do the equivalent, swapping the role of PRIOR and NEXT.
But to do local access to the current window, we don't need to worry about this. And to do local operations TO the next and prior windows, without moving our current spot in the chain, you still don't
_____________________________________________________________
One reason I was writing a line oriented editor is that a line oriented system is very convenient for FORTH. The base address and number of characters can be handed directly to the interpreter, without any need to copy the lines.
The idea this your approach inspires in me is to do something similar with 80 character line buffers, with the lower 5 bits of the logical page address being the page offset into the $A000 High RAM window, and the high three bits of the page address can be two bits to say which buffer INSIDE the page, 1, 2 or 3, and another bit is free for something else ... perhaps a dirty bit that is set when the line has been modified since the last save.
BANK is 1 byte, the logical Window address is 1byte, with XOR linking only the two of them are needed, and the buffer plus the number of characters in the buffer is 81 bytes, so 83 bytes per window. AND ... 256/3=83.33333. For saving the file, the bank might by converted into the sequence of the bank that has been saved so far, so when loaded the banks are all relative to the start of the buffer, and it is easy to walk through the banks and convert them to their actual bank locations.