Jump to content

New productivity upload: X16 Edit - a text editor


Stefan
 Share

Recommended Posts

X16 Edit - a text editor

View File

X16 Edit is a text editor for the Commander X16 platform.

Design goals:

  • Use plain text files
  • Store text buffer in banked RAM (512KB to 2 MB) 
  • Handle large texts efficiently
  • Simple modeless user interface inspired by GNU Nano
  • Implement basic editing functions well - refrain from making the program too feature-rich
  • Support both ISO and PETSCII modes

Tested with emulator version r38. Run with the following command:

  • x16emu -sdcard sdcard.img -prg X16EDIT-x.x.x.PRG -run

where x.x.x is the program version.

You can also run the program with the "Try it now" button. There is, however, no attached disk in the online emulator, and consequently you cannot save or open files. Also, some of the Ctrl+key sequences are not working in the online emulator. To fully test the program you still need to download and run it locally.

Please read the attached file romnotes.pdf if you want to try the ROM version.

Source files available at https://github.com/stefan-b-jakobsson/x16-edit

Released under GNU General Public License v 3 or later.

 

 

 

logo.png

 

manual.pdf

romnotes.pdf


 

  • Like 4
  • Thanks 1
Link to comment
Share on other sites

  • 7 months later...

This is pretty awesome. One quick bug report: ^V does not do pageDn as documented in the online help - F8 works though.

Is there a plan to support the Home/End  and PgDn/PgUp keys? I use those like CRAZY whenever I'm coding (I imagine most ppl do).

I think having a real editor like this, an assembler, and a few basic tools such as sprite / sfx  editors would be hella-useful to have in the ROM of the system to encourage on-system development.

 

  • Like 1
Link to comment
Share on other sites

2 hours ago, ZeroByte said:

This is pretty awesome. One quick bug report: ^V does not do pageDn as documented in the online help - F8 works though.

Is there a plan to support the Home/End  and PgDn/PgUp keys? I use those like CRAZY whenever I'm coding (I imagine most ppl do).

I think having a real editor like this, an assembler, and a few basic tools such as sprite / sfx  editors would be hella-useful to have in the ROM of the system to encourage on-system development.

 

Thanks.

Ctrl+V works in the emulator for me. I'm using MacOS. I know that the emulator behaves a bit differently on Windows and Linux. We would need to know if it works on the real hardware before changing it.

END is supported for now by Shift+Home. This is because the Kernal ignores the END key.

It seems R39 will include support for custom PS/2 scan code handlers, that let you catch a scan code before it's processed by the Kernal. This could be used to enable support for the real END, PageUp and PageDown keys even though the Kernal ignores those keys.

  • Like 1
Link to comment
Share on other sites

Hello,

I am running the r38 x16 emulator on Debian Linux, and for both the stand alone program, and the rom embedded version, when I try to load a file using ctrl+R the application crashes and x16 reboots.  I only mention this

because when using cc64, and passing a file name to the editor it does open without a problem.

The x16edit version is the v0.3.5.

Have a great day!

UPDATE:

Never mind I am stupid.. F5 works Just fine.  I forgot ctrl+R is mapped to x16 reset.

Will ^R get disabled so it does not happen when editing?

Edited by evlthecat
Link to comment
Share on other sites

Hi @evlthecat.

You cannot change the behavior of Ctrl+R in the Windows and Linux emulators from within a program that runs in the emulator. You would need to change the emulator itself.

I hope that the real hardware will not have this problem.

Finding other suitable shortcuts not interfering with any existing Ctrl+key sequence in Windows or Linux is a bit hard. I'm already using most available Ctrl+key combinations! And we would move away farther from GNU Nano.

I don't know if it's a good solution, but it would be possible to support several modifier keys in parallel. That would make it possible to use, for instance, the Win key as an alternative to Ctrl.

Which of the other modifier keys (Alt/Win/AltGr) would have least interference with Windows/Linux and the Kernal? I'm not sure. That requires some research.

  • Thanks 1
Link to comment
Share on other sites

10 hours ago, Stefan said:

Hi @evlthecat.

You cannot change the behavior of Ctrl+R in the Windows and Linux emulators from within a program that runs in the emulator. You would need to change the emulator itself.

I hope that the real hardware will not have this problem.

Finding other suitable shortcuts not interfering with any existing Ctrl+key sequence in Windows or Linux is a bit hard. I'm already using most available Ctrl+key combinations! And we would move away farther from GNU Nano.

I don't know if it's a good solution, but it would be possible to support several modifier keys in parallel. That would make it possible to use, for instance, the Win key as an alternative to Ctrl.

Which of the other modifier keys (Alt/Win/AltGr) would have least interference with Windows/Linux and the Kernal? I'm not sure. That requires some research.

Another alternative is to support an unused and available meta key as a "sticky control", so the sequence <meta><char> has the effect of <control>+<char>. That is also handy if porting the editor later to headless operation where getting the meta key over the serial port is trickier.

Edited by BruceMcF
Link to comment
Share on other sites

Doesn’t the system API give the ability to intercept these various hot keys and consume them in the application instead?

In the case of ^X obviously this isn’t necessary for the emulator, but it would be nice if it could do this for the printscreen/ pause-Brk  keys as those have X16 functionality planned if the custom key caps are any indication... Right now, both Linux and Windows use PrintScreen as a screenshot hot key, but the X16 seems geared to use that as 40/80 COL mode switch... I’d like it if the emu could override the screenshot function and consume the keystroke so the X16 can use that key as it appears on the X16 kbd.

  • Like 1
Link to comment
Share on other sites

8 hours ago, BruceMcF said:

Another alternative is to support an unused and available meta key as a "sticky control", so the sequence <meta><char> has the effect of <control>+<char>. That is also handy if porting the editor later to headless operation where getting the meta key over the serial port is trickier.

X16 Edit supports that kind of function with the ESC key. You may press and release ESC and then any of the command keys.

  • Thanks 1
Link to comment
Share on other sites

Some notes on my progress supporting END, PagUp and PagDn.

From the emulator source code, it's apparent that those keys are discarded. See file keyboard.c, function ps2_scancode_from_SDL_Scancode.

I made the following minimal change to that function, and recompiled the emulator:

case SDL_SCANCODE_END:
return 0x69 | EXTENDED_FLAG;

That emulates PS/2 scan code E069 when the END key is pressed.

The good news is that I could successfully detect the END key in X16 Edit.

Most probably, this will work on the real hardware out of the box.

 

  • Like 3
Link to comment
Share on other sites

I've now made a pull request to include support in the emulator for Delete, Insert, End, PgUp and PgDn keys.

I've also updated X16 Edit to support those keys. This code works only if run in the current master branch of the emulator and Kernal, i.e. what will become R39.

  • Like 1
Link to comment
Share on other sites

  • 2 weeks later...

I use GNU Nano as my main editor in Linux and I noticed a disadvantage of Ctrl+U (Uncut) in X16 Edit: Ctrl+U can only paste once in X16 Edit, but several times in Nano. I hope this will change because I often use Ctrl+U several times in Nano and would like to do so in X16 Edit. I believe there is no advantage of emptying the cut-buffer after the first uncut/paste.

  • Like 1
Link to comment
Share on other sites

1 hour ago, mobluse said:

I use GNU Nano as my main editor in Linux and I noticed a disadvantage of Ctrl+U (Uncut) in X16 Edit: Ctrl+U can only paste once in X16 Edit, but several times in Nano. I hope this will change because I often use Ctrl+U several times in Nano and would like to do so in X16 Edit. I believe there is no advantage of emptying the cut-buffer after the first uncut/paste.

I see what you mean.

GNU Nano empties the cut-buffer if you uncut and then cut/copy. But as you say, it's possible to do repeated uncuts.

I will look at this issue in the upcoming release for R39. Thank you for bringing this to my attention.

  • Like 1
Link to comment
Share on other sites

11 hours ago, mobluse said:

I use GNU Nano as my main editor in Linux and I noticed a disadvantage of Ctrl+U (Uncut) in X16 Edit: Ctrl+U can only paste once in X16 Edit, but several times in Nano. I hope this will change because I often use Ctrl+U several times in Nano and would like to do so in X16 Edit. I believe there is no advantage of emptying the cut-buffer after the first uncut/paste.

This issue is now fixed and pushed to the Github repository, branch "prepare-for-R39". This development branch of X16 Edit works with the current master branch of the emulator and Kernal, what will become R39.

  • Like 3
Link to comment
Share on other sites

  • 4 months later...

I've uploaded a new version of X16 Edit (0.4.0) incorporating some improvements of the program I've been working on during the summer and this autumn. It's nothing major, mostly fixing small things in the user interface not working perfectly 

  • Like 4
Link to comment
Share on other sites

I found a bug (sort of) in the routine that writes the text buffer to disk. It wasn't critical, but at least annoying. Maybe my findings are of some general interest, so here is a short description.

X16 Edit uses the standard Kernal routines to write to disk, i.e.

  • SETNAM + SETLFS + OPEN to open a file
  • CHKOUT to set the file as standard output
  • CHROUT to write actual data to the file

The information on CHROUT error handling is a bit hard to grasp, at least for me. The entries in the compilation of books and manuals here https://www.pagetable.com/c64ref/kernal/ have somewhat divergent information on this topic.

This is what I believe to be true after calling CHROUT:

  • Carry bit set indicates that a Kernal I/O error has occurred, in this context most likely 5=Device not present. The error number is in the A register.
  • After each call to CHROUT you need to call READST to know if a disk side error occurred, such as a file exists error. An error occurred if READST is not 0.
  • To get the actual disk side error, you need to open a file to the command channel (like OPEN 15,8,15 in BASIC) and read the message

X16 Edit did previously write the whole buffer to disk without checking READST. Only then it would check the command channel status. It worked anyway, because the disk seems to ignore data sent after an error condition has occurred (such as file exists). But is wasn't beautiful.

I have also been working on a progress indicator that is shown when loading or saving files. This is useful when handling large files, so that you know the computer has not just locked up.

These changes are committed to the GitHub master branch.

I think my next task is a new code cleaning project focused on minimizing code size. The ROM version of X16 Edit is now only about 75 bytes from filling 16 kB, the size of one ROM bank. It would be nice to have some more leeway than that for future additions.

Edited by Stefan
  • Like 2
Link to comment
Share on other sites

4 hours ago, Stefan said:

I found a bug (sort of) in the routine that writes the text buffer to disk. It wasn't critical, but at least annoying. Maybe my findings are of some general interest, so here is a short description.

X16 Edit uses the standard Kernal routines to write to disk, i.e.

  • SETNAM + SETLFS + OPEN to open a file
  • CHKOUT to set the file as standard output
  • CHROUT to write actual data to the file

The information on CHROUT error handling is a bit hard to grasp, at least for me. The entries in the compilation of books and manuals here https://www.pagetable.com/c64ref/kernal/ have somewhat divergent information on this topic.

This is what I believe to be true after calling CHROUT:

  • Carry bit set indicates that a Kernal I/O error has occurred, in this context most likely 5=Device not present. The error number is in the A register.
  • After each call to CHROUT you need to call READST to know if a disk side error occurred, such as a file exists error. An error occurred if READST is not 0.
  • To get the actual disk side error, you need to open a file to the command channel (like OPEN 15,8,15 in BASIC) and read the message

X16 Edit did previously write the whole buffer to disk without checking READST. Only then it would check the command channel status. It worked anyway, because the disk seems to ignore data sent after an error condition has occurred (such as file exists). But is wasn't beautiful.

...

IIRC, calling READST after writing a whole string was a common{*} practice, so it would seem likely that the Commodore devices handled multiple CHROUT calls with the disk side in an error status exactly as you noted, by disregarding the additional characters and continuing to return the same error status.

{* Indeed, if "PRINT#8 NA$" in Basic worked that way, it could even be said to be a standard practice.}

Edited by BruceMcF
  • Like 1
Link to comment
Share on other sites

@BruceMcF, yes I guess you could gain some performance by not checking READST every time you call CHROUT if you can trust that no harm comes from writing to the "disk" after an error condition. In X16 Edit I could try checking READST every time the program needs to change the memory page it's reading from (i.e. about every 250 bytes).

Link to comment
Share on other sites

On 9/26/2021 at 3:39 AM, Stefan said:

@BruceMcF, yes I guess you could gain some performance by not checking READST every time you call CHROUT if you can trust that no harm comes from writing to the "disk" after an error condition. In X16 Edit I could try checking READST every time the program needs to change the memory page it's reading from (i.e. about every 250 bytes).

Yes, a binary page of 256 bytes is a good middle point between every character and the whole buffer ... especially given that the size of buffers possible in the X16 is much larger than was possible in the Vic20/C64.

  • Like 1
Link to comment
Share on other sites

I've done some code cleaning the last couple of days. Very rewarding when the code becomes more compact, more readable, and more stringent. So far I've reduced the binary by almost 200 bytes.

Some time ago, I watched a few lectures on Clean Code by "Uncle Bob". Very inspiring, even though not all paradigms may be used in assembly, if you want things to fly fast.

A nice weekend to all of you!

 

  • Like 4
Link to comment
Share on other sites

On 9/30/2021 at 11:26 PM, Stefan said:

I've done some code cleaning the last couple of days. Very rewarding when the code becomes more compact, more readable, and more stringent. So far I've reduced the binary by almost 200 bytes.

Some time ago, I watched a few lectures on Clean Code by "Uncle Bob". Very inspiring, even though not all paradigms may be used in assembly, if you want things to fly fast.

Guys like Uncle Bob and Kevlin Henney are good to watch if you want your code to be clear and maintainable.

One thing that Bob Martin said in another video which stuck with me is (paraphrasing) "paradigms are just a set of arbitrary restrictions we put on programmers".

  • Like 1
Link to comment
Share on other sites

Continuing on the topic of clean code, even though its not X16 Edit specific, I have some thoughts on how to apply to assembly programming.

In the lesson linked above, Uncle Bob is talking a lot about the design of functions:

  • Names of functions (and variables) should be carefully chosen, so that reading code is like reading (good?) prose
  • Function names should contain verbs, as they are doing something
  • A function should do only one thing; the process of cleaning code entails breaking a function into the smaller and smaller parts, and you know you're done when no more functions can reasonably be extracted
  • A function should be short (most often 5 lines)

In a modern cross assembler, such as ca65, there's nothing stopping you from naming things properly.

But what about functions doing only one thing, and functions being short like 5 lines?

Any high level language examines functions at compile time, and decides wether the resulting machine code is inlined or made into an actual machine language subroutine. Even if you are writing a lot of really small functions in a high level language, the binary code will probably be efficient.

In 6502 assembly, if you do a lot of subroutine calls with JSR+RTS, they will all stay in the final binary code making it inefficient.

I have never seen 6502 assembly code trying to be clean code in the way Uncle Bob describes. Would it even be possible without loosing performance?

I think it might be, if you use extensive use of macros for code that you want to break out into a separate "function" where the resulting machine code is inlined.

A simple example. Is this a good idea?

.macro goto_topleft
   stz VERA_L
   stz VERA_M
   lda #(2<<4)
   sta VERA_H
.endmacro
 
.macro clear_line
   ldx #80
   lda #32
:  sta VERA_D0
   dex
   bne :-
.endmacro
 
.macro goto_nextline
   stz VERA_L
   inc VERA_M
.endmacro
 
.macro is_finished
   lda VERA_M
   cmp #60
.endmacro
 
.proc clear_screen
   goto_topleft
loop:
   clear_line
   goto_nextline
   is_finished
   bne loop
   rts
.endproc
 
VERA_L = $9f20
VERA_M = $9f21
VERA_H = $9f22
VERA_D0 = $9f23

 

  • Like 3
Link to comment
Share on other sites

Well, I personally don't like (yet) to extract things that are used only once. It feels messy to me. But the example above makes the code very easy to understand. It took me only a few seconds to understand what you are doing.

 

I wonder what's better: commenting code (e.g. giving headlines to sections of code) or writing macros with explanatory names

Edit: or even use descriptive names for labels

Edited by kliepatsch
  • Like 2
Link to comment
Share on other sites

I do like using asm macros to abstract away clutter. Something simple like:

LDA #whatever
STA wherever

I think looks much more readable personally behind a STI macro, which I've used recently with Acme on some code that already existed for that assembler:

!macro STI .addr, .value {
        LDA #.value
        STA .addr
}

It's such a simple thing, but even as tiny as it is, it so much better represents the intent. Now, the name in this case isn't necessarily ideal unless you're trying to keep your asm continuing to look like asm. And for me, if I can take a bunch of these sorts of 2 or 3 or more line "routines", I'm able to have twice as much code on screen in context, which makes comprehension easier (for me).

!macro ADD .dst, .src {
        CLC
        LDA .dst
        ADC .src
        STA .dst
}

I like that when it fits the pattern much more than the equivalent four lines. And I can have ADDI, or ADDW, or ADDWI. I think those are good naming conventions for commonly used snippets that can themselves be used to create larger even better named routines.

I like some things about acme. In some ways it is a lot simpler. It's macro facilities aren't as good IMO as ca65, for example, but they are much better than nothing.

 

  • Like 1
Link to comment
Share on other sites

On 10/3/2021 at 12:38 AM, kliepatsch said:

Well, I personally don't like (yet) to extract things that are used only once. It feels messy to me. But the example above makes the code very easy to understand. It took me only a few seconds to understand what you are doing.

 

I wonder what's better: commenting code (e.g. giving headlines to sections of code) or writing macros with explanatory names

I think the key to programming / software engineering is making that judgement call on a case by case basis. Sometimes one will be preferable to the other.

One thing I haven't done yet, but really need to do, is create alternative mnemonics macros for the various branch instructions.

CMP #value
BEQ label

Is pretty clear. I think it becomes far less clear when it is something like:

AND #mask
BEQ label

To me, reading that, my brain wants to go to the place where "if the relevant bits in A are equal to the mask then branch". But it really is saying the opposite. In a case like this a simple alternative like BZ/BNZ as alternatives to BEQ/BNE makes a world of difference to the way I read the code.

Also, my OCD flares up whenever I see BPL. BMI isn't bad, I can read that as "branch negative". BPL though intuitively (even though I know better) means "branch positive" and zero isn't positive in my mind. I mean, I know how to use it, but the official mnemonics selected for 6502 leave me wanting at times.

  • Like 1
Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

 Share

×
×
  • Create New...

Important Information

Please review our Terms of Use