Friday, March 2, 2012

Time for Some Machine Language Programming

I haven't done any 6502 machine language or assembly programming since about 1985. With the Replica 1 it is time to try my hand again.

I learned machine code programming on my old Ohio Scientific Superboard computer. As well as BASIC in ROM it provided a simple machine language monitor program that let you examine and change memory and run programs. You could also load programs from cassette tape. Despite having a screen and keyboard, the monitor was more primitive than the Apple 1 "Woz" monitor, only letting you examine one memory location at a time.

The main book that helped me understand 6502 machine language programming was Programming a Microcomputer: 6502 by Caxton Foster.



Later when I had an Apple //e the Don Lancaster book below helped me understand how to use the Apple EDASM assembler:



I still have my handy 6502 Reference card:



Back in those days I first wrote a disassembler in BASIC to help debug my programs. It helped make sure I had at least hand assembled them correctly, especially branches. Then the disassembler was rewritten in machine code. Later I wrote a simple assembler written in BASIC and I think I also wrote a very simple assembler in machine language.

The largest machine language program I wrote for the Ohio Scientific was a tank battle game that used character graphics and supported two players with joysticks.

With the Replica 1 I wrote some small standalone assembly code starting with the "hello world".  In fact the Apple 1 manual includes a simple program to type into the Woz monitor and it is reproduced in the Replica 1 manual.

Looking for a little larger project, I decided to write a small machine language monitor. Initially this was just going to be a way to dump memory in a more conventional and fast format. Then I added some more commands.

The functionality overlaps with the Woz monitor and Krusader's mini monitor, but it was meant more as a learning exercise and not really as a useful tool.

Commands

The program prompts the user for commands which all start with a single letter. At any time you can hit to cancel a partially entered command. When a command is completely entered it is executed without having to press .

HELP: ?

This displays a summary of the valid commands.

DUMP: D start

This dumps memory in hexadecimal and ASCII a screen at a time. At the end of a screen you can continue by pressing or cancel with . See later for an example of the display format.

FILL: F start end data

This fills a range of memory from a start to end address using an 8-bit data value.

COPY: C start end dest

This copies memory from the source start and end location to the destination. The memory can overlap only if the destination is greater than the start address.

VERIFY: V start end dest

This verifies that two ranges of memory match. Any mismatches are reported (see an example later). It is used to verify that a copy command was successful.

HEX TO DECIMAL: H addr

This converts a 16-bit hexadecimal value to decimal. It uses signed decimal numbers as in BASIC.

RUN: R address

This command runs a program, starting at the given address.

WOZ MON: $

This runs the Woz monitor.

MINI MON: K

This runs the Krusader mini-monitor.

ASSEMBLER: A

This runs the Krusader Assembler.

BASIC: B

This runs BASIC.

A screen shot is shown below:




Here is the output captured from a sample session, with some comments added in italics and commands I typed in bold.

JMON MONITOR V0.1 BY JEFF TRANTER

Help command:

? ?
COMMANDS:
DUMP:      D
FILL:      F
COPY:      C
VERIFY:    V
HEX TO DEC H
RUN:       R

WOZ MON:   $
MINI MON:  K
ASSEMBLER: A
BASIC:     B
HELP:      ?

Dump command:

? D FF00
FF00 D8 58 A0 7F 8C 12 D0 A9  .X......
FF08 A7 8D 11 D0 8D 13 D0 A9  ........
FF10 5C 20 EF FF 20 1B FF 90  \ .. ...
FF18 F6 B0 F9 20 E5 FE A0 01  ... ....
FF20 88 30 F8 20 BE FE 99 00  .0. ....
FF28 02 C9 0D F0 0B C9 5F F0  ......_.
FF30 EF C9 1B F0 DA C8 10 EB  ........
FF38 A0 FF A9 00 AA 0A 85 2B  .......+
FF40 C8 B9 00 02 C9 0D D0 02  ........
FF48 38 60 09 80 C9 AE 90 F0  8`......
FF50 F0 EC C9 BA F0 E7 C9 D2  ........
FF58 F0 3D 86 28 86 29 84 2A  .=.(.).*
FF60 B9 00 02 49 30 C9 0A 90  ...I0...
FF68 06 69 88 C9 FA 90 11 0A  .i......
FF70 0A 0A 0A A2 04 0A 26 28  ......&(                                        
FF78 26 29 CA D0 F8 C8 D0 E0  &)......                                        
FF80 C4 2A D0 02 18 60 24 2B  .*...`$+                                        
FF88 50 10 A5 28 81 26 E6 26  P..(.&.&
FF90 D0 AF E6 27 4C 41 FF 6C  ...'LA.l
FF98 24 00 30 27 A2 02 B5 27  $.0'...'
FFA0 95 25 95 23 CA D0 F7 D0  .%.#....
FFA8 12 20 E5 FE A5 25 20 DC  . ...% .
FFB0 FF A5 24 20 DC FF A9 3A  ..$ ...:
  TO CONTINUE, TO STOP

Fill some memory:

? F 6000 6FFF 00

Copy some memory:

? C 6000 6FFD 7000

Verify the memory:

? V 6000 6FFF 7000
MISMATCH: 6FFE: 00 7FFE: 7A
MISMATCH: 6FFF: 00 7FFF: FF

Convert some hex numbers to decimal:

? H 0001 = 1
? H 7FFF = 32767
? H 8000 = -32768
? H FFFF = -1

Jump to the Krusader mini-monitor:

? K
A-4B X-91 Y-02 S-03 P-36 BIZ
0C14   00          BRK
-




Here are some notes on the implementation of the code.

It was written using the CAS65 cross-assembler. This archive file includes the following files:

jmon.s   - source code for CAS65
jmon.txt - binary suitable for loading into the Woz monitor
Makefile - Linux make file to build the code

Depending on the version of Krusader you have, you want need to adjust the values of MINIMON and KRUSADER in the code to match the start addresses for your version. Mine were set for Krusader version 1.3.

There are some routines that you might find useful in you own programs, such as the PrintString routine. Feel free to do so. Some of these were based on code examples taken from 6502.org.

The code to convert hex to decimal is a little hairy. I based it on some example binary to BCD code and then made changes to support negative numbers and suppress leading zeroes.

The copy memory routines could be smarter and handle a copy to a lower overlapping address, but I didn't bother to implement this. I did add a check for valid addresses.

Most of the code is well commented and should not be too hard to follow. If you have any questions or comments, feel free to contact me.



Update: The source code and binary can be downloaded from here. I made a version 0.2 which adds a search command.

No comments: