# lib6502(3)

    LIB6502(3)               BSD Library Functions Manual               LIB6502(3)

    NAME
         lib6502 - 6502 microprocessor emulator

    SYNOPSIS
         #include <stdint.h>
         #include <lib6502.h>

         M6502 *
         M6502_new(M6502_Registers *registers, M6502_Memory memory,
                 M6502_Callbacks *callbacks);

         void
         M6502_reset(M6502 *mpu);

         void
         M6502_nmi(M6502 *mpu);

         void
         M6502_irq(M6502 *mpu);

         uint16_t
         M6502_getVector(M6502 *mpu, vector);

         uint16_t
         M6502_setVector(M6502 *mpu, vector, uint16_t address);

         M6502_Callback
         M6502_getCallback(M6502 *mpu, type, uint16_t address);

         M6502_Callback
         M6502_setCallback(M6502 *mpu, type, uint16_t address,
                 M6502_Callback callback);

         void
         M6502_run(M6502 *mpu);

         int
         M6502_disassemble(M6502 *mpu, uint16_t addres_s, char buffer[64]);

         void
         M6502_dump(M6502 *mpu, char buffer[64]);

         void
         M6502_delete(M6502 *mpu);

    DESCRIPTION
         M6502_new() creates an instance of a 6502 microprocessor.  M6502_reset(),
         M6502_nmi() and M6502_irq() place it into the states associated with the
         hardware signals for reset, non-maskable interrupt and interrupt request,
         respectively.  The macros M6502_getVector() and M6502_setVector() read
         and write the vectors through which the processor jumps in response to
         the above signals.  The macros M6502_getCallback() and M6502_setVecttor()
         read and write client-supplied functions that intercept accesses to mem-
         ory.  M6502_run() begins emulated execution.  M6502_dump() and
         M6502_disassemble() create human-readable representations of processor or
         memory state.  M6502_delete() frees all resources associated with a pro-
         cessor instance.  Each of these functions and macros is described in more
         detail below.

         M6502_new() returns a pointer to a M6502 structure containing at least
         the following members:

         struct _M6502
         {
             M6502_Registers  *registers;   /* processor state */
             uint8_t          *memory;      /* memory image */
             M6502_Callbacks  *callbacks;   /* r/w/x callbacks */
         };

         These members are initialised according to the supplied registers, memory
         and callbacks arguments.  If a given argument is NULL, the corresponding
         member is initialised automatically with a suitable (non-NULL) value.

         The members of M6502 are as follows:

         registers  the processor state, containing all registers and condition
                    codes.

         memory     a block of at least 64 kilobytes of storage containing the
                    processor's memory.  (An array type M6502_Memory, suitable for
                    defining values to pass as the memory argument, is defined in
                    the #include <lib6502.h>
                    include file.)

         callbacks  a structure mapping processor memory accesses to client call-
                    back functions.

         Access to the contents of the registers and memory members can be made
         directly.  The registers member is a M6502_Registers containing the fol-
         lowing members:

         struct _M6502_Registers
         {
             uint8_t   a;  /* accumulator */
             uint8_t   x;  /* X index register */
             uint8_t   y;  /* Y index register */
             uint8_t   p;  /* processor status register */
             uint8_t   s;  /* stack pointer */
             uint16_t pc;  /* program counter */
         };

         The memory member is an array of unsigned char and can be indexed
         directly.  In addition, two convenience macros M6502_getVector() and
         M6502_setVector() provide access to the reset and interrupt vectors
         within memory.  M6502_getVector() returns the address stored in the named
         vector which must be precisely one of the following:

               RST  the reset vector.

               NMI  the non-maskable interrupt vector.

               IRQ  the interrupt request vector.

         M6502_setVector() stores its address argument in the named vector and
         returns the new value.

         The callbacks member contains an opaque structure mapping processor mem-
         ory accesses to client callback functions.  Whenever the processor per-
         forms an access for which a corresponding entry exists in the the
         callbacks structure, the emulator suspends execution and invokes the
         callback to complete the operation.  Each callback function should have a
         signature equivalent to:

               int callback (M6502 *mpu, uint16_t address, uint8_t data);

         The macros M6502_getCallback() and M6502_setCallback() read and write
         entries in the callbacks structure.  These macros identify a unique mem-
         ory access operation from the specified address on which it operates and
         type of access involved.  The type argument must be one of the following:

         read   the callback is invoked when the processor attempts to read from
                the given address.  The emulator passes the effective address of
                the operation to the callback in its address argument.  (The data
                argument is undefined.)  The value returned by the callback will
                be used by the emulator as the result of the read operation.

         write  the callback is invoked when the processor attempts to write to
                the given address.  The emulator passes the effective address of
                the operation to the callback in its address argument and the byte
                being written in the data argument.  The emulator will not perform
                the write operation before invoking the callback; if the write
                should complete, the callback must modify the processor's memory
                explicitly.  The valued returned from the callback is ignored.

         call   the callback is invoked when the processor attempts to transfer
                control to the given address by any instruction other than a rela-
                tive branch.  The emulator passes the destination address to the
                callback in its address argument and the instruction that initi-
                ated the control transfer in its data argument (one of JMP, JSR,
                BRK, RTS or RTI).  If the callback returns zero (the callback
                refuses to handle the operation) the emulator will allow the oper-
                ation to complete as normal.  If the callback returns a non-zero
                address (indicating that the callback has handled the operation
                internally) the emulator will transfer control to that address.

         M6502_getCallback() returns zero if there is no callback associated with
         the given type and address.  Passing zero as the callback argument of
         M6502_setCallback() removes any callback that might have been associated
         with type and address.

         M6502_run() emulates processor execution in the given mpu by repeatedly
         fetching the instruction addressed by pc and dispatching to it.  This
         function normally never returns.

         M6502_dump() writes a (NUL-terminated) symbolic representation of the
         processor's internal state into the supplied buffer.  Typical output
         resembles:

               PC=1010 SP=01FE A=0A X=5B Y=00 P=D1 NV-B---C

         M6502_disassemble() writes a (NUL-terminated) symbolic representation of
         the instruction in the processor's memory at the given address into the
         supplied buffer.  It returns the size (in bytes) of the instruction.  (In
         other words, the amount by which address should be incremented to arrive
         at the next instruction.)  Typical output resembles:

               1009 cpx #5B

         (The buffer arguments are oversized to allow for future expansion.)

         M6502_delete() frees the resources associated with the given mpu. Any
         members that were allocated implicitly (passed as NULL to M6502_new())
         are deallocated.  Members that were initialised from non-NULL arguments
         are not deallocated.

    IMPLEMENTATION NOTES
         You can share the memory and callbacks members of M6502 between multiple
         instances to simulate multiprocessor hardware.

    RETURN VALUES
         M6502_new() returns a pointer to a M6502 structure.  M6502_getVector()
         and M6502_setVector() return the contents of the given vector.
         M6502_getCallback() and M6502_setCallback() return the M6502_Callback
         function associated with the given address and access type.
         M6502_disassemble() returns the size (in bytes) of the instruction at the
         given address.  M6502_reset(), M6502_nmi(), M6502_irq(), M6502_run(),
         M6502_dump() and M6502_delete() don't return anything (unless you forgot
         to include

    EXAMPLES
         The following program creates a 6502 processor, sets up callbacks for
         printing characters and halting after a BRK instruction, stores a program
         into memory that prints the alphabet, disassembles the program on stdout,
         and then executes the program.

               #include <stdint.h>
               #include <stdlib.h>
               #include <stdio.h>

               #include "lib6502.h"

               #define WRCH    0xFFEE

               int wrch(M6502 *mpu, uint16_t address, uint8_t data)
               {
                 int pc;
                 putchar(mpu->registers->a);
                 pc  = mpu->memory[++mpu->registers->s + 0x100];
                 pc |= mpu->memory[++mpu->registers->s + 0x100] << 8;
                 return pc + 1;  /* JSR pushes next insn addr - 1 */
               }

               int done(M6502 *mpu, uint16_t address, uint8_t data)
               {
                 char buffer[64];
                 M6502_dump(mpu, buffer);
                 printf("\nBRK instruction\n%s\n", buffer);
                 exit(0);
               }

               int main(int argc, char **argv)
               {
                 M6502    *mpu = M6502_new(0, 0, 0);
                 unsigned  pc  = 0x1000;

                 mpu->callbacks->call[WRCH] = wrch;     /* write character */
                 mpu->callbacks->call[0000] = done;     /* reached after BRK */

               # define gen1(X)        (mpu->memory[pc++] = (uint8_t)(X))
               # define gen2(X,Y)      gen1(X); gen1(Y)
               # define gen3(X,Y,Z)    gen1(X); gen2(Y,Z)

                 gen2(0xA2, 'A'     );  /* LDX #'A'   */
                 gen1(0x8A          );  /* TXA        */
                 gen3(0x20,0xEE,0xFF);  /* JSR FFEE   */
                 gen1(0xE8          );  /* INX        */
                 gen2(0xE0, 'Z'+1   );  /* CPX #'Z'+1 */
                 gen2(0xD0, -9      );  /* BNE 1002   */
                 gen2(0xA9, '\n'    );  /* LDA #'\n'  */
                 gen3(0x20,0xEE,0xFF);  /* JSR FFEE   */
                 gen2(0x00,0x00     );  /* BRK        */

                 {
                   uint16_t ip = 0x1000;
                   while (ip < pc)
                     {
                       char insn[64];
                       ip += M6502_disassemble(mpu, ip, insn);
                       printf("%04X %s\n", ip, insn);
                     }
                 }

                 M6502_setVector(mpu, RST, 0x1000);

                 M6502_reset(mpu);
                 M6502_run(mpu);
                 M6502_delete(mpu);

                 return 0;
               }

    DIAGNOSTICS
         If M6502_new() cannot allocate sufficient memory it prints "out of mem-
         ory" to stderr and exits with a non-zero status.

         If M6502_run() encounters an illegal or undefined instruction, it prints
         "undefined instruction" and the processor's state to stderr, then exits
         with a non-zero status.

    COMPATIBILITY
         M6502 is a generic name. The initial letter is mandated by C naming con-
         ventions and chosen in deference to MOS Technology, the original design-
         ers of the processor.  To the best of my knowledge the 'M' prefix was
         never stamped on a physical 6502.

         The emulator implements the CMOS version of the processor (NMOS bugs in
         effective address calculations involving page boundaries are corrected)
         but does not tolerate the execution of undefined instructions (which were
         all no-ops in the first-generation CMOS hardware).  It would be nice to
         support the several alternative instruction sets (model-specific undocu-
         mented instructions in NMOS models, and various documented extensions in
         the later CMOS models) but there are currently no plans to do so.

         The emulated 6502 will run much faster than real hardware on any modern
         computer.  The fastest 6502 hardware available at the time of writing has
         a clock speed of 14 MHz.  On a 2 GHz PowerPC, the emulated 6502 runs at
         almost 300 MHz.

    SEE ALSO
         run6502(1)

         For development tools, documentation and source code: http://6502.org

    AUTHORS
         The software and manual pages were written by Ian Piumarta.

         The software is provided as-is, with absolutely no warranty, in the hope
         that you will enjoy and benefit from it.  You may use (entirely at your
         own risk) and redistribute it under the terms of a very liberal license
         that does not seek to restrict your rights in any way (unlike certain so-
         called 'open source' licenses that significantly limit your freedom in
         the name of 'free' software that is, ultimately, anything but free).  See
         the file COPYING for details.

    BUGS
         M6502_getVector() and M6502_setVector() evaluate their arguments more
         than once.

         The out-of-memory condition and attempted execution of illegal/undefined
         instructions should not be fatal errors.

         There is no way to limit the duration of execution within M6502_run() to
         a certain number of instructions or cycles.

         The emulator should support some means of implicit interrupt generation,
         either by polling or in response to (Unix) signals.

         The COMPATIBILITY section in this manual page has been diverted from its
         legitimate purpose.

         The plural of 'callback' really aught to be 'callsback'.

         Please send bug reports (and feature requests) to the author at: first-
         Name (at) lastName (dot) com.  (See AUTHORS above for suitable values of
         firstName and lastName.)

    BSD                            October 31, 2005                            BSD