Tuesday, June 26, 2012

Using the 65816 Processor

The Western Design Center 65816 is a 6502 family processor. It is backwards software compatible with the 6502 and 65C02 and was used on a number of computers including the Apple IIGS.

I've been playing with one using the CPU adaptor board I built for my Replica 1.

The 65816 comes up in "emulation mode" where it is software compatible with the 6502 and 65C02. It also implements a number of new instructions, some of which are in the 65C02 and some are new ones.

You can switch the 65816 to "native mode" at any time under software control. Native mode offers 16-bit X and Y registers and accumulator, a 24-bit address space (16 megabytes), new instructions and new addressing modes. In native mode you independently configure whether the accumulator and index registers are 8 or 16-bit. Zero page (now called direct page) and the stack can be relocated anywhere in memory.

The chip is not electrically compatible with the 6502 or 65C02. The 65802 was plug-in compatible with the 6502 but is no longer being made. The 65816 is almost compatible. The adaptor I constructed makes it compatible, or at least compatible enough for the Replica 1.

There are some limitations of the adaptor: the system still only has 16 address lines so 16MB addressing is not possible. The extra 8 high order address lines are ignored. Some hardware pins are also not supported, such as SO and SYNC, but these are not used on the Replica 1.

The only compatibility issue I have run onto is with the CFFA1 compact flash adaptor. It gives an "I/O" error. I suspect it is either due to instruction timing differences or more likely the timing of the clock signals generated by the adaptor is not quite right. I need to investigate further.

Having 16-bit registers is a nice luxury and you can still use 8-bits when you need it, by switching modes, which can save speed and code size.

There are even two new memory copy instructions which can copy a range of memory, up to the full address space, in a single instruction.

I wrote three small demo programs, available here. They compile with the CA65 assembler which fully supports the 65816 instruction set. It even has a "smart" mode where the assembler tracks whether the CPU is in 8 or 16-bit accumulator and index registers modes and generates the code accordingly.

Demo 1 uses some of the new instructions in emulation mode and then switches to native mode, first with 8-bit accumulator and index registers, and then switches to 16-bit. What can be confusing is that an instruction like PHA can push 1 or 2 bytes on the stack depending on the mode selected. An instruction like LDA #$1234 is only valid in 16-bit accumulator mode. As you can imagine, existing 8-bit 6502 machine code will soon break if run in 16-bit mode.

Demo 2 uses the MVP (MOve Positive) instruction. First it copies an overlapping range of memory, demonstrating how it can be used to fill a range of memory, in this case with zeroes. Then it does the more commonly uses copy of a range of memory from one area to another. With this single instruction you can move any amount of memory up to the full 24MB address space, taking 7 cycles per byte copied.

Demo 3 is an example taken from the Western Design Center manual which determines the CPU type: 6502, 65C02, or 65816. I extended it to be a complete program which displays the result on the Replica 1.


Here are some useful links related to the 65816:

  1. http://en.wikipedia.org/wiki/WDC_65816/65802
  2. http://www.zophar.net/fileuploads/2/10538ivwiu/65816info.txt
  3. http://www.defence-force.org/computing/oric/coding/annexe_2/
  4. http://www.smwiki.net/wiki/65c816
  5. http://www.zophar.net/documents/65816.html

The definitive manual is Programming the 65816 Including the 6502, 65C02 and 65802 and is available as a free download from Western Design Center's web site, as is the data sheet.

Debugging Tips for 6502 Programing

After working on some 6502 assembler code recently I was thinking about some useful tips for debugging that I could pass on. Here are a few thoughts based on my experience. Many of these are not specific to the 6502 but valid for assembly language or any software debugging in general.

First, here are some suggestions on things to to to minimize your time spent debugging:

1. Use a cross-assembler

While some programmers can write machine code in their head and change code in memory on the fly, with assembler code you can't afford to make simple mistakes like entering the wrong hex code for an instruction. For any significant amount of code I recommend you use a cross-assembler (or at last a resident assembler, like Krusader).

2. Comment your code

It really helps to comment code, even if you are the only person who may ever look at it. A few weeks or months from now you may not understand what your code is doing if it is uncommented. As compared to high-level languages like C or C++ (or BASIC), I think it is good practice to comment almost every line of code, with additional comments describing what each routine does and tricky features and algorithms, etc. Here is an example:

; Get character from keyboard
; Returns character in A
; Clears high bit to be valid ASCII
; Registers changed: A
        LDA KBDCR               ; Read keyboard control register
        BPL GetKey              ; Loop until key pressed (bit 7 goes high)
        LDA KBD                 ; Get keyboard data
        AND #%01111111          ; Clear ms bit to convert to standard ASCII

3. Use a source code management system

Even if you are the only person who may ever use your code, using a source code management system will help you keep different versions of your code during development. This is really useful for the times when you break something that was previously working and you need to figure out what changed. If you use a remote server it also protects you against losing all your work due to human error or hardware failure. There are many services now like github.com, many of which are free for free software projects, that will host your code so you don't have to set up a server.

4. Build up and test small routines

Don't write several hundred lines of code and then attempt to debug it. Write code in small routines and test them individually. It is much easier to debug and your code will likely end up being more modular as well.

5. Do a desk check

A desk check is a manual process of running through your code, as if you were the computer, verifying that it works correctly. It is often helpful to actually step through the code and keep track of values of variables and registers on paper. You will often find errors in your code this way and can more efficiently find and correct the problems.

I have to admit that I sometimes only go back and do a desk check when my code doesn't work the first time (which is usually the case).

6. Print statement debugging

Often you need to figure out where your code is going, and what the values of certain variables are. A tried and true method of debugging is to put instructions in to print information at key points in the code.

You may want to write functions that can print strings and 8 and 16-bit values that you can call for this purpose. In simple code it may be enough to just print single characters or values. If your hardware is such that you don't have a device to send characters to (screen, serial port, etc.) then even a LED or output port can be used to output debug information.

7. Use a simulator.

If you don't have a good debugger on your system you should consider using one of the available 6502 simulators to run and test your code. They may provide a better environment for debugging and testing and eliminate the possibility of hardware problems.

8. Use a debugger

On the Replica 1 the Krusader assembler provide a "mini-monitor" which lets you change registers and single step through code.

My JMON monitor  also has a breakpoint feature that works in conjunction with the mini monitor.

An assembler listing generated by your cross-assembler is also very helpful when single stepping and using breakpoints so you can relate the code to addresses.

Common Errors

Here is a list of what in my experience are some of the more common errors made, even by experienced programmers, when writing in 6502 assembler.

Off by one errors

This is the classic error where a condition test is off by one, such as a loop which runs one time too few or too often.

Branching on the wrong condition

It is easy to use a "BEQ" when you meant "BNE" and hard to notice the error when looking at the code you wrote. Good comments can help here.

Forgetting to clear/set carry before addition/subtraction

This is specific to the 6502 and most programmers soon learn not to make this mistake.

Use # when not needed or missing it when needed

It is easy to forget to use immediate addressing mode mode when that is what you intended or inadvertently use immediate addressing when you shouldn't. For example, the two code examples below are probably wrong but you can easily overlook the mistake, particularly in your own code:

  LDA '$'               ; probably should have been #'$'
  JSR PrintChar

  LDA #VAL              ; if VAL is a memory location, this is wrong
  JSR PrintByte

Registers being changed by called routines

A common error is to call a subroutine and forget that it changes some registers containing values that you subsequently use. I try to always document what registers are changed and try to preserve all registers in code unless I need to optimize them for efficiency.

Conflicts in memory locations (usually page zero)

This is a common error, especially when calling other code that might use memory locations used by your code.

Branching to the wrong part of loop

It is easy to branch to the wrong location in a loop. How many times does the code below run through the loop?

loop: LDX #10
      BNE loop

I recently had an error like this (but a little more subtle):

        JSR GetKey              ; Get character from keyboard
        CMP #CR                 ; key pressed?
        BEQ EnterPressed        ; If so, handle it
        CMP #ESC                ; key pressed?
        BEQ EscapePressed       ; If so, handle it
        JSR PrintChar           ; Echo the key pressed
        STA IN+1,X              ; Store character in buffer (skip first length byte)
        INX                     ; Advance index into buffer
        CPX #$7E                ; Buffer full?
        BEQ EnterPressed        ; If so, return as if was pressed
        BNE loop                ; Always taken

You can ignore most of what the code is doing. The problem is the last instruction. I meant to branch to label "loop1" but I branched to "loop", which was in a completely different routine (so it assembled okay). In part it happened because I copied and pasted the code from a similar routine and then changed it but left the wrong label. The program ran but the behaviour left me scratching my head and I had to single step through the code before I realized the problem.

Here are some additional errors which in my experience are less common but you should watch out for:

Pushing data on the stack and not correctly restoring it

A particularly bad error is pushing data on the stack and then doing an RTS, which will cause your code to return to the wrong address.

Leaving the CPU in decimal mode

If you use decimal mode, be sure it set it back to binary mode when done.

Jump indirect across page boundary

The 6502 has a bug when using jump indirect across a page boundary. The 65C02 CPU fixes this.

There are many good books on 6502 programming, some of which are freely available on the Internet on sites like http://www.6502.org.

A good reference on 6502 hardware and software advice that was recently posted on the 6502.org forum is here.

Monday, June 25, 2012

Replica 1 Now Running With 65816 CPU

I've now built the 65816 CPU adaptor board described here. The parts arrived last week, I finished wiring it up today, and I powered it up with the Replica 1 for the first time. I was amazed to see that it came up in the Woz Monitor.

I was a little concerned that the design was too simple, and if it did not work for some reason, it could not be made to work.

So far it looks good. I am able to run all the 6502 code I tried and my first quick test of some 65816 instructions, including going into and out of 65816 native mode, appears to work.

I built mine on a protoboard and used two 40-pin DIP headers and a short piece of ribbon cable to attach it to the Replica 1 CPU socket.

65815 Adaptor Board Attached to Replica 1

I'll report here as I learn more about the 65816 and get some code examples running on it.

Oh, and my copy of The New Apple II User's Guide arrived today from amazon.com. I'll post a review once I get a chance to read it (all 700+ pages!)

Monday, June 18, 2012

New JMON commands and Enhancements

JMON is my machine language monitor program for the Replica 1. While waiting for some parts to arrive for my next hardware project, I recently added a few new features to it:
  1. I added a new Register command. It allows displaying and setting the registers. The register values are used when executing a Go command.
  2. The Fill and Search commands now accept a data pattern of any length (up to 127 bytes).
  3. I added back and debugged some code in the break handler that checks for break versus interrupt and displays a message if the handler was called by an interrupt rather than a BRK instruction.
  4. The Go command now does a JSR (or the equivalent) so the called program can return to JMON.
  5. I removed the reliance on some external Woz Mon routines to make it more portable and use constants for the hardware registers like the keyboard and screen.
  6. I added a new = command that does simple hex math calculations (addition and subtraction of two 16-bit hex numbers). An example where this is useful is when using the CFFA1 flash card and you need to calculate the program length given the start and end addresses.
  7. Added a command to call the ACI (Apple Cassette Interface) firmware, if an ACI card is present.

Several Apple 1 related auctions were recently completed.

An original Apple 1 on eBay sold for US$75,600.

An original Apple 1 auctioned by Sotheby's in New York sold for US$374,500. This is a new record high for an Apple 1.

A Mimeo-1, the Apple 1 reproduction kit by Mike Willegal, sold on eBay for US$2,025. Note that this was a kit that included all parts but needed to be assembled. Mike also just posted on his blog that he will be offering more Mimeo-1 kits and assembled systems.

Just as a point of comparison, the original Apple 1 originally sold new for US$666.66.

Vince Briel of Briel Computers is considering making another run of reproduction Apple 1 Cassette Interface board kits.  He needs 20 people interested in purchasing one to justify a run of boards. E-mail him if you are interested.

While it is Apple II related rather than Apple 1, the long awaited first new book to be published on the Apple II, The New Apple II User's Guide, started shipping this week. I've already ordered a copy and plan to write a review when I finish reading it.

Saturday, June 9, 2012

Tiny Basic for the Replica 1

Tiny BASIC is a dialect of the BASIC programming language that was written by Dennis Allison for early microcomputers including 6502 systems like the KIM-1. It could fit into as little as 2 or 3 kilobytes of memory.

I came across this web page with a lot of information about this version by Tom Pittman.

It's a little more primitive than the Apple BASIC for the Apple 1, lacking even FOR NEXT loops. It's also quite slow because it is implemented using an interpreted language called (so it's an interpreter written in an interpreter written in machine language).

Tiny Basic spawned a newsletter called Dr. Dobb's Journal of Tiny BASIC Calisthenics and Orthodontia which evolved into the Dr. Dobb's Journal which is still published today.

Bill O'Neill disassembled a 6502 version of Tiny Basic, added comments, and got it working with generic 6502 systems using a serial ACIA for input and output. I ported it  to the Replica 1 in an evening, only having to make a few changes:

  • modifications to assemble with the CC65 assembler
  • modifying the input and output routines for the Replica 1
  • making some memory map changes to run out of RAM by default

The picture below shows Tiny Basic on startup.

Tiny Basic Startup
And running some simple BASIC code:

Running Some BASIC Code

Many programs were written in Tiny Basic including some quite large adventure type games. Below is a screen shot of one such game:

An Adventure Game

So I now have one more BASIC to add to my collection of ports to the Replica 1 which now includes Apple BASIC, Applesoft Lite, Microsoft Basic (based on the Ohio Scientific version), Enhanced Basic, and Tiny Basic.

Wednesday, June 6, 2012

CFFA1 and BASIC Integration

I've been working on integrating the CFFA1 flash card with my ports of Enhanced BASIC and MS BASIC for the Replica 1 so that SAVE and LOAD commands will support BASIC programs.

Using the CFFA1 APIs to save and load memory to and from files is quite straightforward. To simplify the process of getting the filename from the user, I simply prompt for it.

I had problems getting loading of programs to work reliably on either version of BASIC. It was not clear what range of memory needed to be saved and restored. It's further complicated by the fact that the CFFA1 firmware uses a lot of locations in page zero that are also used by EhBASIC so they need to be correctly saved and restored.

After a few evenings without success it was becoming frustrating and I was ready to set it aside for a while. However, I made a posting to the EhBASIC section of the forums at 6502.org and a couple of helpful forum users gave me some more details I needed to get it working. It now seems to be working well with Enhanced BASIC. I haven't yet resolved all the issues with MS BASIC.

I've been watching an original Apple 1 that is up for sale on ebay. The auction will end tonight and is currently bidding at $35,100.

Another Apple 1 is going up for auction at Sotheby's in a week. They estimate it may sell for $120,00 to $180,000. One last year sold for over $200,000.

I also added a new command to JMON to allow writing data to memory, similar to Woz Mon's ":" command. This was the only feature that it was really lacking. I also have a few more features and enhancements planned.