Showing posts with label programming. Show all posts
Showing posts with label programming. Show all posts

Monday, May 1, 2017

Programming the MeinEnigma - Getting Started


The MeinEnigma is a great platform for learning Arduino programming. The wide variety of on-board hardware provides the opportunity to learn how to program different peripheral devices, some simple and some much more complex. Over the next few blog posts I'll present some example Arduino programs that will run on the MeinEnigma.

The programs are standalone and replace the the standard MeinEnigma software that emulates an Enigma machine. You can easily compile and upload the examples using the Ardino IDE (1) running on your desktop computer, attached to the MeinEnigma using USB.

The code was written to be straightforward and readable but not necessarily the most efficient. For example, in most cases I use variables of type int, whereas often a smaller value type could be used that would save a few bytes of memory. This can be important for larger programs, but can make the code harder to understand.

Buzzer Example

The first example is a freebie - playing the buzzer built in to the MeinEnigma. It happens to be on the same i/o port that most Arduinos use for their built-in LED, so it is identical to the example program most beginning Arduino developers start with to flash the LED. It makes a good first programming task to confirm that you have a working Arduino development environment.

The code is shown below in it's entirety. In the setup() method, which is run once on powerup, we configure the digital i/o pin connected to the buzzer to be an output.

In the loop() method, which is run repeatedly after setup() is called, we drive the output high for half a second then drive it low for the same length of time, causing the buzzer to turn on and off at a one second rate.

Note that for this type of piezoelectic buzzer hardware, a high level will produce a tone at a fixed frequency determined by the buzzer itself. We can't change the frequency or volume level (at least, not without some more complicated programming). As I mentioned in an earlier blog post, you may find the buzzer a litte loud and covering it with some electrical tape is a simple way to reduce the volume.

#define BUZZER 13

void setup() {
  pinMode(BUZZER, OUTPUT);    // Initialize digital pin 13 as an output.
}

void loop() {
  digitalWrite(BUZZER, HIGH); // Turn the buzzer on.
  delay(500);                 // Wait for half a second.
  digitalWrite(BUZZER, LOW);  // Turn the buzzer off.
  delay(500);                 // Wait for half a second.
}

Conclusions

As this blog series progresses I will introduce coding examples that are increasing more complex (and, hopefully, interesting). At the end we will tie it all together with a larger program that makes use of most of the code examples and hardware on the MeinEnigma.

I hope you will follow along and try the examples, and maybe make some changes of your own to the code.

All source code can be found at link 2 under References below.

References


  1. https://www.arduino.cc/en/Main/Software
  2. https://github.com/jefftranter/meinEnigma/tree/master/Examples


Tuesday, June 26, 2012

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
GetKey:
        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
        RTS

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
      DEX
      BNE loop

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

loop1:
        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.

Tuesday, May 15, 2012

Book Review: Beyond Games: Systems Software for your 6502

Title: "Beyond Games: Systems Software for your 6502"
Author: Ken Skier
Publisher: Byte Publications, 1981, 433 pages


This is a used book I purchased recently on ebay. There were many books on 6502 programming of this era but not many on tools to help writing and debugging programs, like machine language monitors and disassembler.

This book provides an introduction to 6502 machine and assembly language programming, and then quickly moves to specific programs, including:
  • a "visible" machine language monitor
  • two hex dump programs
  • a disassembler
  • a move utility
  • a simple text editor

It describes the design of the programs and shows source code. In the appendices it provides complete assembler listings as well as binaries in the form of BASIC DATA statements than can be loaded into memory using a provided BASIC program.

The programs are mostly portable, and the author provides a set of interfaces or "data blocks" that adapt the code to run on the popular 6502-based systems of that era: the Ohio Scientific C-1P, Commodore PET 2001, Apple II, and Atari 800.

There is a brief chapter on each machine describing it's video memory layout.


I found it a very interesting and useful book, and intend to adapt some of the code to the Apple Replica 1.

Interestingly, the design of the table driven disassembler uses some of the same approaches that I used for the one I wrote recently.

Overall the code is clean, well documented, and clearly written. The author purposely avoided using tricks that would obscure the clarity of the code.

The book is long out of print but is available on sources for used books if you search hard enough for it.