Open TheHans255 opened 1 year ago
https://llvm-mos.org/wiki/Porting . At one point I did get a BASIC stub for the Apple IIx running with llvm-mos; you may need to follow the Commodore 64 examples to resurrect it.
https://llvm-mos.org/wiki/Porting . At one point I did get a BASIC stub for the Apple IIx running with llvm-mos; you may need to follow the Commodore 64 examples to resurrect it.
Thanks for the tip. It turned out that I should have been using the "mos-common-clang" linker instead of "mos-clang". The linker script looks a lot more sane now (since I can actually include c.ld
and the others), and I've managed to create some toy examples for the Apple II in C, including some using printf(). The parallel attempts in Rust have been less successful (instead producing a file containing some gibberish and the string "gdb_load_rust_pretty_printers.py", but I'll keep working on those.
Is there any particular advantage to using a BASIC stub instead of a DOS binary file on the Apple II? I find the latter easier to generate, though I reckon that BASIC programs might be easier to port (right now I have to use Ciderpress to get the binary image onto an Apple emulator).
Most Apple IIx users will be familiar with the LOAD command, less so with the BLOAD command. So I went with the BASIC stub solution. Makes it a bit more consistent with the other platforms.
Most Apple IIx users will be familiar with the LOAD command, less so with the BLOAD command. So I went with the BASIC stub solution. Makes it a bit more consistent with the other platforms.
That makes sense, and we could probably do something similar with a CALL instruction in BASIC. Of course, if we want to make the HIRES pages available, the stub will also need to include a copy routine to get the program to $6000, which is also what we would need to write to properly relocate a ProDOS system program for the same reason.
Of course, another option we have on Apple is creating a fully bootable disk, which is probably even more familiar to Apple users than the LOAD command is.
I've started building a repository to perform the work towards building an Apple //e port for LLVM-MOS: https://github.com/TheHans255/apple-ii-port-work
My current focus is on writing text/CLI programs on top of ProDOS, with the goal of eventually recreating a good portion of the C stdlib in order to port programs such as Vi and Telnet. So far, I have written getchar()/putchar() using COUT/RDCHAR, and have created a ProDOS system call interface with endpoints for OPEN, READ, WRITE, and CLOSE. In the immediate future, I plan to expose more of these syscalls and more System Monitor calls, and also provide alternate linking strategies for writing plain Apple, DOS 3.3, or HIRES-capable programs, before submitting a port to this repository.
This is good! I am interested in binaries that can be wholly loaded into memory and that do not use ROM routines or DOS. I think that could just be a matter of library and crt0 configuration. It would also be nice to override getchar() putchar() and other functions, e.g. for a hi-res character routine.
I am not sure what executable format would be best for this, something like Atari's XEX seems useful for handling multiple segments but I'm not aware of anything similar for Apple ][. Right now I just prepend a two-byte header with the start address a la C64 PRG and fill as much RAM as possible. A good binary format would let you skip over DOS, fill language card areas, etc.
My use case is quick-loading into an emulator (faking a disk drive, stuffing RAM and jumping to $803) but it would be nice to turn the executable into a bootable disk. Or at least have an executable format that makes it easy to do so. And of course BASIC stubs and plain old binary files are useful too. There's also AppleSingle but I am not very familiar.
I am interested in binaries that can be wholly loaded into memory and that do not use ROM routines or DOS. I think that could just be a matter of library and crt0 configuration.
I think my efforts should support this. I'm currently developing my SDK additions in multiple tiers that assume different amounts about the runtime environment. Currently, one is just Apple II plus ROM routines, one adds ProDOS, another overrides getchar() putchar() and exit() on top of ProDOS, and a few more set linker settings for different start locations. I can easily add more for different use cases, including DOS 3.3 programs and arbitrarily loaded subroutines (i.e. for BASIC programs or linked libraries).
It would also be nice to override getchar() putchar() and other functions, e.g. for a hi-res character routine.
Totally. My tiered system should help with this too - in fact, I may want to reconfigure things so that these are dependencies that get pulled in.
I am not sure what executable format would be best for this, something like Atari's XEX seems useful for handling multiple segments but I'm not aware of anything similar for Apple ][. Right now I just prepend a two-byte header with the start address a la C64 PRG and fill as much RAM as possible.
I'm not aware of anything like that either. I imagine the next best thing is multiple files and a bootloader program that loads them from DOS in the right places and swaps them in and out as needed, which is definitely very useful if you have a RAM disk, but probably defeats the simplicity that you're looking for.
A good binary format would let you skip over DOS, fill language card areas, etc.
As it happens, as long as it fits your use case (IIe and above), ProDOS lives in the Language Card area, so that could help with the segmentation problem.
My use case is quick-loading into an emulator (faking a disk drive, stuffing RAM and jumping to $803) but it would be nice to turn the executable into a bootable disk. Or at least have an executable format that makes it easy to do so. And of course BASIC stubs and plain old binary files are useful too. There's also AppleSingle but I am not very familiar.
Agreed on those. My current route with ProDOS is to output a System program and use AppleCommander to write it to a bootable disk, and it would probably be nice to streamline that a little bit. I imagine that a bootable DOS 3.3 disk wouldn't be much trouble to generate from scratch.
For the "DOS-free ROM-ignoring" target, I think the linker can be coaxed to output a full .DSK image with bootloader. It could load as much RAM as needed, and the program could optionally use a RWTS library for overlays or additional data. It'd be nice for testing, since the SDK wouldn't need external tools or disk images to produce an emulator-ready product. This might not have a lot of overlap with what you're doing, except for sharing of common "bare metal" (for lack of a better word) routines.
I agree that ProDOS is the priority. DOS 3.3 might be nice for completeness sake, but I don't know if it supports many use cases that ProDOS or raw bootloader doesn't.
For the "DOS-free ROM-ignoring" target, I think the linker can be coaxed to output a full .DSK image with bootloader. It could load as much RAM as needed, and the program could optionally use a RWTS library for overlays or additional data. It'd be nice for testing, since the SDK wouldn't need external tools or disk images to produce an emulator-ready product. This might not have a lot of overlap with what you're doing, except for sharing of common "bare metal" (for lack of a better word) routines.
OK, I think I get it: you would like to have, as an execution target, a disk that is entirely dedicated to the app/game/test program and is not necessarily compatible with any existing OS, in which the 256-byte bootloader at the start of the disk just loads your program directly. Given the format of .DSK files, that should be more than reasonable, and I agree that would be super useful for both quick design iterations and games that want the entirety of a single disk.
I'm not sure exactly how much time I'd want to spend building a Read Write Track Sector library, but I'll be sure to provide whatever platform support we'd need for that.
I agree that ProDOS is the priority. DOS 3.3 might be nice for completeness sake, but I don't know if it supports many use cases that ProDOS or raw bootloader doesn't.
Agreed. All I can think of really is 1) earlier Apple models that don't support ProDOS (since ProDOS 2.0.0+ uses 65C02 instructions), and 2) interacting with DOS 3.3 disks specifically.
OK, I think I get it: you would like to have, as an execution target, a disk that is entirely dedicated to the app/game/test program and is not necessarily compatible with any existing OS, in which the 256-byte bootloader at the start of the disk just loads your program directly.
Yes, exactly! I've even thrown together a CC65 prototype using 0boot that seems to work, barring some language card issues. The bootloader destroys zero page, which is fine if avoiding ROM routines and using all 256 bytes for your program, otherwise it'll have to be re-initialized.
I'm not sure exactly how much time I'd want to spend building a Read Write Track Sector library, but I'll be sure to provide whatever platform support we'd need for that.
Yeah, that might be a bonus feature. I thought it would be easier to find a standalone RWTS, but surprisingly not, except for that 18-sector version used in Prince of Persia.
I've even thrown together a CC65 prototype using 0boot that seems to work, barring some language card issues. The bootloader destroys zero page, which is fine if avoiding ROM routines and using all 256 bytes for your program, otherwise it'll have to be re-initialized.
This is nice! Unfortunately, I'm not sure I quite have the chops to do it myself (and IMO, I'm not quite sure it's a great fit for the vanilla SDK), but I believe that LLVM-MOS's linker scripts should be more than powerful enough to recreate this flow.
Since it seems that we can also accomplish your flow with a handful of post-build steps (i.e. after compiling your C program as a blob, assemble an appropriately configured 0BOOT and concatenate the C program to it to form your disk file), something I can ensure we have is a compiler target that assumes full use of RAM but does not give access to any ROM or DOS routines, emitting a binary blob. This blob should be sufficient to load into an emulator or real device via 0BOOT, but it would also be flexible enough to load onto the Apple II via some other method (e.g. put it on a DOS 3.3 disk to be loaded with BRUN, load it through the audio port with c2t, translate it to Monitor store instructions to be sent over the serial port, stuff an Apple II emulator's save state with the data, etc.)
@TheHans255 Any news on this? Even if partial, it would be good to upstream some Apple // support :-)
@TheHans255 Any news on this? Even if partial, it would be good to upstream some Apple // support :-)
Thanks for asking! I think we could get some stuff upstreamed in soon. Past needing to update my work to match the latest version of the SDK, I guess the main thing blocking upstream integration is the question of which configurations of Apple II machine code we're interested in supporting as a main part of the SDK.
The Apple II has a lot of valid choices for deployment/memory regions, in which pretty much any combination of the following is valid:
All of these options require alternate linking and deployment strategies, and some of them require additional libraries. Still others present complications for both malloc
and printf
, which are included implicitly in mos-common-clang
with no real option to replace them.
malloc
has to be able to use the free regions below it. In programs not using HIRES graphics, programs still have to be able to use the pages that HIRES normally reserves.malloc
that uses an sbrk/morecore model.fprintf
and friends.malloc
needs to be able to skip $C000-$CFFF in the latter case, and __chrout()
needs to switch between simply using the built-in COUT routines and using a home-grown VT100 emulator.Which combinations are we interested in including as a base part of the SDK? I would be OK settling on some core bits of basic functionality and maintaining a separate set of plugin targets for the other combinations, but I would want to know which parts of the functionality we want to support.
This is a question better suited for @johnwbyrd , but I can give my personal opinion, at least.
So far, we generally appear to strive towards supporting "common" configurations - that is, configurations for writing computer programs that people are likely to end up using; be it due to convention, parts availability, or other factors. For example:
.XEX
format, but we also support a few cartridge formats (out of the few dozen documented formats).We also established that LLVM-MOS-SDK is an SDK that tries to be relatively unopinionated while being effective enough for such "common" confgiurations. We acknowledge that other SDKs could pop up based around the LLVM-MOS compiler which target a given platform with higher specificity. However, the NES already has eight subtargets (and issues open for a few more), while the Atari 8-bit computer line has four. If the complexity is compartmentalized and the maintenance cost of such subtargets is low, there is no issue with adding more of them.
I would say that, at the very least, it is not our intention to support the use-case of "subroutine to an existing BASIC program" - LLVM-MOS-SDK produces independent software files as output. Beyond that, I don't really know the Apple II scene enough to understand which configurations are commonly used for creating software in the present day, while which are curiosities only.
Regarding some of the technical issues, which is an area I'm more involved in:
malloc
could use more than one section in theory, but this would be best reserved for a separate issue - I don't think it's a necessary feature for MVP, so to speak.ines.h
, mapper.h
), or the recently added Supervision target for something a little simpler. This would allow people to opt into HIRES graphics (and reserve the relevant memory), for example.stdio.h
doesn't even support file I/O yet. That can wait.I leave a proposal of how to arrange the Apple II targets to you, but here's a very rough GUESS on what I would do based on about ten minutes of researching the hardware:
appleii-common
target which provides all the hardware headers (both for the II and the IIe - there's no reason to distinguish between them at this level, it's up to the programmer to include the headers they want).appleii-dos
and appleii-prodos
. C standard library hooks like getchar()
, putchar()
, etc. supported by default - use of the C standard library is inherently opt-in, so if someone doesn't want the hooks, they can just not use the C standard library and they won't be compiled in.appleii-common
. Something like a monitor ROM should be fairly easy; something like 0boot need not be provided on day one.__a2_hires_pages
, which will be exposed by a helper macro like on NES/PC Engine/Supervision (say, A2_HIRES_PAGES(0);
). If set to 0
, assume the first address of memory of the program to be at $0C00
; if set to 1
, $4000
; if set to 2
, $6000
. We can worry about exposing $0C00-$1FFF
for HIRES programs when LLVM provides non-contiguous memory allocation support.Sorry for the slightly messy comment, but it's tricky to provide concrete answers for hardware I've never worked with... I hope this at least gives some insight/ideas.
Update: Unfortunately, I don't think I'll have the time to complete my work on this anytime soon and upstream Apple II compatibility to this SDK. Life has gotten busier for me lately, and I especially don't have the time I need to keep up with the multiple breaking changes that have been made to the standard library.
I'll probably continue work on my own repositories, likely focusing on alternate implementations of stdio
and malloc
based on ProDOS (since that most closely fits with my original intended use case). In the meantime, if someone else has the time and interest to upstream Apple II compatibility, they are welcome to do so.
I recently came across this project, and I was interested in using it to write code for my Apple //e. There is a demo of LLVM-MOS code running on the Apple //e on the website, but there aren't any target triples for it. Do you think we could add that to the list of platforms?
Ideally, I'd be interested in the following variations:
I'll note that I did attempt to write my own target definition inside my own project (using the instructions from the Embednomicon and basing it off the existing
mos-unknown-none
target), but my linker script did not appear to have access to any of the includes, includingc.ld
,imag-regs.ld
, andcrt0.S
/crt0
/crt
. Here is what I have so far:mos-apple-iie-none.json
:appleiielinker.ld
(which I reference using the-Clink-arg=-Tappleiielinker.ld
argument inconfig.toml
):And the errors I get when compiling
rust-hello-world-mos
with this target triple: