lib6502-jit/examples/README

407 lines
14 KiB
Plaintext

lib6502 - 6502 Microprocessor Emulator
EXAMPLES
This file has three sections:
1. PROGRAMS that you can compile and run
2. COMMANDS that you can copy and paste into a terminal
3. ADVANCED stuff that requires some additional setup
A few numbered footnotes appear at the end and are referenced in the
text in square brackets [6].
----------------------------------------------------------------
1. PROGRAMS
(We're going to start in 'serious mode'. Bear with me.)
The file 'lib1.c' contains the example from the run6502 manual page.
Just compile and run it:
cc -o lib1 lib1.c
./lib1
The file has been commented extensively to explain exactly what is
going on.
----------------------------------------------------------------
2. COMMANDS
(Much more fun: this is the section that appeals to the geek in me.)
6502 machine code is pretty straightforward. (Many 6502 programmers
remember a time from their misguided childhood when they could
compose and edit programs directly in hexadecimal using their 'front
panel' monitor program -- the next best thing to programming with a
row of switches and lamps, but I digress and will leave that story
until the pdp11 emulator is ready. ;-) We can use this fact to
generate an entire program without needing an assembler. The 'perl'
program is available on most Unixy (and several other) systems and
makes it easy to create binary files from a string of hex digits.
(There is a program called 'xxd' that's very good at this kind of
thing, but you might not have it.)
First the program (stolen from lib1.c):
1000 ldx #41 A241
1002 txa 8A
1003 jsr FFEE 20EEFF
1006 inx E8
1007 cpx #5B E05B
1009 bne 1002 D0F7
100B lda #0A A90A
100D jsr FFEE 20EEFF
1010 brk 00
In C-like syntax it is equivalent to:
regX = 'A';
do {
regA = regX;
putchar(regA);
} while (regX != 'Z' + 1);
putchar('\n');
(which by today's standards is a *huge* amount of stuff packed into
just 17 bytes of 'compiled' code -- on a 386 the same program is
around 65 bytes [1], and more like 88 bytes on a 32-bit RISC [2]).
The column on the right is the machine code in hexadecimal. When
strung out in a line it looks like this:
A2418A20EEFFE8E05BD0F7A90A20EEFF00
We can tell perl to 'pack' this hexadecimal string into binary and
save the output in a file:
echo A2418A20EEFFE8E05BD0F7A90A20EEFF00 |
perl -e 'print pack "H*",<STDIN>' > temp.img
To check the contents of the file, we can load it into run6502 and
then disassemble it:
run6502 -l 1000 temp.img -d 1000 +11 -x
The '-l 1000 temp.img' loads the file into the 6502's memory at
address 0x1000, and the '-d 1000 +11' disassembles 17 bytes (11 in
hex) of code starting at 0x1000. The final '-x' tells run6502 not
to try to execute the code. The output should look just like the
program listing above.
This is almost all we need to run it; just a few details remain.
- The emulator doesn't know where to start execution. We need to
set the 'reset' vector to 0x1000 -- the address of the first
instruction in the program. The '-R 1000' option does this.
- The program calls the 'putchar' function at address 0xFFEE to
send a character to the terminal. run6502 can emulate this for
us, with the '-P FFEE' option.
- We have to have some way to make the processor stop execution
(there is no 'halt' instruction on the 6502, at least not the
early versions). The trick is in the last instruction 'BRK',
that generates a 'software interrupt' -- eventually jumping to
the addres in the 'interrupt vector'. If we don't set the
interrupt vector explicitly it remains empty (zero) and BRK will
try to transfer control to address 0. The '-X 0' option tells
run6502 to stop executing if/when the program attempts to
transfer control to address 0 -- which it will, when it executes
the 'BRK' instruction with an empty interrupt vector. QED :-)
Here, then, is the complete command to run our program:
run6502 -l 1000 temp.img -R 1000 -P FFEE -X 0
This program is relocatable. You can load it at address 4321
(change both the -l and -R options) and it will work just fine.
Google for "6502 Reference Card" (with the quotes), grab a pencil
and paper, and you can start writing 6502 programs immediately! (If
you really want to experience what it was like in the late 1970s,
but without the added fun of entering each hex digit one at a time
into a monitor program, simply avoid the temptation ever to look at
your hand-assembled code with the '-d' option. ;-)
If you really start liking this and want to write longer programs in
text files with the hex split over many lines, you'll need a perl
script that can deal with newlines in the input. Something like
this should do the trick...
#!/usr/bin/perl
while (<STDIN>) {
chomp;
print pack "H*", $_
}
(This script is included in the 'examples' directory, in a file
called 'hex2bin', to save you 15 seconds of copy and paste.)
Need a fun project? Write a 6502 assembler... in 6502 machine code,
of course! Read in the assembly language text via 'getchar' (see
the '-G' option) and write out the assembled binary via 'putchar'
(the '-P' option, that we've already seen). Soon you'll be able to:
cat prog.s |
run6502 -l 1000 asm.img -R 1000 -G FFE0 -P FFEE -X 0 > prog.img
run6502 -l 1000 prog.img -R 1000 -G FFE0 -P FFEE -X 0
(The first prog.s you write should probably be the assembler itself,
transcribed from the paper copy used to hand-assemble the assembler
binary. This significant milestone can be reached with a
surprisingly simple assembler. After this pivotal moment the
assembler, assembling itself, can very quickly become very
powerful.)
----------------------------------------------------------------
3. ADVANCED
(Official justification: let's run something big and non-trivial.
More likely: a flimsy excuse for a trip down memory lane.)
The remaining examples assume that you have access to two ROM images
from the Acorn 'BBC Model B' microcomputer: the operating system and
the BASIC language . (Just crawl into the attic, fire up the old
Beeb, '*SAVE' the images into files, and then transfer them to your
Unix box over RS423. Under no circumstances should you google for
'Acorn BBC B OS ROMs zip', without the quotes. That would be
naughty, and probably illegal -- at least until the glorious day
when the revolution finally comes.)
After brushing yourself down (the attic is kind of dusty, no?) save
the two ROM images as 'OS12.ROM' and 'BASIC2.ROM'.
The first thing we can do is use run6502 as an editor to merge the
two ROMs into a single image file:
run6502 \
-l C000 OS12.ROM \
-l 8000 BASIC2.ROM \
-s 0000 +10000 bbc.img \
-x
(This is a single command, with '\' continuation characters joining
the lines into one. Your shell should figure it out if you just
copy and paste.) It leaves a file 'bbc.img' containing both the OS
and BASIC.
To run this image we need the '-B' option. It enables some minimal,
totally lame, hardware emulation of the BBC computer -- just enough
to boot the 'virtual beeb' into BASIC [3]:
run6502 -l 0 bbc.img -B
If all goes well, you should be greeted with a 'beep' and a message
telling you what computer you have (BBC Computer), how much RAM is
available (32K), the language you've been dropped into (BASIC), and
a '>' prompt. Turn on 'CAPS LOCK' (many of us remember those days,
and some of us even used to speak in ALL CAPS) and play:
PRINT 3+4
or maybe:
10 FOR A%=1 TO 10
20 PRINT A%
30 NEXT
LIST
RUN
or even:
10 P%=&2800
20 O%=P%
30 [
40 opt3
50 lda #10
60 jsr &FFEE
70 ldx #65
80 .l txa
90 jsr &FFEE
100 inx
110 cpx #91
120 bne l
130 lda #10
140 jmp &FFEE
150 ]
160 CALL &2800
LIST
RUN
(How cool is that? ;-)
One final thing: there is an option '-i' that works just like '-l'
except that it looks to see if the image file begins with '#!'. If
so, it skips over the first line of the file, up to and including
the first newline. Why? The system call that executes programs on
Unixy systems makes the same check. If the user executes a text
file 'foo' staring with '#!prog ...' then the OS loads and runs
'prog' instead, passing all the '...'s and the name of the text file
'foo' as arguments [4]. If you have 'temp.img' left over from from
the second example, open it in a text editor and add a single line
at the beginning that reads:
#!run6502 -i 1000
(If 'run6502' is not in your current working directory then you will
have to use the full path to the file: '#!/usr/bin/run6502' or
'#!/usr/local/bin/6502' or whatever. No spaces before the '#'!)
Now make the image executable:
chmod +x temp.img
and then (as if you hadn't already guessed) execute it:
./temp.img
Saves an awful lot of tedious typing. [5]
Have fun!
----------------------------------------------------------------
FOOTNOTES
[1] Here is the 'alphabet' program, verbatim, compiled (with
optimisation) on a 386. It's 66 bytes long, almost four times
longer than the 6502 version. (If I were more generous I might
consider that fair: 32 bits divided by 8 bits is four.)
0: 55 push %ebp
1: 89 e5 mov %esp,%ebp
3: 53 push %ebx
4: 83 ec 14 sub $0x14,%esp
7: bb 41 00 00 00 mov $0x41,%ebx
c: a1 00 00 00 00 mov 0x0,%eax
11: 89 44 24 04 mov %eax,0x4(%esp)
15: 89 1c 24 mov %ebx,(%esp)
18: e8 fc ff ff ff call 19 <fputc>
1d: 43 inc %ebx
1e: 83 fb 5b cmp $0x5b,%ebx
21: 75 e9 jne c <prog+0xc>
23: a1 00 00 00 00 mov 0x0,%eax
28: 89 44 24 04 mov %eax,0x4(%esp)
2c: c7 04 24 0a 00 00 00 movl $0xa,(%esp)
33: e8 fc ff ff ff call 34 <fputc>
38: b8 00 00 00 00 mov $0x0,%eax
3d: 83 c4 14 add $0x14,%esp
40: 5b pop %ebx
41: 5d pop %ebp
42: c3 ret
[2] Here is the 'alphabet' program, verbatim, compiled (with
optimisation) on a PowerPC. It's 88 bytes long, more than five
times longer than the 6502 version. (I don't care what you say:
Apple Macs rule and mine has oodles of RAM to spare.)
00000000 mfspr r0,lr
00000004 stmw r29,0xfff4(r1)
00000008 stw r0,0x8(r1)
0000000c stwu r1,0xffb0(r1)
00000010 bcl 20,31,0x14
00000014 mfspr r31,lr
00000018 li r30,0x41
0000001c addis r2,r31,ha16(0xa4-0x14)
00000020 lwz r29,lo16(0xa4-0x14)(r2)
00000024 or r3,r30,r30
00000028 addi r4,r29,0x58
0000002c bl 0x7c ; symbol stub for: _fputc
00000030 cmpwi cr7,r30,0x5a
00000034 addi r30,r30,0x1
00000038 bne cr7,0x24
0000003c li r3,0xa
00000040 bl 0x5c ; symbol stub for: _fputc
00000044 li r3,0x0
00000048 lwz r0,0x58(r1)
0000004c addi r1,r1,0x50
00000050 mtspr lr,r0
00000054 lmw r29,0xfff4(r1)
00000058 blr
[3] Time to 'fess up with an undocumented 'feature'. We ran our
'bbc.img' file like this:
run6502 -l 0 bbc.img -B
I grew tired of typing all those '-'s and made run6502 check to
see if it was invoked with a single, non-option argument.
Running:
run6502 bbc.img
is precisely equivalent to the '-l -B' form above. I don't feel
too guilty about this since the manual page suggests that
providing a single, non-option argument is illegal usage.
[4] Okay, that might be a little confusing. Here it is written out in
full. If you have a text file called 'foo' containing
#!/usr/bin/prog -gobble
blah blah blah
blah blah blah
that is executable, and then you execute it like a compiled
program
./foo
then the OS will notice the '#!' and run the following command
instead:
/usr/bin/prog -gobble ./foo
The '-gobble' tells 'prog' to eat the first line, leaving just the
blah that follows. (The reason for choosing '#!' is that '#' is
the comment character in the standard Unix shell, with the obvious
happy consequences for shell scripts.)
[5] We can play the same '#!' game with our 'bbc.img' file. Open it
up and add the line
#!/usr/local/bin/run6502 -B -l 0
(or whatever, according to the location of the 'run6502' program),
make it executable
chmod +x bbc.img
and execute it:
./bbc.img
To save a whopping 32K of zeros at the beginning of the file,
create the image again with
run6502 \
-l C000 OS12.ROM \
-l 8000 BASIC2.ROM \
-s 8000 +8000 bbc.img \
-x
and run it with
run6502 -l 0 bbc.img -B
and, if you like, insert the single line
#!/usr/local/bin/run6502 -B -l 8000
at the start of the image file and make it executable:
./bbc.img
[6] There is no footnote 6.