Jump to content


  • Content Count

  • Joined

  • Last visited

Community Reputation

6 Neutral
  1. @desertfishThank you for the suggested way of going back to the Kernal. I have added that as the fallback way of doing explicit exit(), which is useful if the program has messed things up too much and want to exit back to a clean machine.
  2. I changed the C startup code to simply use the existing stack as-is. This solves the problem and I now get back to the blinking prompt when main() returns. I also took the opportunity to optimize it to the metal, more or less. I still have a proper C startup for the X16, but I changed the C code to be more like how I would write a Hello World in assembly, using a loop to send it to a character out routine, no printf() or stream related functions dragged in from the C library, no 0 exit code return either. The result is a C program that when compiled and linked results in a .PRG file that is 88 bytes.
  3. To be honest, an 8-bit like the 6502 is not at all about compact code, perhaps not even elegant. You will get much nicer instruction set if you move to the MSP430 or 68000. Even something like the RISC-V may be considered elegant compared to the 6502. An interesting approach would be to join something like Vera with a nice 16 or 32-bit based instruction set CPU. However, you would have to start from scratch with the "operating system" and the group of people interested in a 6502 computer would have to learn a new ISA, and somehow I think you would lose most of the audience along the road. I am not saying it is a bad idea. If the X16 is successful, it may make sense to produce a follow up going to a more modern ISA. It could have the same chipset (more or less). If people avoid 6502 assembly as an implementation language (there are BASIC, C, Forth etc), some software may be possible to migrate with reasonable effort.
  4. The problem is that I take over and set my own stack. I also put some stuff in zero page, but that should be in permitted and safe area (so far). What I am looking for is some soft-reset entry point back to BASIC that leaves the display and program. It should perhaps clear variables and sets up the stack again. Perhaps I should not touch the CPU stack at all?
  5. I just got my first output from my (own) C compiler and it was also among the first times I ever played with the X16 emulator, so I want to share my (minor) success. How should I exit from the program back to the system? Sorry if this is really a stupid question, but it was a long time ago since I programmed a Commodore 8-bit machine.
  6. Moving the bank registers to zero page only makes sense if it is also possible to read them. It is useful to allow calls between banks using a trampoline (bank switcher) in non-banked memory. Such trampoline needs to push the bank number together with the return address so that it later can return via another trampoline to the caller (switching the bank back). If reading is not possible, a shadow bank number could be written to RAM, but that would mean writing the bank twice which defeats the gain of having it in zero page.
  7. If seeking is not available then ELF is out. I do not think is practical to read several program header structures, not knowing how many they are. I still think it is a good idea to be able to load a file into memory and have banks populated automatically. Can someone explain how GEOS helps to load a banked program at power on when I get the READY prompt?
  8. I think a load format for banked data is very useful and it should be in the ROM. That way we can just load a file and have it placed in memory without having to re-invent the wheel by providing custom loaders. Does it prevent anyone from rolling their own? Not at all, you are free to load a plain binary and load data and program chunks from the file system as you see fit. I on my side prefer to have a binary file created by a tool and not need to bother how it gets loaded into memory, even when I need to use the bank system. I like the ELF format myself as it rather simple to use when the requirements are simple, yet it provides a lot of flexibility and can be quite expressive when needed. Most of the complexity comes when adding debug sections or linkable sections, but that is not what we are doing here. I will describe it a bit below to give an idea what it is and show that it actually have a lot of similarities to the proposed format. An ELF file has an ELF header that starts with a magic number (actually the string ELF) that identifies it as ELF. You only need to sanity check that it is 32-bit ELF, inspect the processor type and values related to program headers that describe where the program headers are in the file, their size and how many they are. Each program header describes a chunk of memory to be loaded. The headers appear after each other. A header can describe an area to be cleared, or it can point to actual raw data to be loaded and it can specify a start address. Thus, in a file you have: ----------------- ELF Header --------------- Program header 1 ---------------- Program header 2 ---------------- ... ---------------- Program header n ---------------- Load data ---------------- The file can also contain sections, mainly for debug or vendor specific stuff, but you totally ignore them. Even if sections are in the file, you will automatically ignore them when you use the file offsets to locate the program headers and data. For a banked system you could use a program header for each bank. The address is 32-bit so you can easily combine a bank number with the lower address. If you compare this to the proposed format you will see that the structure is very similar. The ELF header is 52 bytes (in 32-bit ELF) compared to 64 in the proposed format. Each program header is 32 bytes in ELF and 8 in the proposed format. The main difference is that in ELF the program headers are located after each other and contains a field that gives the file offset of the actual data. A loader can interpret relevant fields and do some simple sanity checking, it does not need to be any more complicated. Here is a rough load algorithm: Open file, read ELF header and check the magic number, sanity check some fields in the ELF header to see that it is 32-bit ELF, for 6502 and that there is at least one program header. Store number of program headers, the size and keep a pointer to the current one, initialize it with the offset of the first program header as given in the ELF header. Seek to the next program header, load it and inspect type, read start address (if program type), size, offset in file to actual data. Load raw data from file, if the specified area is larger than what is in file, fill the rest with zero. Step to next program header, decrement header counter, if more program headers then go to 2. Jump to the start address of the program. The main advantage of using an existing format like ELF is that it makes it possible to use other tools for inspecting and altering the executable file. It also avoids re-inventing the wheel again as someone already gave this some thought. We only need to specify how we use the format for the CX16.
  9. I think it would be a good idea to have a loader in ROM so that the machine natively can load large binaries as single files. Without looking much into it, my gut feeling is that unless there is a good reason for it, it is better to use an existing format, or a subset of one. One advantage is that we do not need to invent something new, we just have to specify (supplement) how an existing format is used for this purpose. Second, we can use already existing tools to inspect and do things with such files. For example, I use the ELF format in my tools and as a result I can use tools such as 'objdump' to visualize and inspect the files, and 'strip' to prune them afterwards.
  10. Did you base that on some existing format? I can think of a.out, COFF, ELF and Hunk without going out searching. There are probably more to choose from if you look around. In any case, it would be great to have a format so that large multi-segment programs can be loaded.
  11. For a C or assembly project, I wonder how the PRG file format looks. Is it the same as on the Commodore 64, that it should start at 0x0801 where you can have a tokenized 'SYS address' and the first two bytes in the file are the 01-08 which are just the load address and otherwise ignored (not written to memory)? Second, is there a format so that a file can contain multiple segments to be loaded? For example, one segment is loaded to unbanked memory (like a normal PRG file), then other segments follow in the same file that are automatically loaded into (specified) banks?
  12. I wonder if there is a serial port planned for the Commander X16? It does not seem so from what I have seen, but maybe there is something for the expansion bus? The main reason that I want a serial port is for remote debugging.
  13. I should add that I appreciate having the computer in a separate box. I never liked the computer in a keyboard design (having owned C64 breadbin and Amiga 500). The thing with less memory and slower computer is that you have to work with what you have. That can give a lot of satisfaction getting it to work within constraints. But, yes, you have good points. The 40 column display is annoying and the VERA may be interesting to have. I see myself as mostly using it for remote debugging. If I ever sit down to do things it would be using Forth and in such case I would surely appreciate having 64 or 80 columns. I wish there was a 6551 serial port included, or least a socket for one, but I suppose it would not handle 8MHz anyway. It would be very useful for remote debugging. I will definitely follow the Commander X16 and the alternatives to see how it goes, no decisions on my side yet. Well, I already have an Amiga that gathers dust most of the time, so I do not think I will get the Vampire in the near future at least.
  14. To be honest, for me the main advantage of the Commander X16 over the Mega 65 is the simplicity. It is a standard 6502 (well 65C02) with a simple memory model. However, I think that I will actually just get a new Commodore 64 over any of these machines as it does everything I want. I would just go and buy an Ultimate-64 if I could get a new keyboard for it, but I can't. Thus, I wait and see if the Mega 65 comes out and if the price is not over the top, then I will probably get one and basically see it as a variant of the Ultimate-64. The limitations of the C64 is actually a benefit as if I want to do something myself, as I have to work inside the constraints. It is probably just as fun and also avoids feature creep, making it more likely that I actually have time to finish something. If I wanted something more, then I think I would just do like I did 1987 when I sold my C128D and replaced it with an Amiga. The Vampire Standalone is not that over the top in price and offers a lot more. I prefer that over the Mega 65 when it comes to the extra abilities.
  15. It depends on the run-time model of the compiler how it arranges the stack or stacks. If you have a model with a C stack out in 64K memory, you have plenty of stack. The call stack is still 256 bytes, which translates to 100+ levels or so. That is a lot unless you are into really deep recursive calls. With a decent run-time model based on such ideas you will not gain much by making things global, in fact it is more likely to make things worse. You are best off passing pointers rather than structures by value, otherwise favor local variables and avoid global variables as much as possible. Also, with a stack in the 64K memory there is no problem to allocate things on the stack and pass around as it is just a pointer (just do not let them go out of scope and still use them). The reason that global variables make things worse is that they are subject to aliasing. The compiler cannot know how called functions, interrupts and even arbitrary pointer accesses may affect global variables and is likely to take a conservative approach. With local variables the current function "owns" the variable and the compiler will now more about how it is used and can act on that knowledge. Also, global variables tend to make the code more complicated, especially if the global variables are used in a haphazard way, leading to code that is hard to read, hard to maintain and bugs.
  • Create New...

Important Information

Please review our Terms of Use