• C-coding on a 65c02

    adam.klotblixt03/29/2024 at 05:22 0 comments

    During the wekend I managed to configure both cc65 and llvm-mos to emit code that runs on my own scheduler. Since doing a fair bit of C-code under Fuzix I was curious to see what assembler code the different compilers produced, and to benchmark them.

    I started by writing a 16-bit fixed-point mandelbrot that produced the famous shape in ascii on my desktop pc. Then I followed the official porting guides for cc65 and llvm-mos. I was pleasantly surprised at how easy it is to get started, both projects have very clear guides!

    When I finally got everything going, the visual "timing" said that llvm-mos is roughly twice as fast as cc65 for this particular program. The assembler code also looks more pleasing to me. The fact that cc65 lacks floating point is a big failure in my opinion. So, if I ever want to write C-code for the 65c02, llvm-mos will be the way to go.

  • Pico PIO programming

    adam.klotblixt03/03/2024 at 10:32 0 comments

    I've been stuck the last few days with trying to get PIO code running properly on the Pico. Lots of trouble getting it to work to read addr, read/write data and write control- and bank-registers. Mainly me getting the config wrong, setting the pindirections incorrectly.

    The main takeaway is that when the arm core wait-loops for data from the PIO, it takes more time than actually doing the work directly in the arm core the way I've done it so far. To gain performance, the PIO needs to do stuff in the background while the arm core does some heavy lifting.

    To get more performance to the arm cores, in the PZ1 case that means an external CPLD to do address decoding and memory access.

    Also, now I really know how the PIO works, which is great for the future.

  • Log in to Fuzix

    adam.klotblixt02/25/2024 at 07:57 0 comments

    Another milestone in the Fuzix saga: it's now possible to use 4 virtual displays on the connected LCD. Two serial ports can also be used to log in to the system.

    Image upload to the logs fails for me with "Unknown Error", so no pretty pictures :(
    The new main gallery image is my current setup, booting Fuzix.

  • Fuzix progressing

    adam.klotblixt02/18/2024 at 06:37 0 comments

    I've worked on getting Fuzix on the PZ1 in better shape.

    First I managed to implement a 24-bit LBA blockdevice, so the filesystem can actually see partitions now :D
    IOCTL is still missing for this blockdevice, not sure what functions rely on that...
    Swap requires a blockdevice, but that needs even more work that I have not understood yet.

    Second I implemented a second serial port interface in the EmulatorKit, so it is now possible to login from a second terminal and run 2 programs at once. Potentially very useful. Next up is fixing this in the physical Teensy PZ1 version.

    Really satisfying work.

    EDIT: No work needed to get the Teensy going, just move the correct Fuzix binaries to the correct place and start up! It is NOT fast at 2MHz...

  • Protected mode 65C02

    adam.klotblixt02/09/2024 at 09:28 0 comments

    I've spent quite some time exploring the possibilities of extending the 65C02 to deal with supervisor/user mode. Several HW designs and code variants have been tested to reach something that actually works the way I want.

    The PZ1 has a fixed (ROM) top page, those 256 bytes of code are key to getting everything working.
    The kernel scheduler is run via IRQ and a timer.

    User processes have to use osCalls to get stuff in/out of the system. This adds overhead, but surprisingly little. I think it is worth it.

    Some implementation details:
    - Whenever VPB fires (IRQ/BRK/RESET/NMI) the ioAccess bit 0 is set.
    - Op code read at $FE00 sets ioAccess bit 1. This is used as the osCall entry point.
    - Writing to PORT_IO_ACCESS can clear ioAccess, run as the last instruction before returning to a user process from an osCall.
    - When accessing the $FExx area, and ioAccess is not 0, the io ports are visible. Otherwise a banked memory operation takes place. Nice to have those extra 256 bytes for user processes :)
    - The kernel scheduler saves the ioAccess state for each user process. A process can be in the middle of an osCall, and needs to resume operation in the same state. This is the reason to have 2 bits in ioAccess. Very few osCalls so far are non-interruptible.
    - In order to deal with dangerous op-codes in user mode (SEI/WAI/STP) there is a watch dog timer (WDT). If the user code takes too long because of a WAI or STP, the WDT issues a reset. The WDT prevents any user process from hogging all the cpu cycles.
    - A user process can't access the memory of another process, since the memory banking system is done via io ports, managed by the kernel.

    Other enhancements I've explored:
    - RESET can now differentiate between cold boot, reset button, and WDT reset.
    - BRK can now be used to insert breakpoints for deugging.
    - NMI can now be used to enter a very rudimentary debugger.

    Irritatingly, the 65C02 use the same vector for BRK and IRQ, so extra code is needed to separate those tasks. This makes the IRQ-handler a bit slower than needed, such a shame.

    I've done the implementation for all this in the Pico/Teensy io processor, but have had a good look at how to do it in real HW as well. The HW needed for this is surprisingly simple: some address comparators and a few latches. Definitely possible to do with electronics available in the early 80's, just the way I like it

  • CPLDs & Fuzix

    adam.klotblixt01/18/2024 at 08:53 0 comments

    The CPLDs I had to test my code on are ATF1504 in TQFP44, with 32 gpio. Using 2 of these chips in DIP-converters I was able to try all the functionality I wanted to, but with fewer bits in the bank registers due to lack of pins.

    The next step would have to be using a TQFP100 chip, and that requires a proper PCB. We'll see when that happens.

    For now I have other SW things to try out, mainly towards getting Fuzix running better on PZ1.

  • Emulation, again

    adam.klotblixt01/18/2024 at 08:41 0 comments

    Going on vacation, I wanted to have something to do in the evenings and early mornings. I decided to bring a Raspberry Pi Pico with only an SD-card attached.
    The emulation library vrEmu6502 is really nice: nicely coded, can emulate 65C02, compact and quick. I now have a PZ1 version running on the Pico with 208KiB RAM, which makes it possible to run the 16KiB scheduler, 64KiB ehBasic and 2 full 64KiB tasks. I plan to try out some virtual memory magic later.

    Coding this version has made me realize some simplifications to the real HW implementations, which is nice.

  • Shadow memory no more

    adam.klotblixt01/18/2024 at 08:27 0 comments

    I thought it was faster to have the bank registers readable, but it turns out my scheduler code is somewhat faster by having copies of the bank vaules in regular RAM. The IO shadow memory writes/reads are now removed, simplifying the HW a bit.

  • CPLD bank register

    adam.klotblixt11/18/2023 at 15:02 0 comments

    I've FINALLY done a proper ATF1504 implementation and programmed it via JTAG. WinCupl is a beast to use and it took some time to connect all the needed HW and SW tools to be able to program the chip. Bigby has a great blog about the subject at https://www.hackup.net/2020/01/erasing-and-programming-the-atf1504-cpld/

    The implementation copies the functionality of the two 74HC670 I've used before: four 8-bit bank registers. Since the ATF1504 is much faster than 74HC-logic, the memory cycle could be shorter. This is not a final solution, all the other glue logic should also reside in a CPLD. The 44 pin chip I use does not have enough GPIO for this though, I'd need an 80- or 100-pin chip to get EVERYTHING inside. It is really nice to be able to test each function this way though.

    For some reason, I can't upload a pic of the setup, but that's life sometimes.

  • MMU findings

    adam.klotblixt10/26/2023 at 09:05 0 comments

    Protecting io-space from user processes can be implemented many ways, and I've tested 2 of them.

    1: Use BRK as a way to set a flip-flop that enables io access. The 6502 has a VPB pin that asserts when a vector is pulled, either BRK/IRQ, RESET or NMI. De-asserting the flip-flop is done via a specific port write. It is really easy to do the hardware for this solution, but great care has to be put into making sure there is an io-access de-assert before returning to user-space.

    2: Using SYNC and address decoding to ensure the io-accessing op-code is in kernel-space requires a bit more hardware to pull off, but ensures there are no forgotten io-access de-asserts. Requires fewer cpu cycles as well.

    Having implemented both of these, I tend to lean towards #2 as it is easier to get bullet proof. On the other hand, it would be possible to use #1 to CHOOSE io-protection or not depending on the kernel. Hard choices, but I am glad I now understand more about this issue and what the 6502 can be coaxed to do.