Jump to content


  • Posts

  • Joined

  • Last visited

  • Days Won


Posts posted by Stefan

  1. Version 0.1.0 published.

    New in this version is that BASLOAD is loaded as a normal BASIC program, that is with LOAD"BASLOAD",8, no need for ,8,1.

    You need to RUN the program to setup the environment. This copies BASLOAD to its final destination in RAM ($9000). It will also setup two "wedge" commands to make it a bit more convenient to program. Those wedge commands are !L to load a BASIC file and !E to start X16 Edit.

    New in this version is also that the program when starting X16 Edit first will look in the ROM banks for the editor. If not found there, it will try to load X16 Edit from the root folder of the SD card (as before).

    • Like 2
  2. @desertfish, glad you got it working, even if the "eaten" keys are a bit annoying.

    I have been thinking about supporting another control key than Ctrl.

    At least on MacOS, Alt works like the Commodore key. In Petscii upper case/graphics mode, pressing Alt+A results in a "top left corner" kind of char. Pressing Alt+Shift+A returns a heart. Is that the same on Windows/Linux? Alt seems not to be a good candidate.

    Maybe the Win key is not used for anything else by the X16 Kernal or the emulator, and could be used as an alternative control key.

  3. Sounds intriguing...

    After pressing Ctrl+A to activate auto indent, there should be a message in the status bar at the bottom of the screen saying "AUTO INDENT ON". And when disabling you should see "AUTO INDENT OFF" in the status bar. You may see the status bar message at the start of the video I attached to my last post.

    On Windows and Linux, some Ctrl+key sequences are used by the emulator. One example is Ctrl+R, that resets the emulator instead of its intended action, to open a file. Maybe Ctrl+A is masked in the same way on Windows and Linux.

    If that is the case, auto indent may alternatively be enabled by first pressing and releasing ESC. In the status bar you should see "ENTER COMMAND OR PRESS ESC TO ABORT". If you now press A, it should enable auto indent.

    However, that doesn't explain why you did not get auto indent when using the new load-file-with-options entry. Need to see your code to understand that (hopefully).

  4. On 12/13/2021 at 12:15 AM, desertfish said:

    @Stefan got the new launch-with-options working, however: how is the auto-indent supposed to work?  The editor never seems to auto indent for me....


    When a line break is inserted, it counts the number of leading spaces on the line. The same number of spaces is inserted at the beginning of next line.

    I think that is the way most editors works, even Visual Studio Code that I use a lot.

    • Like 1
  5. On 12/11/2021 at 3:15 PM, desertfish said:

    oh, and one other thing, the CTRL-K cuts a line and adds it to the clipboard. But eventually it will give an error clipboard full, which is problematic if I just wanted to delete a (large) number of lines. Can we get a kill line key or alternatively some way to clear the clipboard? ❤️


    Just need to come up with a reasonable keyboard shortcuts, as they are running out.

    It is not a great solution to start using Alt+key combinations (as Nano does), as those are already linked to graphical characters in PETSCII mode.

    One possibility would be to link Ctrl+K to the cut command, and Ctrl+Shift+K to a new delete line command.

    In a similar way Ctrl+C could be linked to the show cursor position command, and Ctrl+Shift+C could be used for a new command to continuously show the cursor position.

    Have to think about it though.

    EDIT: Always showing cursor position is now done and committed to master branch. I removed the Ctrl+C command altogether. Will continue with delete line command, probably another day.

  6. On 12/11/2021 at 1:31 PM, desertfish said:

    @Stefan a small suggestion , instead of requiring CTRL-C to show the line/column number, please show these continually in the top bar or perhaps in the bottom area (like the Vim editor does).

    Also, maybe, a way to set the screen and text colors beforehand by the calling application?


    Actually I've been working on a new entry point called "Load File With Options".

    You may use this entry point to load a text file on start up. If the file name length is 0, it will not try to load a file, so it can be used whether or not you want to load a file.

    It will let you set the following options before starting X16 Edit:

    • Auto indent on/off
    • Word wrap on/off
    • Tab width
    • Word wrap position
    • Current device #
    • Screen text color/background color
    • Header text color/background color
    • Status bar text color/background color

    That code is already in the master branch on Github, but I need to test it a bit more before publishing as a new version.

    I will see what I can do to display row/column all the time. That I haven't looked into.

    • Like 1
  7. Hi!

    I tried your new version. After some fiddling, not properly reading the instructions, and so on, I got it to work.

    I wrote a simliar test hello world program to yours, and it worked great. I could also restart the assembler without reloading it from disk after compiling, loading and testing the assembly program.

    Very nice job so far!

    • Like 1
  8. It's also nice to know how you define the segments used by the .SEGMENT statement.

    This is done in config files. There is also a manual on writing these config files: https://www.cc65.org/doc/ld65-5.html

    The default config file for X16 assembly programming is cx16-asm.cfg. As may be seen, the default segment for program code ("CODE") has a load address of "MAIN", which in the MEMORY section is defined to start at address %S, which is a shorthand for the start address defined in the FEATURE section at the beginning of the file. At first you may think it's just too complicated fiddling with all these settings and config files, but it really makes programs more manageable, and it's especially easy to change the memory layout of a program if need be.

    ;THIS IS AN EXTRACT FROM cx16-asm.cfg, some non-memory management settings excluded
    STARTADDRESS: default = $0801;
    __LOADADDR__: type = import;
    __HIMEM__: type = weak, value = $9F00;
    MEMORY {
    ZP: file = "", start = $0022, size = $0080 - $0022, define = yes;
    LOADADDR: file = %O, start = %S - 2, size = $0002;
    MAIN: file = %O, start = %S, size = __HIMEM__ - %S;
    ZEROPAGE: load = ZP, type = zp;
    EXTZP: load = ZP, type = zp, optional = yes;
    LOADADDR: load = LOADADDR, type = ro;
    EXEHDR: load = MAIN, type = ro, optional = yes;
    LOWCODE: load = MAIN, type = ro, optional = yes;
    CODE: load = MAIN, type = ro;
    RODATA: load = MAIN, type = ro;
    DATA: load = MAIN, type = rw;
    BSS: load = MAIN, type = bss, define = yes;
    • Thanks 1
  9. A couple of years ago I did this:


    A custom 60 % keyboard. 3D printed plate. Cherry MX Blue switches. Diodes on every switch to prevent ghosting. Matrix hand wired and soldered directly to the switch pins, no PCB. Teensy LC as controller with a simple custom written firmware.

    I use it at work. Still works like a charm. My favorite keyboard, but I guess I might be biased 🙂

    The host wakeup problem I wrote about in the linked blog post is only an issue on my iMac. It works normally on a sleeping Windows PC.

    • Like 4
  10. Bringing clarity to code by lots of comments is not the Clean Code way of doing things, apparently. Comments have the disadvantage of being ignored by the assembler or compiler. Keeping them up to date is a manual process. And all manual processes will fail 🙂

    Good labels are a better option.

    The advantage of extracting small functions into macros is that you get better abstraction in higher level functions. In my example above, the function "clear_screen" contains mostly macro calls. It's possible to understand what they will do without looking at the macro. You get the big picture very quickly. And if you are interested in the details, you may look at the macro definition.

    That said, I've never tried to program anything in this fashion. It would be interesting to do that.

    • Like 2
  11. 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
    .macro clear_line
       ldx #80
       lda #32
    :  sta VERA_D0
       bne :-
    .macro goto_nextline
       stz VERA_L
       inc VERA_M
    .macro is_finished
       lda VERA_M
       cmp #60
    .proc clear_screen
       bne loop
    VERA_L = $9f20
    VERA_M = $9f21
    VERA_H = $9f22
    VERA_D0 = $9f23


    • Like 3
  12. 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
  13. 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.

    • Like 2

    2 minutes ago, Ender said:

    I would think that a license like that, (that is, one that makes you credit the author but not copy the license) kind of defeats the purpose, wouldn't it?  Because let's say someone derives from your work and credits you but doesn't copy your license.  Then someone else can derive from their work, but since there was no license, they don't credit the author.  Then it can spread from there, with no credit to the author being given.  I think the copying of the license kind of needs to happen, for it to have any effectiveness.

    Seems logical to me. The 2-clause BSD license couldn't be much simpler (or shorter).

  15. Returning to the original questions by @AuntiePixel, there are at least two solutions in the "downloads/dev tools" area.

    One is @Scott Robison's BASIC PREPROCESSOR. It takes BASIC source code stored in a plain text source file with some additional markups for labels and long variable names, and outputs a runnable tokenized BASIC program. You don't use line numbers in the source file. One cool thing is that the preprocessor apparently is written in its own BASIC file format.

    The other is my BASLOAD program. It loads BASIC source code files stored as plain text into RAM. While loading a file it's tokenized so that it can be run be the built-in interpreter. It's made to work in parallel to X16 Edit. As the BASIC PREPROCESSOR, it doesn't use line numbers.

    Both solutions let you write the BASIC source files in any editor of your choosing. That includes writing source files on modern PCs and transferring them to the SD card.

    • Like 1
    • Thanks 1
  16. Hi,

    I found this nice article written by @Greg King on how to use the cc65 package for X16 programming:


    Section 4.2 contains information on the command line params you should use and the default config file for assembly programming. I think I used this information myself to get started with the ca65 assembler, as it's not obvious how to do this.

    As to the use of .ORG you could read section 17 in the ca65 users manual on porting assembly source code written for other assemblers:


    In short, you may set the start address of the program with the cl65 command line param --start-addr or by writing your own config file replacing the default cx16-asm.cfg.

  17. .org and .segment statements are not required if you compile with the config file cx16-asm.cfg.

    If using the command line params I mentioned, the compiler defaults to the CODE segment, and you therefore need not explicitly tell the compiler that.

    The .org will, surprisingly, not affect the load address of the executable. It sets the program counter, normally only used when you compile code that is meant to be moved to its final destination after the program is loaded. As the manual says, you normally need not use the .org statement (https://www.cc65.org/doc/ca65-11.html#ss11.72)

    In most cases you want to create a program that can be run with the BASIC RUN command. To do this, use the  -u __EXEHDR__ compiler param, and all will be done automatically for you.

    As proof of this, the source code, and the command line params given to cl65 in my previous post works fine.

    If you want to manually control where code ends up in memory when you load the executable, you must learn how to use and write CA65 config files.

    You can test writing a small program using the .org statement, for instance this:

    .segment "STARTUP"
    .segment "INIT"
    .segment "ONCE"
    .segment "BASS"
    .segment "CODE"
    .org $0900
    lda #65
    jsr $ffd2

    Compile it with "cl65 -t cx16 -o TEST.PRG test.asm". Move the program to the SD card image. Load the program it in the emulator with LOAD"TEST.PRG",8,1. The program still loads to $0801.

    This is because the CA65 has a linker that decides where the code ends up. And to give the linker commands, you need to write config files.

  18. Hi,

    Some pointers:

    • Lose the .org and .segment statements, they are not needed
    • The message string cannot be at the beginning of the source code if you intend to use $080d as entry point. The computer doesn't know if the bytes there are a string or code, it will try to run it as code, and the program likely crashes. Move the string to the end of the source.
    • To compile I use the following command: cl65 -o HELLOWORLD.PRG -u __EXEHDR__ -t cx16 -C cx16-asm.cfg helloworld.asm
    • The compiler translates ASCII to PETSCII correctly, but note that an unshifted char in your modern PC will be an uppercase char on the X16, presuming you are using the default uppercase/graphics mode that the X16 is started in.

    The modified source code that I tested (lines that could be removed commented out):

    ;.org $080D
    ;.segment "STARTUP"
    ;.segment "INIT"
    ;.segment "ONCE"
    .segment "CODE"
    CHROUT = $FFD2
    ;CHRIN = $FFCF
    ZP_PTR_0 = $20
    lda #<message
    sta ZP_PTR_0
    lda #>message
    sta ZP_PTR_0 + 1
    ldy #0
    lda (ZP_PTR_0),y
    beq stop
    bra @loop
    .byte "hello",0
  • Create New...

Important Information

Please review our Terms of Use