Here's a quick summary of some details related to version1 of JJ65C02:
ROM-based BASIC:
I've planned on adding a ROM-based BASIC since the start, and now that the miniOS bootloader is "ready", I started the work in earnest. The question was, of course, which BASIC to use, with the top 3 contenders being EhBASIC (Enhanced BASIC), MS BASIC, and BBC BASIC. I decided on EhBASIC because it seemed the fastest and most capable, as well as having acceptable licensing terms (for non-commercial use). Unfortunately, the porting to the cc65
toolchain required some work.
I used the KrisOS work-in-progress version as a starting point, which had already changed some of the zeropage assignments to the standard .res
formats. Unfortunately, there was still a lot of work needed to provide the correct spacing since EhBASIC loads some code chunks from ROM to RAM (in page0) and unless you have the spacing correct, you'll break everything. The fact that EhBASIC also re-uses the same zeropage allocations for different variables makes the situation even worse. So what I did was dump a full zeropage mapping of an unported EhBASIC and used that to work backwards on ensuring all variables and labels were where they were expected to be, regardless of where the starting point was.
Once that was done, I jumped in on cleaning up the code and standardizing the syntax formatting. This involved lowercasing all opcodes and making sure that all labels used colons. After that, it was fixing CNTRL-C handing and then adding in an EXIT
command to return from the Interpreter back to miniOS.
Lots of work, but know there's an up-to-date version of EhBASIC ported to the cc65
toolchain for others to use and expand on! My little contribution to the cause.
YMODEM:
I had already decided that I'd use the XMODEM protocol for that, mostly because there was a well-known and well-used 6502-based software implementation already available. But that implementation used XMODEM/CRC and, at least on the various terminal emulation packages I tried, there was really spotty and/or buggy versions of that. Either the host software used XMODEM Checksum or else there was something else, but I had failures at least half the time.
It wasn't all bad though, because while trying to track down the issues with XMODEM, I went ahead and implemented ACAI Rx IRQ handling; my thought was maybe it was overrun issues and buffering the input would address that. It didn't, but moving to an read buffer made sense no matter what. To make it super easy, I allocate 256bytes for the buffer, that way we automatically overwrite "old" data since the index pointers are 8bits and can just increment them and not worry about over-writing buffer space. And since my build doesn't include any hardware flow control, a read buffer is even more advantageous.
After some reasearch, it looked like YMODEM and ZMODEM were viable options, but the amount of work required for ZMODEM hardly seemed worth the effort. YMODEM is basically a formal improvement over XMODEM and so the vast majority of the existing code could be re-used. And I found that YMODEM support was both more ubiquitous and standardized. So YMODEM was it. And it works like a dream
Because of the read buffer, this meant that I needed to adjust my memory mapping again, although not the address decoder logic at all. I've set aside a full 768bytes for SYSRAM which give me some room for growth and allows me to finalize, at least for now, the starting address for RAM-based code.
Next on task was working out the specifics of assembling loadable RAM images using cc65
setup I have for building minios. The only real tricky part was in ensuring that the RAM code had access to the exported functions and memory block of the ROM code. There are 2 main ways of doing this.
First of all, one could create an include file that defines the names of all such exports and associates them with their actual address. cc65
can create both a "label" file and a "map" file that includes such info, but not in an easily consumable format. That would would require some sort of small scriptlet that reads one of those files and creates a exports.h
file.
The other is to simply have the build process for the RAM code link against the ROM code, and have ld65
do all that for us automatically. My first attempt was to archive all the ROM code object files into a library using ar65
and link against that, but the indexing that ar65
does messed up the addressing. So instead it links against all the individual object files, which works great, but is ugly. I've included a small example in the minios distro to explain the details and provide a template to follow.
So at this point, both the hardware build and the software ROM has all the initial functionality I wanted, and it runs well and reliably up to 4Mhz.
The Current Memory Map:
The current memory map is as efficient and as functional as I could devise (currently, that is). The intent was to maximize RAM, and then ROM, and then ensure adequate set-aside for I/O.
To that end, the address and chip select decoder logic for RAM is super easy. If a15 is 0, then we're in RAM space, and we can use the full 32k. Alternatively, a15 high (1), means we are in ROM territory, but we also want to carve out a small chunk of that space for I/O.
For I/O, the design uses the W65c22
and the w65c51
as the VIA and ACIA chips, respectively. Now most I/O, especially using these chip, don't need a large address space, and even 4K is excessive, but workable. To maximize ROM space, I use a 74hc138
and consider the states of address lines a14, a13 and a12. If these are all low (0), then, with a15 also low, that maps to the $8000-$8fff address space. We can use that as a chip select for the I/O chips; If any are high however, that means that we are in ROM space, and so we can use that as the other ROM chip select signal.
So now that we are choosing the I/O chips, how do we determine which chip we actually want to enable? What I came up with is an elegant solution where address lines a4, a5,... are used for the secondary chip select. So, in this case, if a4 is high, we select the ACIA; if a5 is high, we pick the VIA. We can continue this way for up to 6 more chips. This leaves a3->a0 to select the actual address in these mini-blocks allocated per chip:
a11 a10 a9 a8 a7 a6 a5 a4 | a3 a2 a1 a0 | Address
0 0 0 0 0 0 0 1 | x x x x | $8010-$801f
0 0 0 0 0 0 1 0 | x x x x | $8020-$802f
0 0 0 0 0 1 0 0 | x x x x | $8040-$804f
0 0 0 0 1 0 0 0 | x x x x | $8080-$808f
0 0 0 1 0 0 0 0 | x x x x | $8100-$810f
0 0 1 0 0 0 0 0 | x x x x | $8200-$820f
0 1 0 0 0 0 0 0 | x x x x | $8400-$840f
1 0 0 0 0 0 0 0 | x x x x | $8800-$888f
Note that overlapping address spaces (like $8030, where a4 and a5 are both 1) are not used since this would select multiple I/O chips.
Discussions
Become a Hackaday.io Member
Create an account to leave a comment. Already have an account? Log In.