Jump to content


  • Posts

  • Joined

  • Last visited

  • Days Won


Stefan last won the day on October 3

Stefan had the most liked content!

1 Follower

About Stefan

  • Birthday 01/03/1973

Recent Profile Visitors

The recent visitors block is disabled and is not being shown to other users.

Stefan's Achievements


Rookie (2/14)

  • Dedicated Rare
  • Very Popular Rare
  • First Post Rare
  • Collaborator Rare
  • Reacting Well Rare

Recent Badges



  1. Some time ago, I made a pull request for a new BASIC command "FSYS" to invoke code in other ROM banks. That could be used for a lot of things, including to call the clear line routine @JimmyDansbo found. However, as there has been no recent development, at least not in public, of the Kernal, I don't know if there is interest in the solution.
  2. 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 FEATURES { STARTADDRESS: default = $0801; } SYMBOLS { __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; } SEGMENTS { 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; }
  3. A couple of years ago I did this: https://forum.pjrc.com/threads/55395-Keyboard-simple-firmware 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.
  4. 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.
  5. 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
  6. 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!
  7. @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).
  8. 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.
  9. 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
  10. Seems logical to me. The 2-clause BSD license couldn't be much simpler (or shorter).
  11. 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.
  12. Hi, I found this nice article written by @Greg King on how to use the cc65 package for X16 programming: https://cc65.github.io/doc/cx16.html 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: https://cc65.github.io/doc/ca65.html#toc17 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.
  13. .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 rts 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.
  14. 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 start: lda #<message sta ZP_PTR_0 lda #>message sta ZP_PTR_0 + 1 ldy #0 @loop: lda (ZP_PTR_0),y beq stop JSR CHROUT iny bra @loop stop: rts message: .byte "hello",0
  15. I made a separate thread about the SD card issues, should anyone want to continue this discussion.
  • Create New...

Important Information

Please review our Terms of Use