Friday, July 26, 2013

Marking Time

It is time once again for another exciting update on my Micro-C for CoCo project.  It has been a busy week for me here in North Cackalacky!  Unfortunately for me, it has mostly been a "work" kind of busy...  Nevertheless, I have been able to make a little progress on my project.

Pak It In

Like many machines of it's era (e.g. the Atari 2600), the CoCo had the option of using software distributed on ROM cartridges.  The clever marketing folks at Tandy marketed this as "Program Pak" software...  In any case, there are advantages to this software format.  So, it might be nice to distribute compiled C programs the same way.

Relocating the compiled code to the ROM cartridge address space is the obvious first step.  An added twist is the question of where to put the variables.  Obviously, storing "variables" in ROM is of limited value.  Luckily, the Dunfield library already provides for allocating variables in a different address range from where the code resides.  Yet another variation of the startup code allows for selecting this configuration.


Many programs require the use of interrupts for proper operation.  But, handling interrupts requires machine-specific accommodations that aren't part of the standard C language.  Many compilers provide mechanisms for implementing interrupt handlers in C, and Micro-C is no exception.  Unfortunately, the default mechanism provided with the Micro-C package generates code to hook the 6809's interrupt vectors in a ROM image.  That is fine for use in an embedded system, but it won't work with the CoCo.

The CoCo ROMs redirect the interrupt vectors through a secondary set of vectors in RAM.  Using the stock Micro-C interrupt implementation as a guide for interfacing with the compiled code, I implemented a CoCo-compatible version.  My implementation uses a macro to output some inline assembly code in order to establish some code labels and to provide a 'trampoline' function.  The trampoline function calls the C interrupt handler and then uses an RTI instruction to resume normal operation of the CPU.  I also added some macros to make it easier to point the secondary interrupt vectors at the appropriate handlers in the C code.

Time Flies

One of the few timing sources on the CoCo is the ~60 Hz Vertical Sync (a.k.a. "Vsync") interrupt.  With the code described above I was able to build a simple clock program that keeps time by counting Vsync interrupts.  I also used an inline assembly macro to provide access to the SYNC instruction.  This allowed me to properly time a colorful border animation for the clock -- the border is timed to complete a loop in approximately 1 second.  Just for fun, I compiled the clock to run in the emulator as a ROM image.  Since I didn't bother to burn a physical ROM, I also re-compiled the clock as a BIN file to load through the cassette interface on a real CoCo.


If you experiment with running my clock demo, you will find that the CoCo isn't really a very good time keeper.  Keeping time by dead reckoning with an almost-but-not-quite 60 Hz timing source doesn't compete with the accuracy of a Swiss watch!  I toyed with a few drift compensation strategies, but so far all I've done is prove that programming the CoCo in C is every bit as much fun as it is to program one in BASIC or assembly... :-)

Anyway, I guess that is enough for now.  Only a few more days remain for the Summer 2013 Retrochallenge event.  I hope to make the most of them, but I suspect that in any case I will be working on this project well past the end of the competition.  Either way, I hope you will stay tuned!

Sunday, July 21, 2013

BASIC Instinct

As I was working on implementing a CoCo API comparable to what Color BASIC provides, I had a couple of realizations.  The first was that while such an API might be superficially familiar to some CoCo programmers, it wouldn't necessarily be a good API for anyone to use.  The other was that an API implementation wasn't actually as interesting as some other aspects of the project!

So for now, I'll leave the remainder of the API library work undone.  I may return to expanding the library later, but for now I want to hit a few more objectives.  In this episode, we will look at interfacing C programs to Color BASIC...

CLEAR Some Space

Once again we come to the problem of where to put a program in the CoCo's memory space.  This is similar to what we faced before, except that now we have to share the available memory with a BASIC program at the same time.  What needs to happen is for BASIC to give us some space to live and for it then to leave us alone.

Color BASIC is ready for us.  The CLEAR command allows us to influence how BASIC allocates memory, including the ability to set a limit on the highest address BASIC will use for its programs.  This allows a BASIC program to make room for us before loading and executing our program.

Corresponding to that, I created options in the startup code to allow the C programmer to specify what CLEAR value is being used.  This causes the C program to be compiled to run in the proper space and to allocate memory appropriately.  More importantly, it provides us an open field in which to run without worrying that we might trample on something important to our BASIC program.


CoCo programs that don't interact with BASIC don't have any way to accept anything like a 'command-line argument', so there has been no need to worry about that until now.  BASIC doesn't offer C programs a command-line either, but there are ways to pass information to our C program if we so desire.  In fact, perusal of the Color BASIC documentation reveals that there are a variety of options for passing information both into and out of C programs.

Lacking a single path to follow, I implemented a variety of options in the startup code.  These correspond to the documented options for Color BASIC to pass information to a 6809 machine language program, and they require certain variations to the 'main' declaration in the C program.  Some of those options require data structures that are foreign to anything resembling standard C, so I also knocked together a header file to describe the various data structures involved.  I posted that header file and some sample prefix files that set options for the startup code, if anyone is interested.


The Color BASIC documentation had another interesting point -- the stack provided by Color BASIC is only good for about 30 bytes of usage.  It wouldn't take many levels of function calls with arguments pushed onto the stack to burn-up that little amount of space!  Not only that, but the Micro-C library's implementation of malloc presumes that the stack is at a higher address than the heap.  The memory provided by the CLEAR command is at a higher address than the Color BASIC stack, so malloc won't work with it.

The answer is simple enough -- allocate a new stack at the top of RAM.  Of course, in order to return to the calling BASIC program the Color BASIC stack pointer has to be saved at program entry and restored at program exit.  A few more simple modifications to the library startup code accomplished this task without incident.

So, now we can produce CoCo programs written in C that either stand alone or which can fully cooperate with programs written in BASIC.  Things are coming together nicely!  There is only a little more than a week left in the Summer 2013 Retrochallenge event.  Do you want to see what comes next?  Then I guess you'll have to stay tuned!

Monday, July 15, 2013

Know Your Compiler

We've reached the half-way point for the 2013 Summer Retrochallenge event!  I would, of course, like to be further along with my Micro-C/6809 support package for the CoCo.  But, overall, I think the project is making good progress.  Over the weekend I posted about my success at getting some initial routines implemented, and I was able to show a simple low-res graphics program running.  In the process, I have learned a bit more about how that compiler works and how best to make use of it.

Cast Away

When I was implementing a routine to compute the address for a byte of graphics data, I discovered an anomaly.  I initially tried to use an expression that added a computed value to a constant offset, but found that the resulting value was incorrect.  Surprisingly, separating the addition of the computed value into a different line from the initial assignment of the offset value resulted in a correct result.  Reviewing the generated code showed that the non-working case was adding a 16-bit constant to an 8-bit register -- oops!

Compilers often have quirks, and "small" compilers often have some weird ones.  I suspected that I might have been hitting one of those, so I reviewed the docs that came with Micro-C.  The docs revealed that due to performance considerations (i.e. a preference for 8-bit operations), untyped constants used in an expression with 8-bit types are treated as 8-bit values.  I can't help but think that this sort of situation begs for throwing a warning at compile time, but alas it did not.  Anyway, adding an explicit typecast to a 16-bit type prompted the compiler into generating working code.

Optimism

As I was rooting around in the code generated by the compiler, I noted some oddities.  The code was doing some weird things, like a register store to memory followed immediately by a load from that same memory location to the exact same register.  This sub-optimal behavior reminded me that I was not telling the compiler to run the optimizer.  So, I added the "-o" option to the invocation of cc09 and observed many improvements to the generated code.

When running the test code built without optimization, I observed that the program didn't run significantly faster than the BASIC program upon which it was based.  I originally surmised that the slow speed was the result of including a keyboard scan inside the "blinking" loop.  However, the optimized code was easily observed to run much faster than the non-optimized code!  Maybe compiled languages really are faster... ;-)

Handiwork

The expression in question included a combination of multiplication and division by powers of two, which I recognized could be implemented with logical operations like masking and shifting.  I observed that even with optimization enabled, the generated code was still using multiply and divide operations to compute that expression.  A more modern compiler might be expected to recognize this situation and to use the logical operations instead, but the "peephole" optimizer included with Micro-C doesn't make the connection.

In order to squeeze out a little more performance, I re-implemented the expression using logical operations in the C code itself.  This generated the code as expected, and a modest performance gain was observed with the test program.  Sometimes you just have to take matters into your own hands!

I'll make a study of the generated code available for those interested in such things.  Next time I'll probably move onto implementing some solutions for better integration with BASIC...or maybe I'll do something else...  Anyway, I guess you'll have to stay tuned to find out!

Saturday, July 13, 2013

Set, Reset, Point

Another week passes with limited progress on my Retrochallenge project.  My day job has been keeping me busy lately.  So, I've been working for the weekend! :-)

BASIC Functionality

I wasn't sure where to start on designing an API for the CoCo.  So for now, I've decided to punt by simply mimicking the commands provided by the CoCo's built-in BASIC programming environment.  I won't attempt to transform C into BASIC with implementations of MID$ and the like.  But, I will try to implement some of the machine-specific commands for graphics, sound, and the like.

With that said, I have begun by implementing the simplest suite of graphics commands.  On the CoCo, this is provided by the set, reset, and point operations for turning pixels on and off and querying a pixel's status on the "semi-graphics" (i.e. VDG mode SG4) screen.  Since that mode allows for mixed text and graphics, I also included routines for clearing the screen and for positioning the text cursor.

Parsing Anomaly?

A funny thing happened on the way to the forum...  While implementing the code to calculate the addresses used in the graphics routines, I discovered that the calculation was producing incorrect results.  To debug that situation, I deconstructed a complex statement into multiple simpler statements.  To my surprise, the calculation then produced the correct results!

I still don't see what difference exists between those implementations that could explain why one performs correctly and the other does not.  Sometimes compilers (particularly "small" ones) have some idiosyncrasies, and the Dunfield compiler is no exception.  I'll have to dig back through the documentation to see if I am hitting one of those cases.  On the other hand, I may simply have done something wrong.  I would welcome anyone who is interested to look at the code in _getpos and to let me know if they see the difference.


Slow Ride

The program running in the video is a C implementation of a program from page 128 of Getting Started with Extended Color BASIC.  As I commented in the video, the C program doesn't seem to run any faster than the original BASIC program.  The difference is that the C program checks for keyboard input after each "blink", while the BASIC program just loops with a GOTO and has to be stopped by pressing BREAK.  If you remove the keyboard check in the C program, the blink is more like a fast flicker.

I would guess that the same keyboard scan routine is called in either case, with that fact hidden in the case of the BASIC program.  The keyboard scan in the ROM does a lot of work, and that work takes time.  Hopefully the speed penalty demonstrated with the C program calling the ROM routine illustrates that simply coding a program in a compiled language is insufficient to guarantee improved performance.

I had a lot of fun working on this today.  Hopefully I can get some chores done and find more time to work on this tomorrow!  If you want to see what comes of that, then I guess you'll have to stay tuned...

Sunday, July 7, 2013

Small Steps

Well, I'm still trudging along on retargeting Micro-C for the CoCo.  As with any project, the fundamentals must come first!

The docs from the Micro-C package covered how to run the tools, what they do, etc.  The key to targeting a specific system is to port the included library, lib09, to the target system.  This is described in the readme.txt file under the lib09 directory.

Location, Location, Location

First and foremost, the decision must be made as to where the code will live in the CoCo's memory.  This location might be anywhere, but realistically it needs to be somewhere in RAM.   Ideally the code will also be located somewhere that won't interfere with Color BASIC, or at least somewhere that allows Color BASIC to load our programs.

There are a number of considerations that might influence this choice.  These include how one intends to load programs, what limits one might impose on the size of programs, what level of cooperation one might want to have between C and BASIC programs, etc.  For now, I'm going to shoot for loading under Disk Extended Color BASIC (i.e. DECB) and I'm not going to worry about cooperation between C and BASIC at runtime.  Being familiar with how DECB uses memory on the CoCo, I have chosen to load C programs at address $0E00 and to set the initial stack pointer to $8000 at program entry.  At program exit, the CPU reset vector at [$FFFE] will be executed.

Console I/O

The lib09 file called serio.asm implements the basic operations for reading and writing text, one character at a time.  The default implementation targets a 6551 ACIA, commonly used for RS-232 implementations on 6809-based machines of a certain era.  In fact, that would work great if we only wanted to use the Tandy Deluxe RS-232 Pak.  But, I think it would be better to write to the video screen and to read from the keyboard... :-)

Fortunately, Microsoft included ROM routines in Color BASIC for just this purpose.  The CHROUT routine handles screen output, including basic scrolling.  The POLCAT routine handles scanning the keyboard.  I implemented routines in serio.asm that used CHROUT and POLCAT in place of the example 6551-based routines.

File Format

The S-record file output by the Micro-C tools isn't directly usable by the CoCo.  The hex values need to be translated to the equivalent binary values, and both header and footer data need to be added to tell DECB where to load the program and how to execute it.  A little cleverness with ORG, FCB, and FDB statements in the lib09 prefix and suffix files tricks the Micro-C tools into generating the header and footer data required by DECB.  Translating the file into binary requires another utility.

Older versions of the Dunfield tools included a utility called hexfmt that could translate between S-record files and pure binary files (among other things).  I can't seem to find this file for download on the Dunfield site right now, but I might be overlooking it.  (It seems to be included in the Z80 cross assembler package here.)  On Linux the objcopy utility can also do such conversions.


Achievement Unlocked

With the fundamentals covered, I am now free to move onto library enhancements, better cooperation with Color BASIC, etc.  I also plan to go into a bit more detail on the wonders of MON09.  Of course, to see that you will have to stay tuned... :-)

Friday, July 5, 2013

Slow Start

The 2013 Summer Retrochallenge started days ago, but I haven't done much so far -- I did take some time to read the various docs that came with the Micro-C package, but beyond that I've been caught somewhere between laziness and real life demands.

I hope to get a bit more done this weekend.  In the meantime, I figured I could take a few moments to describe a little bit about my development environment for this project...

Penalty Box

For me, the biggest drawback with using the Micro-C tools is the fact that they run under DOS.  I was a DOS refugee long before Bill Gates said goodbye to DOS, having been an OS/2 user in the early- and mid-90's.  Alas, there is little to be done about that other than to adapt.  Fortunately, the world of free software has provided an acceptable option in the form of DOSbox!

DOSbox emulates an x86 PC running DOS.  This tool was originally created to enable playing DOS-era games on more modern systems, and that remains the focus of its development.  Fortunately, making games work generally requires the same things as making apps work.  The Micro-C tools work just fine under DOSbox.

Hear Me Roar

A compiler that runs on one system but produces code for another is called a "cross compiler".  This sort of tool provides many advantages, including faster compilation times and easier integration with other (e.g. modern) tools.  One problem with cross compiling is that the resulting code needs to be loaded onto the target system before it can be run and tested.  An emulator for the target system can make this task a lot more convenient.

One of the best CoCo emulators is called Xroar.  Xroar was actually written to emulate the Dragon, but the two systems are so similar that adding CoCo support must have seemed an obvious addition.  In any event, Xroar works and provides stable and accurate emulation of the CoCo.  It also provides options for quickly loading code -- perfect for a rapid development cadence!

Hall Monitor

Sometimes there is just no substitute for running on real hardware.  Loading code on a stock system can be slow and awkward.  Doing that might include audio playback through the cassette port, or even managing physical diskettes.  File transfers over the serial port are another option.

Using the serial port requires software running on the CoCo.  A good option for that is a machine code monitor.  Fortunately the Micro-C package includes such a monitor, called MON09.  Along with the ability to load code and modify memory, MON09 is capable of examining and modifying registers, setting breakpoints, etc.  It is a great development tool for working with the CoCo.  I'll describe my modifications to MON09 for the CoCo in a later post.

Well, that is enough for now.  I should have a bit more available soon.  Until then...stay tuned!

Sunday, June 30, 2013

A New Hope

It's that time again -- the 2013 Retrochallenge Summer event!  Over the past couple of years I've really enjoyed participating in these events.  The regular schedule brings a sort of discipline that reminds me that I need a little "retro" time.  So, here I go again... :-)

Project Options

As usual, I had to ponder a bit to decide on a project for this event.  I never have any shortage of possibilities, and I have at least one notebook where I've jotted down various project ideas in case I should ever be short for one.  This year I was drawn towards working on a CoCo-driven laser projection project, but I also considered a game for a vintage console (e.g. the Intellivision), attempting to adapt Boisy Pitre's Liber809 project to an Atari 5200, expanding my VDG tricks blog or adding enhancements to Fahrfall.  Those all would be good projects (and may still be).  But this time, something else appealed to me.

Stay On Target

Years ago I became aware of Micro-C and related tools from Dunfield Development Systems.  These tools target a variety of older and/or embedded processors, including the 6809 used in the CoCo.  They are provided in a form that is relatively easy to re-target to specific systems as needed.  In fact, I did a simplistic port of these tools to the CoCo several years ago for a simple project.

The Micro-C tools are now available for free (as in "beer") download.  That puts them within reach of a broader range of potential CoCo developers, but as-is they aren't particularly helpful for targeting the CoCo.  Even my simplistic port isn't too good for that, since it didn't have any CoCo-specific hardware support, etc.

Wrap It Up

So, that's the project -- target the Micro-C tools to the CoCo.  This should cover properly locating code, generating correct executable files, and at least basic console I/O.  I expect to also include at least some primitive support for configuring graphics modes, playing sounds, reading the joysticks, etc.  I doubt if I will get to supporting disk drives this month, but that can wait for later.

The contest starts tomorrow, so that's all for now.  If any of the above sounds interesting to you, then be sure to stay tuned!